changeset 11076:445f05f9f7b4

PSARC/2008/693 VRRP PSARC/2009/388 VRRP Update 6288572 RFE: VRRP implementation desired for Solaris
author Cathy Zhou <Cathy.Zhou@Sun.COM>
date Tue, 17 Nov 2009 09:17:48 -0800
parents e1d25eb89954
children 4bd474a3ea0e
files usr/src/Makefile.lint usr/src/cmd/Makefile usr/src/cmd/Makefile.check usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c usr/src/cmd/cmd-inet/usr.lib/Makefile usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c usr/src/cmd/cmd-inet/usr.lib/vrrpd/Makefile usr/src/cmd/cmd-inet/usr.lib/vrrpd/svc-vrrp usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrp.conf usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrp.xml usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd.c usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd.xcl usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd_impl.h usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c usr/src/cmd/cmd-inet/usr.sbin/in.routed/table.c usr/src/cmd/cmd-inet/usr.sbin/route.c usr/src/cmd/dladm/dladm.c usr/src/cmd/nscd/nscd_frontend.c usr/src/cmd/ptools/pfiles/pfiles.c usr/src/cmd/svc/shell/net_include.sh usr/src/cmd/truss/print.c usr/src/cmd/vrrpadm/Makefile usr/src/cmd/vrrpadm/vrrpadm.c usr/src/cmd/vrrpadm/vrrpadm.xcl usr/src/lib/Makefile usr/src/lib/libdladm/common/libdladm_impl.h usr/src/lib/libdladm/common/libdlvlan.c usr/src/lib/libdladm/common/libdlvnic.c usr/src/lib/libdladm/common/libdlvnic.h usr/src/lib/libsecdb/auth_attr.txt usr/src/lib/libsecdb/help/auths/Makefile usr/src/lib/libsecdb/help/auths/NetworkVRRP.html usr/src/lib/libsecdb/help/profiles/Makefile usr/src/lib/libsecdb/help/profiles/RtNetVRRP.html usr/src/lib/libsecdb/prof_attr.txt usr/src/lib/libvrrpadm/Makefile usr/src/lib/libvrrpadm/Makefile.com usr/src/lib/libvrrpadm/amd64/Makefile usr/src/lib/libvrrpadm/common/libvrrpadm.c usr/src/lib/libvrrpadm/common/libvrrpadm.h usr/src/lib/libvrrpadm/common/llib-lvrrpadm usr/src/lib/libvrrpadm/common/mapfile-vers usr/src/lib/libvrrpadm/common/netinet/vrrp.h usr/src/lib/libvrrpadm/i386/Makefile usr/src/lib/libvrrpadm/libvrrpadm.xcl usr/src/lib/libvrrpadm/sparc/Makefile usr/src/lib/libvrrpadm/sparcv9/Makefile usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNW0on/prototype_com usr/src/pkgdefs/SUNWcsu/prototype_com usr/src/pkgdefs/SUNWhea/prototype_com usr/src/pkgdefs/SUNWvrrpr/Makefile usr/src/pkgdefs/SUNWvrrpr/depend usr/src/pkgdefs/SUNWvrrpr/pkginfo.tmpl usr/src/pkgdefs/SUNWvrrpr/prototype_com usr/src/pkgdefs/SUNWvrrpr/prototype_i386 usr/src/pkgdefs/SUNWvrrpr/prototype_sparc usr/src/pkgdefs/SUNWvrrpu/Makefile usr/src/pkgdefs/SUNWvrrpu/depend usr/src/pkgdefs/SUNWvrrpu/pkginfo.tmpl usr/src/pkgdefs/SUNWvrrpu/prototype_com usr/src/pkgdefs/SUNWvrrpu/prototype_i386 usr/src/pkgdefs/SUNWvrrpu/prototype_sparc usr/src/pkgdefs/etc/exception_list_i386 usr/src/pkgdefs/etc/exception_list_sparc usr/src/uts/common/inet/ip/conn_opt.c usr/src/uts/common/inet/ip/ip6_if.c usr/src/uts/common/inet/ip/ip6_ire.c usr/src/uts/common/inet/ip/ip_if.c usr/src/uts/common/inet/ip/ip_ire.c usr/src/uts/common/inet/ip/ip_rts.c usr/src/uts/common/inet/ip_if.h usr/src/uts/common/inet/ipclassifier.h usr/src/uts/common/inet/udp/udp_opt_data.c usr/src/uts/common/io/dld/dld_proto.c usr/src/uts/common/io/vnic/vnic_ctl.c usr/src/uts/common/io/vnic/vnic_dev.c usr/src/uts/common/net/if.h usr/src/uts/common/net/route.h usr/src/uts/common/sys/Makefile usr/src/uts/common/sys/dlpi.h usr/src/uts/common/sys/mac_provider.h usr/src/uts/common/sys/socket.h usr/src/uts/common/sys/sysevent/eventdefs.h usr/src/uts/common/sys/sysevent/vrrp.h usr/src/uts/common/sys/types.h usr/src/uts/common/sys/vnic.h usr/src/uts/common/sys/vnic_impl.h
diffstat 88 files changed, 8865 insertions(+), 209 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Makefile.lint	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/Makefile.lint	Tue Nov 17 09:17:48 2009 -0800
@@ -79,6 +79,7 @@
 	cmd/cmd-inet/usr.lib/inetd \
 	cmd/cmd-inet/usr.lib/pppoe \
 	cmd/cmd-inet/usr.lib/slpd \
+	cmd/cmd-inet/usr.lib/vrrpd \
 	cmd/cmd-inet/usr.lib/wpad \
 	cmd/cmd-inet/usr.lib/wanboot \
 	cmd/cmd-inet/usr.sadm \
@@ -298,6 +299,7 @@
 	cmd/utmp_update \
 	cmd/utmpd \
 	cmd/valtools \
+	cmd/vrrpadm \
 	cmd/vt \
 	cmd/wall \
 	cmd/wbem \
@@ -427,6 +429,7 @@
 	lib/libunistat \
 	lib/libuuid \
 	lib/libuutil \
+	lib/libvrrpadm \
 	lib/libwanboot \
 	lib/libwanbootutil \
 	lib/libxnet \
--- a/usr/src/cmd/Makefile	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -428,6 +428,7 @@
 	vi		\
 	volcheck	\
 	volrmmount	\
+	vrrpadm		\
 	vscan		\
 	vt		\
 	w		\
@@ -718,6 +719,7 @@
 	vi		\
 	volcheck	\
 	volrmmount	\
+	vrrpadm		\
 	w		\
 	wbem		\
 	who		\
--- a/usr/src/cmd/Makefile.check	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/Makefile.check	Tue Nov 17 09:17:48 2009 -0800
@@ -94,6 +94,7 @@
 	cmd-inet/usr.lib/inetd		\
 	cmd-inet/usr.lib/mdnsd		\
 	cmd-inet/usr.lib/slpd		\
+	cmd-inet/usr.lib/vrrpd		\
 	cmd-inet/usr.lib/wpad		\
 	cmd-inet/usr.sbin		\
 	cmd-inet/usr.sbin/in.ftpd	\
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c	Tue Nov 17 09:17:48 2009 -0800
@@ -111,6 +111,23 @@
 	}
 	pif->pif_index = lifr.lifr_index;
 
+	/*
+	 * Check if this is a VRRP interface. If yes, its IP addresses (the
+	 * VRRP virtual addresses) cannot be configured using DHCP.
+	 */
+	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
+		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
+		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFFLAGS for %s", pname);
+		goto failure;
+	}
+
+	if (lifr.lifr_flags & IFF_VRRP) {
+		*error = DHCP_IPC_E_INVIF;
+		dhcpmsg(MSG_ERROR, "insert_pif: VRRP virtual addresses over %s "
+		    "cannot be configured using DHCP", pname);
+		goto failure;
+	}
+
 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) {
 		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname);
--- a/usr/src/cmd/cmd-inet/usr.lib/Makefile	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -26,9 +26,9 @@
 SUBDIRS=	bridged dhcp dsvclockd ilbd in.chargend in.daytimed \
 		in.discardd in.echod in.dhcpd in.mpathd in.ndpd \
 		in.ripngd in.timed inetd mdnsd ncaconfd pppoe \
-		slpd wanboot wpad
+		slpd vrrpd wanboot wpad
 
-MSGSUBDIRS=	dsvclockd ilbd in.dhcpd inetd ncaconfd wanboot
+MSGSUBDIRS=	dsvclockd ilbd in.dhcpd inetd ncaconfd vrrpd wanboot
 
 include ../../Makefile.cmd
 
@@ -37,7 +37,7 @@
 	$(CLOSED)/cmd/cmd-inet/usr.lib/in.iked
 
 POFILES=	dsvclockd/dsvclockd.po in.dhcpd/in.dhcpd.po \
-		inetd/inetd.po ncaconfd/ncaconfd.po \
+		inetd/inetd.po ncaconfd/ncaconfd.po vrrpd/vrrpd.po \
 		wanboot/wanboot.po
 POFILE=		usr.lib.po
 
--- a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c	Tue Nov 17 09:17:48 2009 -0800
@@ -429,11 +429,12 @@
 	}
 
 	/*
-	 * Ignore loopback and point-to-multipoint interfaces.
+	 * Ignore loopback, point-to-multipoint and VRRP interfaces.
+	 * The IP addresses over VRRP interfaces cannot be auto-configured.
 	 * Point-to-point interfaces always have IFF_MULTICAST set.
 	 */
 	if (!(lifr.lifr_flags & IFF_MULTICAST) ||
-	    (lifr.lifr_flags & IFF_LOOPBACK)) {
+	    (lifr.lifr_flags & (IFF_LOOPBACK|IFF_VRRP))) {
 		return;
 	}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/vrrpd/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,82 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		vrrpd
+OBJS=		vrrpd.o
+SRCS=		$(OBJS:.o=.c)
+SVCMETHOD=	svc-vrrp
+MANIFEST=	vrrp.xml
+CFGFILES=	vrrp.conf
+
+include ../../../Makefile.cmd
+
+ROOTMANIFESTDIR=	$(ROOTSVCNETWORK)
+
+$(ROOTETC)/inet/$(CFGFILES):= FILEMODE= 644 
+$(ROOTETC)/inet/$(CFGFILES):= OWNER= root 
+$(ROOTETC)/inet/$(CFGFILES):= GROUP= sys 
+
+C99MODE=		$(C99_ENABLE)
+
+#
+# We need access to the ancillary data features which are only available
+# via the SUS standards.  Further, C99 support requires SUSv3 or higher.
+#
+CPPFLAGS += -D_XOPEN_SOURCE=600 -D__EXTENSIONS__
+LDLIBS += -lvrrpadm -lsocket -lnsl -ldladm -linetutil -lsysevent -lnvpair -lsecdb -linetcfg
+LINTFLAGS += -erroff=E_INCONS_ARG_DECL2  -erroff=E_INCONS_ARG_USED2
+
+#
+# Instrument vrrpd with CTF data to ease debugging.
+#
+CTFCONVERT_HOOK = && $(CTFCONVERT_O)
+CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS)
+$(OBJS) := CFLAGS += $(CTF_FLAGS)
+
+# for messaging catalog. No messages are present in CMN_DIR sources.
+POFILES = $(LOCAL_OBJS:%.o=%.po)
+XGETFLAGS += -a -x vrrpd.xcl
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK)
+	$(POST_PROCESS)
+
+include ../Makefile.lib
+
+install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) \
+	$(ROOTETC)/inet/$(CFGFILES)
+
+check:	$(CHKMANIFEST)
+
+clean:
+	$(RM) $(OBJS)
+
+lint:	lint_SRCS
+
+include ../../../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/vrrpd/svc-vrrp	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,40 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Service Method Support Script for the VRRP service
+#
+
+. /lib/svc/share/smf_include.sh
+
+# Start the vrrpd daemon
+/usr/lib/inet/vrrpd
+if [ $? = 0 ]; then
+        exit $SMF_EXIT_OK
+else
+        exit $SMF_EXIT_ERR_FATAL
+fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrp.conf	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# DO NOT EDIT OR PARSE THIS FILE!
+#
+# Use the vrrpadm(1m) command to change the contents of this file.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrp.xml	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ CDDL HEADER START
+ 
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+	Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+	Use is subject to license terms.
+
+	NOTE:  This service manifest is not editable; its contents will
+	be overwritten by package or patch operations, including
+	operating system upgrade.  Make customizations in a different
+	file.
+-->
+
+<service_bundle type='manifest' name='SUNWvrrpr:vrrp'>
+
+<service
+	name='network/vrrp'
+	type='service'
+	version='1'>
+
+	<create_default_instance enabled='true' />
+
+	<single_instance/>
+
+        <dependency
+		name='physical'
+		grouping='require_all'
+		restart_on='none'
+		type='service'>
+		<service_fmri value='svc:/network/physical' />
+	</dependency>
+
+        <dependency
+		name='filesystem'
+		grouping='require_all'
+		restart_on='none'
+		type='service'>
+		<service_fmri value='svc:/system/filesystem/minimal' />
+	</dependency>
+
+	<exec_method
+	    type='method'
+	    name='start'
+	    exec='/lib/svc/method/svc-vrrp start'
+	    timeout_seconds='60' >
+		<method_context>
+			<!--
+			    We need net_icmpaccess and net_rawaccess
+			    privileges to open the raw socket, the
+			    sys_ip_config privilege to bring up/down
+			    the virtual IP addresses, and the sys_resource
+			    privilege to setrlimit().
+			-->
+			<method_credential
+			user='root'
+			group='root'
+			limit_privileges=':default'
+			privileges='zone,net_icmpaccess,net_rawaccess,sys_ip_config,sys_resource'
+			/>
+		</method_context>
+	</exec_method>
+
+	<exec_method
+	    type='method'
+	    name='stop'
+	    exec=':kill'
+	    timeout_seconds='60' />
+
+	<property_group name='startd' type='framework'>
+		<!-- sub-process core dumps shouldn't restart session -->
+		<propval name='ignore_error' type='astring'
+		     value='core,signal' />
+	</property_group>
+
+	<stability value='Unstable' />
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'> VRRP service daemon
+			</loctext>
+		</common_name>
+		<documentation> 
+			<manpage title='vrrpd' section='1M' 
+				manpath='/usr/share/man' /> 
+		</documentation> 
+	</template>
+</service>
+
+</service_bundle>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd.c	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,4472 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysevent/vrrp.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/varargs.h>
+#include <auth_attr.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <unistd.h>
+#include <zone.h>
+#include <libsysevent.h>
+#include <limits.h>
+#include <locale.h>
+#include <inetcfg.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <assert.h>
+#include <ucred.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <priv_utils.h>
+#include <libdllink.h>
+#include <libdlvnic.h>
+#include <pwd.h>
+#include <libvrrpadm.h>
+#include <net/route.h>
+#include "vrrpd_impl.h"
+
+/*
+ * A VRRP router can be only start participating the VRRP protocol of a virtual
+ * router when all the following conditions are met:
+ *
+ * - The VRRP router is enabled (vr->vvr_conf.vvc_enabled is _B_TRUE)
+ * - The RX socket is successfully created over the physical interface to
+ *   receive the VRRP multicast advertisement. Note that one RX socket can
+ *   be shared by several VRRP routers configured over the same physical
+ *   interface. (See vrrpd_init_rxsock())
+ * - The TX socket is successfully created over the VNIC interface to send
+ *   the VRRP advertisment. (See vrrpd_init_txsock())
+ * - The primary IP address has been successfully selected over the physical
+ *   interface. (See vrrpd_select_primary())
+ *
+ * If a VRRP router is enabled but the other conditions haven't be satisfied,
+ * the router will be stay at the VRRP_STATE_INIT state. If all the above
+ * conditions are met, the VRRP router will be transit to either
+ * the VRRP_STATE_MASTER or the VRRP_STATE_BACKUP state, depends on the VRRP
+ * protocol.
+ */
+
+#define	skip_whitespace(p)	while (isspace(*(p))) ++(p)
+
+#define	BUFFSIZE	65536
+
+#define	VRRPCONF	"/etc/inet/vrrp.conf"
+
+typedef struct vrrpd_rtsock_s {
+	int		vrt_af;		/* address family */
+	int		vrt_fd;		/* socket for the PF_ROUTE msg */
+	iu_event_id_t	vrt_eid;	/* event ID */
+} vrrpd_rtsock_t;
+
+static int		vrrp_logflag = 0;
+boolean_t		vrrp_debug_level = 0;
+iu_eh_t			*vrrpd_eh = NULL;
+iu_tq_t			*vrrpd_timerq = NULL;
+static vrrp_handle_t	vrrpd_vh = NULL;
+static int		vrrpd_cmdsock_fd = -1;	/* socket to communicate */
+						/* between vrrpd/libvrrpadm */
+static iu_event_id_t	vrrpd_cmdsock_eid = -1;
+static int		vrrpd_ctlsock_fd = -1;	/* socket to bring up/down */
+						/* the virtual IP addresses */
+static int		vrrpd_ctlsock6_fd = -1;
+static vrrpd_rtsock_t	vrrpd_rtsocks[2] = {
+	{AF_INET, -1, -1},
+	{AF_INET6, -1, -1}
+};
+static iu_timer_id_t	vrrp_scan_timer_id = -1;
+
+TAILQ_HEAD(vrrp_vr_list_s, vrrp_vr_s);
+TAILQ_HEAD(vrrp_intf_list_s, vrrp_intf_s);
+static struct vrrp_vr_list_s	vrrp_vr_list;
+static struct vrrp_intf_list_s	vrrp_intf_list;
+static char		vrrpd_conffile[MAXPATHLEN];
+
+/*
+ * Multicast address of VRRP advertisement in network byte order
+ */
+static vrrp_addr_t	vrrp_muladdr4;
+static vrrp_addr_t	vrrp_muladdr6;
+
+static int		vrrpd_scan_interval = 20000;	/* ms */
+
+/*
+ * macros to calculate skew_time and master_down_timer
+ *
+ * Note that the input is in centisecs and output are in msecs
+ */
+#define	SKEW_TIME(pri, intv)	((intv) * (256 - (pri)) / 256)
+#define	MASTER_DOWN_INTERVAL(pri, intv)	(3 * (intv) + SKEW_TIME((pri), (intv)))
+
+#define	SKEW_TIME_VR(vr)	\
+	SKEW_TIME((vr)->vvr_conf.vvc_pri, (vr)->vvr_master_adver_int)
+#define	MASTER_DOWN_INTERVAL_VR(vr)	\
+	MASTER_DOWN_INTERVAL((vr)->vvr_conf.vvc_pri, (vr)->vvr_master_adver_int)
+
+#define	VRRP_CONF_UPDATE	0x01
+#define	VRRP_CONF_DELETE	0x02
+
+static char *af_str(int);
+
+static iu_tq_callback_t vrrp_adv_timeout;
+static iu_tq_callback_t vrrp_b2m_timeout;
+static iu_eh_callback_t vrrpd_sock_handler;
+static iu_eh_callback_t vrrpd_rtsock_handler;
+static iu_eh_callback_t vrrpd_cmdsock_handler;
+
+static int daemon_init();
+
+static vrrp_err_t vrrpd_init();
+static void vrrpd_fini();
+static vrrp_err_t vrrpd_cmdsock_create();
+static void vrrpd_cmdsock_destroy();
+static vrrp_err_t vrrpd_rtsock_create();
+static void vrrpd_rtsock_destroy();
+static vrrp_err_t vrrpd_ctlsock_create();
+static void vrrpd_ctlsock_destroy();
+
+static void vrrpd_scan_timer(iu_tq_t *, void *);
+static void vrrpd_scan(int);
+static vrrp_err_t vrrpd_init_rxsock(vrrp_vr_t *);
+static void vrrpd_fini_rxsock(vrrp_vr_t *);
+static vrrp_err_t vrrpd_init_txsock(vrrp_vr_t *);
+static vrrp_err_t vrrpd_init_txsock_v4(vrrp_vr_t *);
+static vrrp_err_t vrrpd_init_txsock_v6(vrrp_vr_t *);
+static void vrrpd_fini_txsock(vrrp_vr_t *);
+
+static vrrp_err_t vrrpd_create_vr(vrrp_vr_conf_t *);
+static vrrp_err_t vrrpd_enable_vr(vrrp_vr_t *);
+static void vrrpd_disable_vr(vrrp_vr_t *, vrrp_intf_t *, boolean_t);
+static void vrrpd_delete_vr(vrrp_vr_t *);
+
+static vrrp_err_t vrrpd_create(vrrp_vr_conf_t *, boolean_t);
+static vrrp_err_t vrrpd_delete(const char *);
+static vrrp_err_t vrrpd_enable(const char *, boolean_t);
+static vrrp_err_t vrrpd_disable(const char *);
+static vrrp_err_t vrrpd_modify(vrrp_vr_conf_t *, uint32_t);
+static void vrrpd_list(vrid_t, char *, int, vrrp_ret_list_t *, size_t *);
+static void vrrpd_query(const char *, vrrp_ret_query_t *, size_t *);
+
+static boolean_t vrrp_rd_prop_name(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_vrid(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_af(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_pri(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_adver_int(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_preempt(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_accept(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_ifname(vrrp_vr_conf_t *, const char *);
+static boolean_t vrrp_rd_prop_enabled(vrrp_vr_conf_t *, const char *);
+static int vrrp_wt_prop_name(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_vrid(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_af(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_pri(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_adver_int(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_preempt(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_accept(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_ifname(vrrp_vr_conf_t *, char *, size_t);
+static int vrrp_wt_prop_enabled(vrrp_vr_conf_t *, char *, size_t);
+
+static void vrrpd_cmd_create(void *, void *, size_t *);
+static void vrrpd_cmd_delete(void *, void *, size_t *);
+static void vrrpd_cmd_enable(void *, void *, size_t *);
+static void vrrpd_cmd_disable(void *, void *, size_t *);
+static void vrrpd_cmd_modify(void *, void *, size_t *);
+static void vrrpd_cmd_list(void *, void *, size_t *);
+static void vrrpd_cmd_query(void *, void *, size_t *);
+
+static vrrp_vr_t *vrrpd_lookup_vr_by_vrid(char *, vrid_t vrid_t, int);
+static vrrp_vr_t *vrrpd_lookup_vr_by_name(const char *);
+static vrrp_intf_t *vrrpd_lookup_if(const char *, int);
+static vrrp_err_t vrrpd_create_if(const char *, int, uint32_t, vrrp_intf_t **);
+static void vrrpd_delete_if(vrrp_intf_t *, boolean_t);
+static vrrp_err_t vrrpd_create_ip(vrrp_intf_t *, const char *, vrrp_addr_t *,
+    uint64_t flags);
+static void vrrpd_delete_ip(vrrp_intf_t *, vrrp_ip_t *);
+
+static void vrrpd_init_ipcache(int);
+static void vrrpd_update_ipcache(int);
+static int vrrpd_walk_ipaddr(icfg_if_t *, void *);
+static vrrp_err_t vrrpd_add_ipaddr(char *, int, vrrp_addr_t *,
+    int, uint64_t);
+static vrrp_ip_t *vrrpd_select_primary(vrrp_intf_t *);
+static void vrrpd_reselect_primary(vrrp_intf_t *);
+static void vrrpd_reenable_all_vr();
+static void vrrpd_remove_if(vrrp_intf_t *, boolean_t);
+
+static uint16_t in_cksum(int, uint16_t, void *);
+static uint16_t vrrp_cksum4(struct in_addr *, struct in_addr *,
+    uint16_t, vrrp_pkt_t *);
+static uint16_t vrrp_cksum6(struct in6_addr *, struct in6_addr *,
+    uint16_t, vrrp_pkt_t *);
+static size_t vrrpd_build_vrrp(vrrp_vr_t *, uchar_t *, int, boolean_t);
+
+static void vrrpd_process_adv(vrrp_vr_t *, vrrp_addr_t *, vrrp_pkt_t *);
+static vrrp_err_t vrrpd_send_adv(vrrp_vr_t *, boolean_t);
+
+/* state transition functions */
+static vrrp_err_t vrrpd_state_i2m(vrrp_vr_t *);
+static vrrp_err_t vrrpd_state_i2b(vrrp_vr_t *);
+static void vrrpd_state_m2i(vrrp_vr_t *);
+static void vrrpd_state_b2i(vrrp_vr_t *);
+static vrrp_err_t vrrpd_state_b2m(vrrp_vr_t *);
+static vrrp_err_t vrrpd_state_m2b(vrrp_vr_t *);
+static void vrrpd_state_trans(vrrp_state_t, vrrp_state_t, vrrp_vr_t *);
+
+static vrrp_err_t vrrpd_set_noaccept(vrrp_vr_t *, boolean_t);
+static vrrp_err_t vrrpd_virtualip_update(vrrp_vr_t *, boolean_t);
+static vrrp_err_t vrrpd_virtualip_updateone(vrrp_intf_t *, vrrp_ip_t *,
+    boolean_t);
+static int vrrpd_post_event(const char *, vrrp_state_t, vrrp_state_t);
+
+static void vrrpd_initconf();
+static vrrp_err_t vrrpd_updateconf(vrrp_vr_conf_t *, uint_t);
+static vrrp_err_t vrrpd_write_vrconf(char *, size_t, vrrp_vr_conf_t *);
+static vrrp_err_t vrrpd_read_vrconf(char *, vrrp_vr_conf_t *);
+static vrrp_err_t vrrpd_readprop(const char *, vrrp_vr_conf_t *);
+static void vrrpd_cleanup();
+
+static void vrrp_log(int, char *, ...);
+static int timeval_to_milli(struct timeval);
+static struct timeval timeval_delta(struct timeval, struct timeval);
+
+typedef struct vrrpd_prop_s {
+	char		*vs_propname;
+	boolean_t	(*vs_propread)(vrrp_vr_conf_t *, const char *);
+	int		(*vs_propwrite)(vrrp_vr_conf_t *, char *, size_t);
+} vrrp_prop_t;
+
+/*
+ * persistent VRRP properties array
+ */
+static vrrp_prop_t vrrp_prop_info_tbl[] = {
+	{"name", vrrp_rd_prop_name, vrrp_wt_prop_name},
+	{"vrid", vrrp_rd_prop_vrid, vrrp_wt_prop_vrid},
+	{"priority", vrrp_rd_prop_pri, vrrp_wt_prop_pri},
+	{"adv_intval", vrrp_rd_prop_adver_int, vrrp_wt_prop_adver_int},
+	{"preempt_mode", vrrp_rd_prop_preempt, vrrp_wt_prop_preempt},
+	{"accept_mode", vrrp_rd_prop_accept, vrrp_wt_prop_accept},
+	{"interface", vrrp_rd_prop_ifname, vrrp_wt_prop_ifname},
+	{"af", vrrp_rd_prop_af, vrrp_wt_prop_af},
+	{"enabled", vrrp_rd_prop_enabled, vrrp_wt_prop_enabled}
+};
+
+#define	VRRP_PROP_INFO_TABSIZE	\
+	(sizeof (vrrp_prop_info_tbl) / sizeof (vrrp_prop_t))
+
+typedef void vrrp_cmd_func_t(void *, void *, size_t *);
+
+typedef struct vrrp_cmd_info_s {
+	vrrp_cmd_type_t	vi_cmd;
+	size_t		vi_reqsize;
+	size_t		vi_acksize;	/* 0 if the size is variable */
+	boolean_t	vi_setop;	/* Set operation? Check credentials */
+	vrrp_cmd_func_t	*vi_cmdfunc;
+} vrrp_cmd_info_t;
+
+static vrrp_cmd_info_t vrrp_cmd_info_tbl[] = {
+	{VRRP_CMD_CREATE, sizeof (vrrp_cmd_create_t),
+	    sizeof (vrrp_ret_create_t), _B_TRUE, vrrpd_cmd_create},
+	{VRRP_CMD_DELETE, sizeof (vrrp_cmd_delete_t),
+	    sizeof (vrrp_ret_delete_t), _B_TRUE, vrrpd_cmd_delete},
+	{VRRP_CMD_ENABLE, sizeof (vrrp_cmd_enable_t),
+	    sizeof (vrrp_ret_enable_t), _B_TRUE, vrrpd_cmd_enable},
+	{VRRP_CMD_DISABLE, sizeof (vrrp_cmd_disable_t),
+	    sizeof (vrrp_ret_disable_t), _B_TRUE, vrrpd_cmd_disable},
+	{VRRP_CMD_MODIFY, sizeof (vrrp_cmd_modify_t),
+	    sizeof (vrrp_ret_modify_t), _B_TRUE, vrrpd_cmd_modify},
+	{VRRP_CMD_QUERY, sizeof (vrrp_cmd_query_t), 0,
+	    _B_FALSE, vrrpd_cmd_query},
+	{VRRP_CMD_LIST, sizeof (vrrp_cmd_list_t), 0,
+	    _B_FALSE, vrrpd_cmd_list}
+};
+
+#define	VRRP_DOOR_INFO_TABLE_SIZE	\
+	(sizeof (vrrp_cmd_info_tbl) / sizeof (vrrp_cmd_info_t))
+
+static int
+ipaddr_cmp(int af, vrrp_addr_t *addr1, vrrp_addr_t *addr2)
+{
+	if (af == AF_INET) {
+		return (memcmp(&addr1->in4.sin_addr,
+		    &addr2->in4.sin_addr, sizeof (struct in_addr)));
+	} else {
+		return (memcmp(&addr1->in6.sin6_addr,
+		    &addr2->in6.sin6_addr, sizeof (struct in6_addr)));
+	}
+}
+
+static vrrp_vr_t *
+vrrpd_lookup_vr_by_vrid(char *ifname, vrid_t vrid, int af)
+{
+	vrrp_vr_t *vr;
+
+	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
+		if (strcmp(vr->vvr_conf.vvc_link, ifname) == 0 &&
+		    vr->vvr_conf.vvc_vrid == vrid &&
+		    vr->vvr_conf.vvc_af == af) {
+			break;
+		}
+	}
+	return (vr);
+}
+
+static vrrp_vr_t *
+vrrpd_lookup_vr_by_name(const char *name)
+{
+	vrrp_vr_t *vr;
+
+	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
+		if (strcmp(vr->vvr_conf.vvc_name, name) == 0)
+			break;
+	}
+	return (vr);
+}
+
+static vrrp_intf_t *
+vrrpd_lookup_if(const char *ifname, int af)
+{
+	vrrp_intf_t	*intf;
+
+	TAILQ_FOREACH(intf, &vrrp_intf_list, vvi_next) {
+		if (strcmp(ifname, intf->vvi_ifname) == 0 &&
+		    af == intf->vvi_af) {
+			break;
+		}
+	}
+	return (intf);
+}
+
+static vrrp_err_t
+vrrpd_create_if(const char *ifname, int af, uint32_t ifindex,
+    vrrp_intf_t **intfp)
+{
+	vrrp_intf_t	*intf;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_create_if(%s, %s, %d)",
+	    ifname, af_str(af), ifindex);
+
+	if (((*intfp) = malloc(sizeof (vrrp_intf_t))) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_create_if(): failed to "
+		    "allocate %s/%s interface", ifname, af_str(af));
+		return (VRRP_ENOMEM);
+	}
+
+	intf = *intfp;
+	TAILQ_INIT(&intf->vvi_iplist);
+	(void) strlcpy(intf->vvi_ifname, ifname, sizeof (intf->vvi_ifname));
+	intf->vvi_af = af;
+	intf->vvi_sockfd = -1;
+	intf->vvi_nvr = 0;
+	intf->vvi_eid = -1;
+	intf->vvi_pip = NULL;
+	intf->vvi_ifindex = ifindex;
+	intf->vvi_state = NODE_STATE_NEW;
+	intf->vvi_vr_state = VRRP_STATE_INIT;
+	TAILQ_INSERT_TAIL(&vrrp_intf_list, intf, vvi_next);
+	return (VRRP_SUCCESS);
+}
+
+/*
+ * An interface is deleted. If update_vr is true, the deletion of the interface
+ * may cause the state transition of assoicated VRRP router (if this interface
+ * is either the primary or the VNIC interface of the VRRP router); otherwise,
+ * simply delete the interface without updating the VRRP router.
+ */
+static void
+vrrpd_delete_if(vrrp_intf_t *intf, boolean_t update_vr)
+{
+	vrrp_ip_t	*ip;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_delete_if(%s, %s, %supdate_vr)",
+	    intf->vvi_ifname, af_str(intf->vvi_af), update_vr ? "" : "no_");
+
+	if (update_vr) {
+		/*
+		 * If a this interface is the physical interface or the VNIC
+		 * of a VRRP router, the deletion of the interface (no IP
+		 * address exists on this interface) may cause the state
+		 * transition of the VRRP router. call vrrpd_remove_if()
+		 * to find all corresponding VRRP router and update their
+		 * states.
+		 */
+		vrrpd_remove_if(intf, _B_FALSE);
+	}
+
+	/*
+	 * First remove and delete all the IP addresses on the interface
+	 */
+	while (!TAILQ_EMPTY(&intf->vvi_iplist)) {
+		ip = TAILQ_FIRST(&intf->vvi_iplist);
+		vrrpd_delete_ip(intf, ip);
+	}
+
+	/*
+	 * Then remove and delete the interface
+	 */
+	TAILQ_REMOVE(&vrrp_intf_list, intf, vvi_next);
+	(void) free(intf);
+}
+
+static vrrp_err_t
+vrrpd_create_ip(vrrp_intf_t *intf, const char *lifname, vrrp_addr_t *addr,
+    uint64_t flags)
+{
+	vrrp_ip_t	*ip;
+	char		abuf[INET6_ADDRSTRLEN];
+
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(intf->vvi_af, addr, abuf, INET6_ADDRSTRLEN, _B_FALSE);
+	vrrp_log(VRRP_DBG0, "vrrpd_create_ip(%s, %s, %s, 0x%x)",
+	    intf->vvi_ifname, lifname, abuf, flags);
+
+	if ((ip = malloc(sizeof (vrrp_ip_t))) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_create_ip(%s, %s):"
+		    "failed to allocate IP", lifname, abuf);
+		return (VRRP_ENOMEM);
+	}
+
+	(void) strncpy(ip->vip_lifname, lifname, sizeof (ip->vip_lifname));
+	ip->vip_state = NODE_STATE_NEW;
+	ip->vip_flags = flags;
+	(void) memcpy(&ip->vip_addr, addr, sizeof (ip->vip_addr));
+
+	/*
+	 * Make sure link-local IPv6 IP addresses are at the head of the list
+	 */
+	if (intf->vvi_af == AF_INET6 &&
+	    IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr)) {
+		TAILQ_INSERT_HEAD(&intf->vvi_iplist, ip, vip_next);
+	} else {
+		TAILQ_INSERT_TAIL(&intf->vvi_iplist, ip, vip_next);
+	}
+	return (VRRP_SUCCESS);
+}
+
+static void
+vrrpd_delete_ip(vrrp_intf_t *intf, vrrp_ip_t *ip)
+{
+	char	abuf[INET6_ADDRSTRLEN];
+	int	af = intf->vvi_af;
+
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(af, &ip->vip_addr, abuf, sizeof (abuf), _B_FALSE);
+	vrrp_log(VRRP_DBG0, "vrrpd_delete_ip(%s, %s, %s) is %sprimary",
+	    intf->vvi_ifname, ip->vip_lifname, abuf,
+	    intf->vvi_pip == ip ? "" : "not ");
+
+	if (intf->vvi_pip == ip)
+		intf->vvi_pip = NULL;
+
+	TAILQ_REMOVE(&intf->vvi_iplist, ip, vip_next);
+	(void) free(ip);
+}
+
+static char *
+rtm_event2str(uchar_t event)
+{
+	switch (event) {
+	case RTM_NEWADDR:
+		return ("RTM_NEWADDR");
+	case RTM_DELADDR:
+		return ("RTM_DELADDR");
+	case RTM_IFINFO:
+		return ("RTM_IFINFO");
+	case RTM_ADD:
+		return ("RTM_ADD");
+	case RTM_DELETE:
+		return ("RTM_DELETE");
+	case RTM_CHANGE:
+		return ("RTM_CHANGE");
+	case RTM_OLDADD:
+		return ("RTM_OLDADD");
+	case RTM_OLDDEL:
+		return ("RTM_OLDDEL");
+	case RTM_CHGADDR:
+		return ("RTM_CHGADDR");
+	case RTM_FREEADDR:
+		return ("RTM_FREEADDR");
+	default:
+		return ("RTM_OTHER");
+	}
+}
+
+int
+main(int argc, char *argv[])
+{
+	int c, err;
+	struct sigaction sa;
+	sigset_t mask;
+	struct rlimit rl;
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	/*
+	 * We need PRIV_SYS_CONFIG to post VRRP sysevent, PRIV_NET_RAWACESS
+	 * and PRIV_NET_ICMPACCESS to open  the raw socket, PRIV_SYS_IP_CONFIG
+	 * to bring up/down the virtual IP addresses, and PRIV_SYS_RESOURCE to
+	 * setrlimit().
+	 *
+	 * Note that sysevent is not supported in non-global zones.
+	 */
+	if (getzoneid() == GLOBAL_ZONEID) {
+		err = __init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
+		    PRIV_SYS_CONFIG, PRIV_NET_RAWACCESS, PRIV_NET_ICMPACCESS,
+		    PRIV_SYS_IP_CONFIG, PRIV_SYS_RESOURCE, NULL);
+	} else {
+		err = __init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
+		    PRIV_NET_RAWACCESS, PRIV_NET_ICMPACCESS,
+		    PRIV_SYS_IP_CONFIG, PRIV_SYS_RESOURCE, NULL);
+	}
+
+	if (err == -1) {
+		vrrp_log(VRRP_ERR, "main(): init_daemon_priv() failed");
+		return (EXIT_FAILURE);
+	}
+
+	/*
+	 * If vrrpd is started by other process, it will inherit the
+	 * signal block mask. We unblock all signals to make sure the
+	 * signal handling will work normally.
+	 */
+	(void) sigfillset(&mask);
+	(void) thr_sigsetmask(SIG_UNBLOCK, &mask, NULL);
+	sa.sa_handler = vrrpd_cleanup;
+	sa.sa_flags = 0;
+	(void) sigemptyset(&sa.sa_mask);
+	(void) sigaction(SIGINT, &sa, NULL);
+	(void) sigaction(SIGQUIT, &sa, NULL);
+	(void) sigaction(SIGTERM, &sa, NULL);
+
+	vrrp_debug_level = 0;
+	(void) strlcpy(vrrpd_conffile, VRRPCONF, sizeof (vrrpd_conffile));
+	while ((c = getopt(argc, argv, "d:f:")) != EOF) {
+		switch (c) {
+		case 'd':
+			vrrp_debug_level = atoi(optarg);
+			break;
+		case 'f':
+			(void) strlcpy(vrrpd_conffile, optarg,
+			    sizeof (vrrpd_conffile));
+			break;
+		default:
+			break;
+		}
+	}
+
+	closefrom(3);
+	if (vrrp_debug_level == 0 && (daemon_init() != 0)) {
+		vrrp_log(VRRP_ERR, "main(): daemon_init() failed");
+		return (EXIT_FAILURE);
+	}
+
+	rl.rlim_cur = RLIM_INFINITY;
+	rl.rlim_max = RLIM_INFINITY;
+	if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
+		vrrp_log(VRRP_ERR, "main(): setrlimit() failed");
+		return (EXIT_FAILURE);
+	}
+
+	if (vrrpd_init() != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "main(): vrrpd_init() failed");
+		return (EXIT_FAILURE);
+	}
+
+	/*
+	 * Get rid of unneeded privileges.
+	 */
+	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
+	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, PRIV_SYS_RESOURCE, NULL);
+
+	/*
+	 * Read the configuration and initialize the existing VRRP
+	 * configuration
+	 */
+	vrrpd_initconf();
+
+	/*
+	 * Start the loop to handle the timer and the IO events.
+	 */
+	switch (iu_handle_events(vrrpd_eh, vrrpd_timerq)) {
+	case -1:
+		vrrp_log(VRRP_ERR, "main(): iu_handle_events() failed "
+		    "abnormally");
+		break;
+	default:
+		break;
+	}
+
+	vrrpd_cleanup();
+	return (EXIT_SUCCESS);
+}
+
+static int
+daemon_init()
+{
+	pid_t	pid;
+
+	vrrp_log(VRRP_DBG0, "daemon_init()");
+
+	if (getenv("SMF_FMRI") == NULL) {
+		vrrp_log(VRRP_ERR, "main(): vrrpd is an smf(5) managed service "
+		    "and should not be run from the command line.");
+		return (-1);
+	}
+
+	if ((pid = fork()) < 0)
+		return (-1);
+
+	if (pid != 0) {
+		/* in parent process: do nothing. */
+		exit(0);
+	}
+
+	/*
+	 * in child process, became a daemon, and return to main() to continue.
+	 */
+	(void) chdir("/");
+	(void) setsid();
+	(void) close(0);
+	(void) close(1);
+	(void) close(2);
+	(void) open("/dev/null", O_RDWR, 0);
+	(void) dup2(0, 1);
+	(void) dup2(0, 2);
+	openlog("vrrpd", LOG_PID, LOG_DAEMON);
+	vrrp_logflag = 1;
+	return (0);
+}
+
+static vrrp_err_t
+vrrpd_init()
+{
+	vrrp_err_t	err = VRRP_ESYS;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_init()");
+
+	TAILQ_INIT(&vrrp_vr_list);
+	TAILQ_INIT(&vrrp_intf_list);
+
+	if (vrrp_open(&vrrpd_vh) != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrp_open() failed");
+		goto fail;
+	}
+
+	if ((vrrpd_timerq = iu_tq_create()) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_init(): iu_tq_create() failed");
+		goto fail;
+	}
+
+	if ((vrrpd_eh = iu_eh_create()) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_init(): iu_eh_create() failed");
+		goto fail;
+	}
+
+	/*
+	 * Create the AF_UNIX socket used to communicate with libvrrpadm.
+	 *
+	 * This socket is used to receive the administrative requests and
+	 * send back the results.
+	 */
+	if (vrrpd_cmdsock_create() != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrpd_cmdsock_create() "
+		    "failed");
+		goto fail;
+	}
+
+	/*
+	 * Create the VRRP control socket used to bring up/down the virtual
+	 * IP addresses. It is also used to set the IFF_NOACCEPT flag of
+	 * the virtual IP addresses.
+	 */
+	if (vrrpd_ctlsock_create() != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrpd_ctlsock_create() "
+		    "failed");
+		goto fail;
+	}
+
+	/*
+	 * Create the PF_ROUTER socket used to listen to the routing socket
+	 * messages and build the interface/IP address list.
+	 */
+	if (vrrpd_rtsock_create() != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrpd_rtsock_create() "
+		    "failed");
+		goto fail;
+	}
+
+	/*
+	 * Build the list of interfaces and IP addresses. Also, start the time
+	 * to scan the interfaces/IP addresses periodically.
+	 */
+	vrrpd_scan(AF_INET);
+	vrrpd_scan(AF_INET6);
+	if ((vrrp_scan_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
+	    vrrpd_scan_interval, vrrpd_scan_timer, NULL)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_init(): start scan_timer failed");
+		goto fail;
+	}
+
+	/*
+	 * Initialize the VRRP multicast address.
+	 */
+	bzero(&vrrp_muladdr4, sizeof (vrrp_addr_t));
+	vrrp_muladdr4.in4.sin_family = AF_INET;
+	(void) inet_pton(AF_INET, "224.0.0.18", &vrrp_muladdr4.in4.sin_addr);
+
+	bzero(&vrrp_muladdr6, sizeof (vrrp_addr_t));
+	vrrp_muladdr6.in6.sin6_family = AF_INET6;
+	(void) inet_pton(AF_INET6, "ff02::12", &vrrp_muladdr6.in6.sin6_addr);
+
+	return (VRRP_SUCCESS);
+
+fail:
+	vrrpd_fini();
+	return (err);
+}
+
+static void
+vrrpd_fini()
+{
+	vrrp_log(VRRP_DBG0, "vrrpd_fini()");
+
+	(void) iu_cancel_timer(vrrpd_timerq, vrrp_scan_timer_id, NULL);
+	vrrp_scan_timer_id = -1;
+
+	vrrpd_rtsock_destroy();
+	vrrpd_ctlsock_destroy();
+	vrrpd_cmdsock_destroy();
+
+	if (vrrpd_eh != NULL) {
+		iu_eh_destroy(vrrpd_eh);
+		vrrpd_eh = NULL;
+	}
+
+	if (vrrpd_timerq != NULL) {
+		iu_tq_destroy(vrrpd_timerq);
+		vrrpd_timerq = NULL;
+	}
+
+	vrrp_close(vrrpd_vh);
+	vrrpd_vh = NULL;
+	assert(TAILQ_EMPTY(&vrrp_vr_list));
+	assert(TAILQ_EMPTY(&vrrp_intf_list));
+}
+
+static void
+vrrpd_cleanup(void)
+{
+	vrrp_vr_t	*vr;
+	vrrp_intf_t	*intf;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_cleanup()");
+
+	while (!TAILQ_EMPTY(&vrrp_vr_list)) {
+		vr = TAILQ_FIRST(&vrrp_vr_list);
+		vrrpd_delete_vr(vr);
+	}
+
+	while (!TAILQ_EMPTY(&vrrp_intf_list)) {
+		intf = TAILQ_FIRST(&vrrp_intf_list);
+		vrrpd_delete_if(intf, _B_FALSE);
+	}
+
+	vrrpd_fini();
+	closelog();
+	exit(1);
+}
+
+/*
+ * Read the configuration file and initialize all the existing VRRP routers.
+ */
+static void
+vrrpd_initconf()
+{
+	FILE *fp;
+	char line[LINE_MAX];
+	int linenum = 0;
+	vrrp_vr_conf_t conf;
+	vrrp_err_t err;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_initconf()");
+
+	if ((fp = fopen(vrrpd_conffile, "rF")) == NULL) {
+		vrrp_log(VRRP_ERR, "failed to open the configuration file %s",
+		    vrrpd_conffile);
+		return;
+	}
+
+	while (fgets(line, sizeof (line), fp) != NULL) {
+		linenum++;
+		conf.vvc_vrid = VRRP_VRID_NONE;
+		if ((err = vrrpd_read_vrconf(line, &conf)) != VRRP_SUCCESS) {
+			vrrp_log(VRRP_ERR, "failed to parse %d line %s",
+			    linenum, line);
+			continue;
+		}
+
+		/*
+		 * Blank or comment line
+		 */
+		if (conf.vvc_vrid == VRRP_VRID_NONE)
+			continue;
+
+		/*
+		 * No need to update the configuration since the VRRP router
+		 * created/enabled based on the existing configuration.
+		 */
+		if ((err = vrrpd_create(&conf, _B_FALSE)) != VRRP_SUCCESS) {
+			vrrp_log(VRRP_ERR, "VRRP router %s creation failed: "
+			    "%s", conf.vvc_name, vrrp_err2str(err));
+			continue;
+		}
+
+		if (conf.vvc_enabled &&
+		    ((err = vrrpd_enable(conf.vvc_name, _B_FALSE)) !=
+		    VRRP_SUCCESS)) {
+			vrrp_log(VRRP_ERR, "VRRP router %s enable failed: %s",
+			    conf.vvc_name, vrrp_err2str(err));
+		}
+	}
+
+	(void) fclose(fp);
+}
+
+/*
+ * Create the AF_UNIX socket used to communicate with libvrrpadm.
+ *
+ * This socket is used to receive the administrative request and
+ * send back the results.
+ */
+static vrrp_err_t
+vrrpd_cmdsock_create()
+{
+	iu_event_id_t		eid;
+	struct sockaddr_un	laddr;
+	int			sock, flags;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_cmdsock_create()");
+
+	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): socket(AF_UNIX) "
+		    "failed: %s", strerror(errno));
+		return (VRRP_ESYS);
+	}
+
+	/*
+	 * Set it to be non-blocking.
+	 */
+	flags = fcntl(sock, F_GETFL, 0);
+	(void) fcntl(sock, F_SETFL, (flags | O_NONBLOCK));
+
+	/*
+	 * Unlink first in case a previous daemon instance exited ungracefully.
+	 */
+	(void) unlink(VRRPD_SOCKET);
+
+	bzero(&laddr, sizeof (laddr));
+	laddr.sun_family = AF_UNIX;
+	(void) strlcpy(laddr.sun_path, VRRPD_SOCKET, sizeof (laddr.sun_path));
+	if (bind(sock, (struct sockaddr *)&laddr, sizeof (laddr)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): bind() failed: %s",
+		    strerror(errno));
+		(void) close(sock);
+		return (VRRP_ESYS);
+	}
+
+	if (listen(sock, 30) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): listen() "
+		    "failed: %s", strerror(errno));
+		(void) close(sock);
+		return (VRRP_ESYS);
+	}
+
+	if ((eid = iu_register_event(vrrpd_eh, sock, POLLIN,
+	    vrrpd_cmdsock_handler, NULL)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): iu_register_event()"
+		    " failed");
+		(void) close(sock);
+		return (VRRP_ESYS);
+	}
+
+	vrrpd_cmdsock_fd = sock;
+	vrrpd_cmdsock_eid = eid;
+	return (VRRP_SUCCESS);
+}
+
+static void
+vrrpd_cmdsock_destroy()
+{
+	vrrp_log(VRRP_DBG0, "vrrpd_cmdsock_destroy()");
+
+	(void) iu_unregister_event(vrrpd_eh, vrrpd_cmdsock_eid, NULL);
+	(void) close(vrrpd_cmdsock_fd);
+	vrrpd_cmdsock_fd = -1;
+	vrrpd_cmdsock_eid = -1;
+}
+
+/*
+ * Create the PF_ROUTER sockets used to listen to the routing socket
+ * messages and build the interface/IP address list. Create one for
+ * each address family (IPv4 and IPv6).
+ */
+static vrrp_err_t
+vrrpd_rtsock_create()
+{
+	int		i, flags, sock;
+	iu_event_id_t	eid;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_rtsock_create()");
+
+	for (i = 0; i < 2; i++) {
+		sock = socket(PF_ROUTE, SOCK_RAW, vrrpd_rtsocks[i].vrt_af);
+		if (sock == -1) {
+			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): socket() "
+			    "failed: %s", strerror(errno));
+			break;
+		}
+
+		/*
+		 * Set it to be non-blocking.
+		 */
+		if ((flags = fcntl(sock, F_GETFL, 0)) < 0) {
+			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): "
+			    "fcntl(F_GETFL) failed: %s", strerror(errno));
+			break;
+		}
+
+		if ((fcntl(sock, F_SETFL, flags | O_NONBLOCK)) < 0) {
+			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): "
+			    "fcntl(F_SETFL) failed: %s", strerror(errno));
+			break;
+		}
+
+		if ((eid = iu_register_event(vrrpd_eh, sock, POLLIN,
+		    vrrpd_rtsock_handler, &(vrrpd_rtsocks[i].vrt_af))) == -1) {
+			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): register "
+			    "rtsock %d(%s) failed", sock,
+			    af_str(vrrpd_rtsocks[i].vrt_af));
+			break;
+		}
+
+		vrrpd_rtsocks[i].vrt_fd = sock;
+		vrrpd_rtsocks[i].vrt_eid = eid;
+	}
+
+	if (i != 2) {
+		(void) close(sock);
+		vrrpd_rtsock_destroy();
+		return (VRRP_ESYS);
+	}
+
+	return (VRRP_SUCCESS);
+}
+
+static void
+vrrpd_rtsock_destroy()
+{
+	int		i;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_rtsock_destroy()");
+	for (i = 0; i < 2; i++) {
+		(void) iu_unregister_event(vrrpd_eh, vrrpd_rtsocks[i].vrt_eid,
+		    NULL);
+		(void) close(vrrpd_rtsocks[i].vrt_fd);
+		vrrpd_rtsocks[i].vrt_eid = -1;
+		vrrpd_rtsocks[i].vrt_fd = -1;
+	}
+}
+
+/*
+ * Create the VRRP control socket used to bring up/down the virtual
+ * IP addresses. It is also used to set the IFF_NOACCEPT flag of
+ * the virtual IP addresses.
+ */
+static vrrp_err_t
+vrrpd_ctlsock_create()
+{
+	int	s, s6;
+	int	on = _B_TRUE;
+
+	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): socket(INET) "
+		    "failed: %s", strerror(errno));
+		return (VRRP_ESYS);
+	}
+	if (setsockopt(s, SOL_SOCKET, SO_VRRP, &on, sizeof (on)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): "
+		    "setsockopt(INET, SO_VRRP) failed: %s", strerror(errno));
+		(void) close(s);
+		return (VRRP_ESYS);
+	}
+
+	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): socket(INET6) "
+		    "failed: %s", strerror(errno));
+		(void) close(s);
+		return (VRRP_ESYS);
+	}
+	if (setsockopt(s6, SOL_SOCKET, SO_VRRP, &on, sizeof (on)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): "
+		    "setsockopt(INET6, SO_VRRP) failed: %s", strerror(errno));
+		(void) close(s);
+		(void) close(s6);
+		return (VRRP_ESYS);
+	}
+
+	vrrpd_ctlsock_fd = s;
+	vrrpd_ctlsock6_fd = s6;
+	return (VRRP_SUCCESS);
+}
+
+static void
+vrrpd_ctlsock_destroy()
+{
+	(void) close(vrrpd_ctlsock_fd);
+	vrrpd_ctlsock_fd = -1;
+	(void) close(vrrpd_ctlsock6_fd);
+	vrrpd_ctlsock6_fd = -1;
+}
+
+/*ARGSUSED*/
+static void
+vrrpd_cmd_create(void *arg1, void *arg2, size_t *arg2_sz)
+{
+	vrrp_cmd_create_t	*cmd = (vrrp_cmd_create_t *)arg1;
+	vrrp_ret_create_t	*ret = (vrrp_ret_create_t *)arg2;
+	vrrp_err_t		err;
+
+	err = vrrpd_create(&cmd->vcc_conf, _B_TRUE);
+	if (err == VRRP_SUCCESS && cmd->vcc_conf.vvc_enabled) {
+		/*
+		 * No need to update the configuration since it is already
+		 * done in the above vrrpd_create() call
+		 */
+		err = vrrpd_enable(cmd->vcc_conf.vvc_name, _B_FALSE);
+		if (err != VRRP_SUCCESS)
+			(void) vrrpd_delete(cmd->vcc_conf.vvc_name);
+	}
+	ret->vrc_err = err;
+}
+
+/*ARGSUSED*/
+static void
+vrrpd_cmd_delete(void *arg1, void *arg2, size_t *arg2_sz)
+{
+	vrrp_cmd_delete_t	*cmd = (vrrp_cmd_delete_t *)arg1;
+	vrrp_ret_delete_t	*ret = (vrrp_ret_delete_t *)arg2;
+
+	ret->vrd_err = vrrpd_delete(cmd->vcd_name);
+}
+
+/*ARGSUSED*/
+static void
+vrrpd_cmd_enable(void *arg1, void *arg2, size_t *arg2_sz)
+{
+	vrrp_cmd_enable_t	*cmd = (vrrp_cmd_enable_t *)arg1;
+	vrrp_ret_enable_t	*ret = (vrrp_ret_enable_t *)arg2;
+
+	ret->vrs_err = vrrpd_enable(cmd->vcs_name, _B_TRUE);
+}
+
+/*ARGSUSED*/
+static void
+vrrpd_cmd_disable(void *arg1, void *arg2, size_t *arg2_sz)
+{
+	vrrp_cmd_disable_t	*cmd = (vrrp_cmd_disable_t *)arg1;
+	vrrp_ret_disable_t	*ret = (vrrp_ret_disable_t *)arg2;
+
+	ret->vrx_err = vrrpd_disable(cmd->vcx_name);
+}
+
+/*ARGSUSED*/
+static void
+vrrpd_cmd_modify(void *arg1, void *arg2, size_t *arg2_sz)
+{
+	vrrp_cmd_modify_t	*cmd = (vrrp_cmd_modify_t *)arg1;
+	vrrp_ret_modify_t	*ret = (vrrp_ret_modify_t *)arg2;
+
+	ret->vrm_err = vrrpd_modify(&cmd->vcm_conf, cmd->vcm_mask);
+}
+
+static void
+vrrpd_cmd_query(void *arg1, void *arg2, size_t *arg2_sz)
+{
+	vrrp_cmd_query_t	*cmd = (vrrp_cmd_query_t *)arg1;
+
+	vrrpd_query(cmd->vcq_name, arg2, arg2_sz);
+}
+
+static void
+vrrpd_cmd_list(void *arg1, void *arg2, size_t *arg2_sz)
+{
+	vrrp_cmd_list_t	*cmd = (vrrp_cmd_list_t *)arg1;
+
+	vrrpd_list(cmd->vcl_vrid, cmd->vcl_ifname, cmd->vcl_af, arg2, arg2_sz);
+}
+
+/*
+ * Write-type requeset must have the solaris.network.vrrp authorization.
+ */
+static boolean_t
+vrrp_auth_check(int connfd, vrrp_cmd_info_t *cinfo)
+{
+	ucred_t		*cred = NULL;
+	uid_t		uid;
+	struct passwd	*pw;
+	boolean_t	success = _B_FALSE;
+
+	vrrp_log(VRRP_DBG0, "vrrp_auth_check()");
+
+	if (!cinfo->vi_setop)
+		return (_B_TRUE);
+
+	/*
+	 * Validate the credential
+	 */
+	if (getpeerucred(connfd, &cred) == (uid_t)-1) {
+		vrrp_log(VRRP_ERR, "vrrp_auth_check(): getpeerucred() "
+		    "failed: %s", strerror(errno));
+		return (_B_FALSE);
+	}
+
+	if ((uid = ucred_getruid((const ucred_t *)cred)) == (uid_t)-1) {
+		vrrp_log(VRRP_ERR, "vrrp_auth_check(): ucred_getruid() "
+		    "failed: %s", strerror(errno));
+		goto done;
+	}
+
+	if ((pw = getpwuid(uid)) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrp_auth_check(): getpwuid() failed");
+		goto done;
+	}
+
+	success = (chkauthattr("solaris.network.vrrp", pw->pw_name) == 1);
+
+done:
+	ucred_free(cred);
+	return (success);
+}
+
+/*
+ * Process the administrative request from libvrrpadm
+ */
+/* ARGSUSED */
+static void
+vrrpd_cmdsock_handler(iu_eh_t *eh, int s, short events, iu_event_id_t id,
+    void *arg)
+{
+	vrrp_cmd_info_t		*cinfo = NULL;
+	vrrp_err_t		err = VRRP_SUCCESS;
+	uchar_t			buf[BUFFSIZE], ackbuf[BUFFSIZE];
+	size_t			cursize, acksize, len;
+	uint32_t		cmd;
+	int			connfd, i;
+	struct sockaddr_in	from;
+	socklen_t		fromlen;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_cmdsock_handler()");
+
+	fromlen = (socklen_t)sizeof (from);
+	if ((connfd = accept(s, (struct sockaddr *)&from, &fromlen)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler() accept(): %s",
+		    strerror(errno));
+		return;
+	}
+
+	/*
+	 * First get the type of the request
+	 */
+	cursize = 0;
+	while (cursize < sizeof (uint32_t)) {
+		len = read(connfd, buf + cursize,
+		    sizeof (uint32_t) - cursize);
+		if (len == (size_t)-1 && (errno == EAGAIN || errno == EINTR)) {
+			continue;
+		} else if (len > 0) {
+			cursize += len;
+			continue;
+		}
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): invalid message "
+		    "length");
+		(void) close(connfd);
+		return;
+	}
+
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	cmd = ((vrrp_cmd_t *)buf)->vc_cmd;
+	for (i = 0; i < VRRP_DOOR_INFO_TABLE_SIZE; i++) {
+		if (vrrp_cmd_info_tbl[i].vi_cmd == cmd) {
+			cinfo = vrrp_cmd_info_tbl + i;
+			break;
+		}
+	}
+
+	if (cinfo == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): invalid request "
+		    "type %d", cmd);
+		err = VRRP_EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Get the rest of the request.
+	 */
+	assert(cursize == sizeof (uint32_t));
+	while (cursize < cinfo->vi_reqsize) {
+		len = read(connfd, buf + cursize,
+		    cinfo->vi_reqsize - cursize);
+		if (len == (size_t)-1 && (errno == EAGAIN || errno == EINTR)) {
+			continue;
+		} else if (len > 0) {
+			cursize += len;
+			continue;
+		}
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): invalid message "
+		    "length");
+		err = VRRP_EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Validate the authorization
+	 */
+	if (!vrrp_auth_check(connfd, cinfo)) {
+		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): "
+		    "not sufficient authorization");
+		err = VRRP_EPERM;
+	}
+
+done:
+	/*
+	 * Ack the request
+	 */
+	if (err != 0) {
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		((vrrp_ret_t *)ackbuf)->vr_err = err;
+		acksize = sizeof (vrrp_ret_t);
+	} else {
+		/*
+		 * If the size of ack is varied, the cmdfunc callback
+		 * will set the right size.
+		 */
+		if ((acksize = cinfo->vi_acksize) == 0)
+			acksize = sizeof (ackbuf);
+
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		cinfo->vi_cmdfunc((vrrp_cmd_t *)buf, ackbuf, &acksize);
+	}
+
+	/*
+	 * Send the ack back.
+	 */
+	cursize = 0;
+	while (cursize < acksize) {
+		len = sendto(connfd, ackbuf + cursize, acksize - cursize,
+		    0, (struct sockaddr *)&from, fromlen);
+		if (len == (size_t)-1 && errno == EAGAIN) {
+			continue;
+		} else if (len > 0) {
+			cursize += len;
+			continue;
+		} else {
+			vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler() failed to "
+			    "ack: %s", strerror(errno));
+			break;
+		}
+	}
+
+	(void) shutdown(connfd, SHUT_RDWR);
+	(void) close(connfd);
+}
+
+/*
+ * Process the routing socket messages and update the interfaces/IP addresses
+ * list
+ */
+/* ARGSUSED */
+static void
+vrrpd_rtsock_handler(iu_eh_t *eh, int s, short events,
+    iu_event_id_t id, void *arg)
+{
+	char			buf[BUFFSIZE];
+	struct ifa_msghdr	*ifam;
+	int			nbytes;
+	int			af = *(int *)arg;
+	boolean_t		scanif = _B_FALSE;
+
+	for (;;) {
+		nbytes = read(s, buf, sizeof (buf));
+		if (nbytes <= 0) {
+			/* No more messages */
+			break;
+		}
+
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		ifam = (struct ifa_msghdr *)buf;
+		if (ifam->ifam_version != RTM_VERSION) {
+			vrrp_log(VRRP_ERR, "vrrpd_rtsock_handler(): version %d "
+			    "not understood", ifam->ifam_version);
+			break;
+		}
+
+		vrrp_log(VRRP_DBG0, "vrrpd_rtsock_handler(): recv %s event",
+		    rtm_event2str(ifam->ifam_type));
+
+		switch (ifam->ifam_type) {
+		case RTM_FREEADDR:
+		case RTM_CHGADDR:
+		case RTM_NEWADDR:
+		case RTM_DELADDR:
+			/*
+			 * An IP address has been created/updated/deleted or
+			 * brought up/down, re-initilialize the interface/IP
+			 * address list.
+			 */
+			scanif = _B_TRUE;
+			break;
+		default:
+			/* Not interesting */
+			break;
+		}
+	}
+
+	if (scanif)
+		vrrpd_scan(af);
+}
+
+/*
+ * Periodically scan the interface/IP addresses on the system.
+ */
+/* ARGSUSED */
+static void
+vrrpd_scan_timer(iu_tq_t *tq, void *arg)
+{
+	vrrp_log(VRRP_DBG0, "vrrpd_scan_timer()");
+	vrrpd_scan(AF_INET);
+	vrrpd_scan(AF_INET6);
+}
+
+/*
+ * Get the list of the interface/IP addresses of the specified address
+ * family.
+ */
+static void
+vrrpd_scan(int af)
+{
+	vrrp_log(VRRP_DBG0, "vrrpd_scan(%s)", af_str(af));
+
+again:
+	vrrpd_init_ipcache(af);
+
+	/*
+	 * If interface index changes, walk again.
+	 */
+	if (icfg_iterate_if(af, ICFG_PLUMBED, NULL,
+	    vrrpd_walk_ipaddr) != ICFG_SUCCESS)
+		goto again;
+
+	vrrpd_update_ipcache(af);
+}
+
+/*
+ * First mark all IP addresses of the specific address family to be removed.
+ * This flag will then be cleared when we walk up all the IP addresses.
+ */
+static void
+vrrpd_init_ipcache(int af)
+{
+	vrrp_intf_t	*intf, *next_intf;
+	vrrp_ip_t	*ip, *nextip;
+	char		abuf[INET6_ADDRSTRLEN];
+
+	vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(%s)", af_str(af));
+
+	next_intf = TAILQ_FIRST(&vrrp_intf_list);
+	while ((intf = next_intf) != NULL) {
+		next_intf = TAILQ_NEXT(intf, vvi_next);
+		if (intf->vvi_af != af)
+			continue;
+
+		/*
+		 * If the interface is still marked as new, it means that this
+		 * vrrpd_init_ipcache() call is a result of ifindex change,
+		 * which causes the re-walk of all the interfaces (see
+		 * vrrpd_add_ipaddr()), and some interfaces are still marked
+		 * as new during the last walk. In this case, delete this
+		 * interface with the "update_vr" argument to be _B_FALSE,
+		 * since no VRRP router has been assoicated with this
+		 * interface yet (the association is done in
+		 * vrrpd_update_ipcache()).
+		 *
+		 * This interface will be re-added later if it still exists.
+		 */
+		if (intf->vvi_state == NODE_STATE_NEW) {
+			vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(): remove %s "
+			    "(%d), may be added later", intf->vvi_ifname,
+			    intf->vvi_ifindex);
+			vrrpd_delete_if(intf, _B_FALSE);
+			continue;
+		}
+
+		for (ip = TAILQ_FIRST(&intf->vvi_iplist); ip != NULL;
+		    ip = nextip) {
+			nextip = TAILQ_NEXT(ip, vip_next);
+			/* LINTED E_CONSTANT_CONDITION */
+			VRRPADDR2STR(af, &ip->vip_addr, abuf,
+			    INET6_ADDRSTRLEN, _B_FALSE);
+
+			if (ip->vip_state != NODE_STATE_NEW) {
+				vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(%s/%d, "
+				    "%s(%s/0x%x))", intf->vvi_ifname,
+				    intf->vvi_ifindex, ip->vip_lifname,
+				    abuf, ip->vip_flags);
+				ip->vip_state = NODE_STATE_STALE;
+				continue;
+			}
+
+			/*
+			 * If the IP is still marked as new, it means that
+			 * this vrrpd_init_ipcache() call is a result of
+			 * ifindex change, which causes the re-walk of all
+			 * the IP addresses (see vrrpd_add_ipaddr()).
+			 * Delete this IP.
+			 *
+			 * This IP will be readded later if it still exists.
+			 */
+			vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(): remove "
+			    "%s/%d , %s(%s)", intf->vvi_ifname,
+			    intf->vvi_ifindex, ip->vip_lifname, abuf);
+			vrrpd_delete_ip(intf, ip);
+		}
+	}
+}
+
+/*
+ * Walk all the IP addresses on the given interface and update its
+ * addresses list. Return ICFG_FAILURE if it is required to walk
+ * all the interfaces again (one of the interface index changes in between).
+ */
+/* ARGSUSED */
+static int
+vrrpd_walk_ipaddr(icfg_if_t *intf, void *arg)
+{
+	icfg_handle_t	ih;
+	int		ifindex;
+	vrrp_addr_t	addr;
+	socklen_t	addrlen = (socklen_t)sizeof (struct sockaddr_in6);
+	int		prefixlen;
+	uint64_t	flags;
+	int		err = ICFG_SUCCESS;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_walk_ipaddr(%s, %s)", intf->if_name,
+	    af_str(intf->if_protocol));
+
+	if (icfg_open(&ih, intf) != ICFG_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_walk_ipaddr(%s, %s): icfg_open() "
+		    "failed: %s", intf->if_name, af_str(intf->if_protocol),
+		    strerror(errno));
+		return (err);
+	}
+
+	if (icfg_get_flags(ih, &flags) != ICFG_SUCCESS) {
+		if (errno != ENXIO && errno != ENOENT) {
+			vrrp_log(VRRP_ERR, "vrrpd_walk_ipaddr(%s, %s): "
+			    "icfg_get_flags() failed %s", intf->if_name,
+			    af_str(intf->if_protocol), strerror(errno));
+		}
+		goto done;
+	}
+
+	/*
+	 * skip virtual/IPMP/P2P interfaces.
+	 */
+	if ((flags & (IFF_VIRTUAL|IFF_IPMP|IFF_POINTOPOINT)) != 0) {
+		vrrp_log(VRRP_DBG0, "vrrpd_walk_ipaddr(%s, %s) skipped",
+		    intf->if_name, af_str(intf->if_protocol));
+		goto done;
+	}
+
+	if (icfg_get_index(ih, &ifindex) != ICFG_SUCCESS) {
+		if (errno != ENXIO && errno != ENOENT) {
+			vrrp_log(VRRP_ERR, "vrrpd_walk_ipaddr(%s, %s) "
+			    "icfg_get_index() failed: %s", intf->if_name,
+			    af_str(intf->if_protocol), strerror(errno));
+		}
+		goto done;
+	}
+
+	if (icfg_get_addr(ih, (struct sockaddr *)&addr, &addrlen,
+	    &prefixlen, _B_FALSE) != ICFG_SUCCESS) {
+		if (errno != ENXIO && errno != ENOENT) {
+			vrrp_log(VRRP_ERR, "vrrpd_walk_ipaddr(%s, %s) "
+			    "icfg_get_addr() failed: %s", intf->if_name,
+			    af_str(intf->if_protocol), strerror(errno));
+		}
+		goto done;
+	}
+
+	/*
+	 * Filter out the all-zero IP address.
+	 */
+	if (VRRPADDR_UNSPECIFIED(intf->if_protocol, &addr))
+		goto done;
+
+	/*
+	 * The interface is unplumbed/replumbed during we walk the IP
+	 * addresses. Try walk the IP addresses one more time.
+	 */
+	if (vrrpd_add_ipaddr(intf->if_name, intf->if_protocol,
+	    &addr, ifindex, flags) == VRRP_EAGAIN)
+		err = ICFG_FAILURE;
+
+done:
+	icfg_close(ih);
+	return (err);
+}
+
+/*
+ * Given the information of each IP address, update the interface and
+ * IP addresses list
+ */
+static vrrp_err_t
+vrrpd_add_ipaddr(char *lifname, int af, vrrp_addr_t *addr, int ifindex,
+    uint64_t flags)
+{
+	char		ifname[LIFNAMSIZ], *c;
+	vrrp_intf_t	*intf;
+	vrrp_ip_t	*ip;
+	char		abuf[INET6_ADDRSTRLEN];
+	vrrp_err_t	err;
+
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(af, addr, abuf, INET6_ADDRSTRLEN, _B_FALSE);
+	vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s, %s, %d, 0x%x)", lifname,
+	    abuf, ifindex, flags);
+
+	/*
+	 * Get the physical interface name from the logical interface name.
+	 */
+	(void) strlcpy(ifname, lifname, sizeof (ifname));
+	if ((c = strchr(ifname, ':')) != NULL)
+		*c = '\0';
+
+	if ((intf = vrrpd_lookup_if(ifname, af)) == NULL) {
+		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(): %s is new", ifname);
+		err = vrrpd_create_if(ifname, af, ifindex, &intf);
+		if (err != VRRP_SUCCESS)
+			return (err);
+	} else if (intf->vvi_ifindex != ifindex) {
+		/*
+		 * If index changes, it means that this interface is
+		 * unplumbed/replumbed since we last checked. If this
+		 * interface is not used by any VRRP router, just
+		 * update its ifindex, and the IP addresses list will
+		 * be updated later. Otherwise, return EAGAIN to rewalk
+		 * all the IP addresses from the beginning.
+		 */
+		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s) ifindex changed ",
+		    "from %d to %d", ifname, intf->vvi_ifindex, ifindex);
+		if (!IS_PRIMARY_INTF(intf) && !IS_VIRTUAL_INTF(intf)) {
+			intf->vvi_ifindex = ifindex;
+		} else {
+			/*
+			 * delete this interface from the list if this
+			 * interface has already been assoicated with
+			 * any VRRP routers.
+			 */
+			vrrpd_delete_if(intf, _B_TRUE);
+			return (VRRP_EAGAIN);
+		}
+	}
+
+	/*
+	 * Does this IP address already exist?
+	 */
+	TAILQ_FOREACH(ip, &intf->vvi_iplist, vip_next) {
+		if (strcmp(ip->vip_lifname, lifname) == 0)
+			break;
+	}
+
+	if (ip != NULL) {
+		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s, %s) IP exists",
+		    lifname, abuf);
+		ip->vip_state = NODE_STATE_NONE;
+		ip->vip_flags = flags;
+		if (ipaddr_cmp(af, addr, &ip->vip_addr) != 0) {
+			/*
+			 * Address has been changed, mark it as new
+			 * If this address is already selected as the
+			 * primary IP address, the new IP will be checked
+			 * to see whether it is still qualified as the
+			 * primary IP address. If not, the primary IP
+			 * address will be reselected.
+			 */
+			(void) memcpy(&ip->vip_addr, addr,
+			    sizeof (vrrp_addr_t));
+
+			ip->vip_state = NODE_STATE_NEW;
+		}
+	} else {
+		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s, %s) IP is new",
+		    lifname, abuf);
+
+		err = vrrpd_create_ip(intf, lifname, addr, flags);
+		if (err != VRRP_SUCCESS)
+			return (err);
+	}
+	return (VRRP_SUCCESS);
+}
+
+/*
+ * Update the interface and IP addresses list. Remove the ones that have been
+ * staled since last time we walk the IP addresses and updated the ones that
+ * have been changed.
+ */
+static void
+vrrpd_update_ipcache(int af)
+{
+	vrrp_intf_t	*intf, *nextif;
+	vrrp_ip_t	*ip, *nextip;
+	char		abuf[INET6_ADDRSTRLEN];
+	boolean_t	primary_selected;
+	boolean_t	primary_now_selected;
+	boolean_t	need_reenable = _B_FALSE;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(%s)", af_str(af));
+
+	nextif = TAILQ_FIRST(&vrrp_intf_list);
+	while ((intf = nextif) != NULL) {
+		nextif = TAILQ_NEXT(intf, vvi_next);
+		if (intf->vvi_af != af)
+			continue;
+
+		/*
+		 * Does the interface already select its primary IP address?
+		 */
+		primary_selected = (intf->vvi_pip != NULL);
+		assert(!primary_selected || IS_PRIMARY_INTF(intf));
+
+		/*
+		 * Removed the IP addresses that have been unconfigured.
+		 */
+		for (ip = TAILQ_FIRST(&intf->vvi_iplist); ip != NULL;
+		    ip = nextip) {
+			nextip = TAILQ_NEXT(ip, vip_next);
+			if (ip->vip_state != NODE_STATE_STALE)
+				continue;
+
+			/* LINTED E_CONSTANT_CONDITION */
+			VRRPADDR2STR(af, &ip->vip_addr, abuf, INET6_ADDRSTRLEN,
+			    _B_FALSE);
+			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): IP %s "
+			    "is removed over %s", abuf, intf->vvi_ifname);
+			vrrpd_delete_ip(intf, ip);
+		}
+
+		/*
+		 * No IP addresses left, delete this interface.
+		 */
+		if (TAILQ_EMPTY(&intf->vvi_iplist)) {
+			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): "
+			    "no IP left over %s", intf->vvi_ifname);
+			vrrpd_delete_if(intf, _B_TRUE);
+			continue;
+		}
+
+		/*
+		 * If this is selected ss the physical interface for any
+		 * VRRP router, reselect the primary address if needed.
+		 */
+		if (IS_PRIMARY_INTF(intf)) {
+			vrrpd_reselect_primary(intf);
+			primary_now_selected = (intf->vvi_pip != NULL);
+
+			/*
+			 * Cannot find the new primary IP address.
+			 */
+			if (primary_selected && !primary_now_selected) {
+				vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache() "
+				    "reselect primary IP on %s failed",
+				    intf->vvi_ifname);
+				vrrpd_remove_if(intf, _B_TRUE);
+			} else if (!primary_selected && primary_now_selected) {
+				/*
+				 * The primary IP address is successfully
+				 * selected on the physical interfacew we
+				 * need to walk through all the VRRP routers
+				 * that is created on this physical interface
+				 * and see whether they can now be enabled.
+				 */
+				need_reenable = _B_TRUE;
+			}
+		}
+
+		/*
+		 * For every new virtual IP address, bring up/down it based
+		 * on the state of VRRP router.
+		 *
+		 * Note that it is fine to not update the IP's vip_flags field
+		 * even if vrrpd_virtualip_updateone() changed the address's
+		 * up/down state, since the vip_flags field is only used for
+		 * select primary IP address over a physical interface, and
+		 * vrrpd_virtualip_updateone() only affects the virtual IP
+		 * address's status.
+		 */
+		for (ip = TAILQ_FIRST(&intf->vvi_iplist); ip != NULL;
+		    ip = nextip) {
+			nextip = TAILQ_NEXT(ip, vip_next);
+			/* LINTED E_CONSTANT_CONDITION */
+			VRRPADDR2STR(af, &ip->vip_addr, abuf, INET6_ADDRSTRLEN,
+			    _B_FALSE);
+			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): "
+			    "IP %s over %s%s", abuf, intf->vvi_ifname,
+			    ip->vip_state == NODE_STATE_NEW ? " is new" : "");
+
+			if (IS_VIRTUAL_INTF(intf)) {
+				/*
+				 * If this IP is new, update its up/down state
+				 * based on the virtual interface's state
+				 * (which is determined by the VRRP router's
+				 * state). Otherwise, check only and prompt
+				 * warnings if its up/down state has been
+				 * changed.
+				 */
+				if (vrrpd_virtualip_updateone(intf, ip,
+				    ip->vip_state == NODE_STATE_NONE) !=
+				    VRRP_SUCCESS) {
+					vrrp_log(VRRP_DBG0,
+					    "vrrpd_update_ipcache(): "
+					    "IP %s over %s update failed", abuf,
+					    intf->vvi_ifname);
+					vrrpd_delete_ip(intf, ip);
+					continue;
+				}
+			}
+			ip->vip_state = NODE_STATE_NONE;
+		}
+
+		/*
+		 * The IP address is deleted when it is failed to be brought
+		 * up. If no IP addresses are left, delete this interface.
+		 */
+		if (TAILQ_EMPTY(&intf->vvi_iplist)) {
+			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): "
+			    "no IP left over %s", intf->vvi_ifname);
+			vrrpd_delete_if(intf, _B_TRUE);
+			continue;
+		}
+
+		if (intf->vvi_state == NODE_STATE_NEW) {
+			/*
+			 * A new interface is found. This interface can be
+			 * the primary interface or the virtual VNIC
+			 * interface.  Again, we need to walk throught all
+			 * the VRRP routers to see whether some of them can
+			 * now be enabled because of the new primary IP
+			 * address or the new virtual IP addresses.
+			 */
+			intf->vvi_state = NODE_STATE_NONE;
+			need_reenable = _B_TRUE;
+		}
+	}
+
+	if (need_reenable)
+		vrrpd_reenable_all_vr();
+}
+
+/*
+ * Reselect primary IP if:
+ * - The existing primary IP is no longer qualified (removed or it is down or
+ *   not a link-local IP for IPv6 VRRP router);
+ * - This is a physical interface but no primary IP is chosen;
+ */
+static void
+vrrpd_reselect_primary(vrrp_intf_t *intf)
+{
+	vrrp_ip_t	*ip;
+	char		abuf[INET6_ADDRSTRLEN];
+
+	assert(IS_PRIMARY_INTF(intf));
+
+	/*
+	 * If the interface's old primary IP address is still valid, return
+	 */
+	if (((ip = intf->vvi_pip) != NULL) && (QUALIFY_PRIMARY_ADDR(intf, ip)))
+		return;
+
+	if (ip != NULL) {
+		/* LINTED E_CONSTANT_CONDITION */
+		VRRPADDR2STR(intf->vvi_af, &ip->vip_addr, abuf,
+		    sizeof (abuf), _B_FALSE);
+		vrrp_log(VRRP_DBG0, "vrrpd_reselect_primary(%s): primary IP %s "
+		    "is no longer qualified", intf->vvi_ifname, abuf);
+	}
+
+	ip = vrrpd_select_primary(intf);
+	intf->vvi_pip = ip;
+
+	if (ip != NULL) {
+		/* LINTED E_CONSTANT_CONDITION */
+		VRRPADDR2STR(intf->vvi_af, &ip->vip_addr, abuf,
+		    sizeof (abuf), _B_FALSE);
+		vrrp_log(VRRP_DBG0, "vrrpd_reselect_primary(%s): primary IP %s "
+		    "is selected", intf->vvi_ifname, abuf);
+	}
+}
+
+/*
+ * Select the primary IP address. Since the link-local IP address is always
+ * at the head of the IP address list, try to find the first UP IP address
+ * and see whether it qualify.
+ */
+static vrrp_ip_t *
+vrrpd_select_primary(vrrp_intf_t *pif)
+{
+	vrrp_ip_t	*pip;
+	char		abuf[INET6_ADDRSTRLEN];
+
+	vrrp_log(VRRP_DBG1, "vrrpd_select_primary(%s)", pif->vvi_ifname);
+
+	TAILQ_FOREACH(pip, &pif->vvi_iplist, vip_next) {
+		assert(pip->vip_state != NODE_STATE_STALE);
+
+		/* LINTED E_CONSTANT_CONDITION */
+		VRRPADDR2STR(pif->vvi_af, &pip->vip_addr, abuf,
+		    INET6_ADDRSTRLEN, _B_FALSE);
+		vrrp_log(VRRP_DBG0, "vrrpd_select_primary(%s): %s is %s",
+		    pif->vvi_ifname, abuf,
+		    (pip->vip_flags & IFF_UP) ? "up" : "down");
+
+		if (pip->vip_flags & IFF_UP)
+			break;
+	}
+
+	/*
+	 * Is this valid primary IP address?
+	 */
+	if (pip == NULL || !QUALIFY_PRIMARY_ADDR(pif, pip)) {
+		vrrp_log(VRRP_DBG0, "vrrpd_select_primary(%s/%s) failed",
+		    pif->vvi_ifname, af_str(pif->vvi_af));
+		return (NULL);
+	}
+	return (pip);
+}
+
+/*
+ * This is a new interface. Check whether any VRRP router is waiting for it
+ */
+static void
+vrrpd_reenable_all_vr()
+{
+	vrrp_vr_t *vr;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_reenable_all_vr()");
+
+	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
+		if (vr->vvr_conf.vvc_enabled)
+			(void) vrrpd_enable_vr(vr);
+	}
+}
+
+/*
+ * If primary_addr_gone is _B_TRUE, it means that we failed to select
+ * the primary IP address on this (physical) interface; otherwise,
+ * it means the interface is no longer available.
+ */
+static void
+vrrpd_remove_if(vrrp_intf_t *intf, boolean_t primary_addr_gone)
+{
+	vrrp_vr_t *vr;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_remove_if(%s): %s", intf->vvi_ifname,
+	    primary_addr_gone ? "primary address gone" : "interface deleted");
+
+	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
+		if (vr->vvr_conf.vvc_enabled)
+			vrrpd_disable_vr(vr, intf, primary_addr_gone);
+	}
+}
+
+/*
+ * Update the VRRP configuration file based on the given configuration.
+ * op is either VRRP_CONF_UPDATE or VRRP_CONF_DELETE
+ */
+static vrrp_err_t
+vrrpd_updateconf(vrrp_vr_conf_t *newconf, uint_t op)
+{
+	vrrp_vr_conf_t	conf;
+	FILE		*fp, *nfp;
+	int		nfd;
+	char		line[LINE_MAX];
+	char		newfile[MAXPATHLEN];
+	boolean_t	found = _B_FALSE;
+	vrrp_err_t	err = VRRP_SUCCESS;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_updateconf(%s, %s)", newconf->vvc_name,
+	    op == VRRP_CONF_UPDATE ? "update" : "delete");
+
+	if ((fp = fopen(vrrpd_conffile, "r+F")) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): open %s failed: %s",
+		    vrrpd_conffile, strerror(errno));
+		return (VRRP_EDB);
+	}
+
+	(void) snprintf(newfile, MAXPATHLEN, "%s.new", vrrpd_conffile);
+	if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
+	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): open %s failed: %s",
+		    newfile, strerror(errno));
+		(void) fclose(fp);
+		return (VRRP_EDB);
+	}
+
+	if ((nfp = fdopen(nfd, "wF")) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): fdopen(%s) failed: %s",
+		    newfile, strerror(errno));
+		goto done;
+	}
+
+	while (fgets(line, sizeof (line), fp) != NULL) {
+		conf.vvc_vrid = VRRP_VRID_NONE;
+		if (!found && (err = vrrpd_read_vrconf(line, &conf)) !=
+		    VRRP_SUCCESS) {
+			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): invalid "
+			    "configuration format: %s", line);
+			goto done;
+		}
+
+		/*
+		 * Write this line out if:
+		 * - this is a comment line; or
+		 * - we've done updating/deleting the the given VR; or
+		 * - if the name of the VR read from this line does not match
+		 *   the VR name that we are about to update/delete;
+		 */
+		if (found || conf.vvc_vrid == VRRP_VRID_NONE ||
+		    strcmp(conf.vvc_name, newconf->vvc_name) != 0) {
+			if (fputs(line, nfp) != EOF)
+				continue;
+
+			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
+			    "write line %s", line);
+			err = VRRP_EDB;
+			goto done;
+		}
+
+		/*
+		 * Otherwise, update/skip the line.
+		 */
+		found = _B_TRUE;
+		if (op == VRRP_CONF_DELETE)
+			continue;
+
+		assert(op == VRRP_CONF_UPDATE);
+		if ((err = vrrpd_write_vrconf(line, sizeof (line),
+		    newconf)) != VRRP_SUCCESS) {
+			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
+			    "update configuration for %s", newconf->vvc_name);
+			goto done;
+		}
+		if (fputs(line, nfp) == EOF) {
+			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
+			    "write line %s", line);
+			err = VRRP_EDB;
+			goto done;
+		}
+	}
+
+	/*
+	 * If we get to the end of the file and have not seen the router that
+	 * we are about to update, write it out.
+	 */
+	if (!found && op == VRRP_CONF_UPDATE) {
+		if ((err = vrrpd_write_vrconf(line, sizeof (line),
+		    newconf)) == VRRP_SUCCESS && fputs(line, nfp) == EOF) {
+			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
+			    "write line %s", line);
+			err = VRRP_EDB;
+		}
+	} else if (!found && op == VRRP_CONF_DELETE) {
+		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to find "
+		    "configuation for %s", newconf->vvc_name);
+		err = VRRP_ENOTFOUND;
+	}
+
+	if (err != VRRP_SUCCESS)
+		goto done;
+
+	if (fflush(nfp) == EOF || rename(newfile, vrrpd_conffile) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
+		    "rename file %s", newfile);
+		err = VRRP_EDB;
+	}
+
+done:
+	(void) fclose(fp);
+	(void) fclose(nfp);
+	(void) unlink(newfile);
+	return (err);
+}
+
+static vrrp_err_t
+vrrpd_write_vrconf(char *line, size_t len, vrrp_vr_conf_t *conf)
+{
+	vrrp_prop_t	*prop;
+	int		n, i;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_write_vrconf(%s)", conf->vvc_name);
+
+	for (i = 0; i < VRRP_PROP_INFO_TABSIZE; i++) {
+		prop = &vrrp_prop_info_tbl[i];
+		n = snprintf(line, len, i == 0 ? "%s=" : " %s=",
+		    prop->vs_propname);
+		if (n < 0 || n >= len)
+			break;
+		len -= n;
+		line += n;
+		n = prop->vs_propwrite(conf, line, len);
+		if (n < 0 || n >= len)
+			break;
+		len -= n;
+		line += n;
+	}
+	if (i != VRRP_PROP_INFO_TABSIZE) {
+		vrrp_log(VRRP_ERR, "vrrpd_write_vrconf(%s): buffer size too"
+		    "small", conf->vvc_name);
+		return (VRRP_EDB);
+	}
+	n = snprintf(line, len, "\n");
+	if (n < 0 || n >= len) {
+		vrrp_log(VRRP_ERR, "vrrpd_write_vrconf(%s): buffer size too"
+		    "small", conf->vvc_name);
+		return (VRRP_EDB);
+	}
+	return (VRRP_SUCCESS);
+}
+
+static vrrp_err_t
+vrrpd_read_vrconf(char *line, vrrp_vr_conf_t *conf)
+{
+	char		*str, *token;
+	char		*next;
+	vrrp_err_t	err = VRRP_SUCCESS;
+	char		tmpbuf[MAXLINELEN];
+
+	str = tmpbuf;
+	(void) strlcpy(tmpbuf, line, MAXLINELEN);
+
+	/*
+	 * Skip leading spaces, blank lines, and comments.
+	 */
+	skip_whitespace(str);
+	if ((str - tmpbuf == strlen(tmpbuf)) || (*str == '#')) {
+		conf->vvc_vrid = VRRP_VRID_NONE;
+		return (VRRP_SUCCESS);
+	}
+
+	/*
+	 * Read each VR properties.
+	 */
+	for (token = strtok_r(str, " \n\t", &next); token != NULL;
+	    token = strtok_r(NULL, " \n\t", &next)) {
+		if ((err = vrrpd_readprop(token, conf)) != VRRP_SUCCESS)
+			break;
+	}
+
+	/* All properties read but no VRID defined */
+	if (err == VRRP_SUCCESS && conf->vvc_vrid == VRRP_VRID_NONE)
+		err = VRRP_EINVAL;
+
+	return (err);
+}
+
+static vrrp_err_t
+vrrpd_readprop(const char *str, vrrp_vr_conf_t *conf)
+{
+	vrrp_prop_t	*prop;
+	char		*pstr;
+	int		i;
+
+	if ((pstr = strchr(str, '=')) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_readprop(%s): invalid property", str);
+		return (VRRP_EINVAL);
+	}
+
+	*pstr++ = '\0';
+	for (i = 0; i < VRRP_PROP_INFO_TABSIZE; i++) {
+		prop = &vrrp_prop_info_tbl[i];
+		if (strcasecmp(str, prop->vs_propname) == 0) {
+			if (prop->vs_propread(conf, pstr))
+				break;
+		}
+	}
+
+	if (i == VRRP_PROP_INFO_TABSIZE) {
+		vrrp_log(VRRP_ERR, "vrrpd_readprop(%s): invalid property", str);
+		return (VRRP_EINVAL);
+	}
+
+	return (VRRP_SUCCESS);
+}
+
+static boolean_t
+vrrp_rd_prop_name(vrrp_vr_conf_t *conf, const char *str)
+{
+	size_t size = sizeof (conf->vvc_name);
+	return (strlcpy(conf->vvc_name, str, size) < size);
+}
+
+static boolean_t
+vrrp_rd_prop_vrid(vrrp_vr_conf_t *conf, const char *str)
+{
+	conf->vvc_vrid = strtol(str, NULL, 0);
+	return (!(conf->vvc_vrid < VRRP_VRID_MIN ||
+	    conf->vvc_vrid > VRRP_VRID_MAX ||
+	    (conf->vvc_vrid == 0 && errno != 0)));
+}
+
+static boolean_t
+vrrp_rd_prop_af(vrrp_vr_conf_t *conf, const char *str)
+{
+	if (strcasecmp(str, "AF_INET") == 0)
+		conf->vvc_af = AF_INET;
+	else if (strcasecmp(str, "AF_INET6") == 0)
+		conf->vvc_af = AF_INET6;
+	else
+		return (_B_FALSE);
+	return (_B_TRUE);
+}
+
+static boolean_t
+vrrp_rd_prop_pri(vrrp_vr_conf_t *conf, const char *str)
+{
+	conf->vvc_pri = strtol(str, NULL, 0);
+	return (!(conf->vvc_pri < VRRP_PRI_MIN ||
+	    conf->vvc_pri > VRRP_PRI_OWNER ||
+	    (conf->vvc_pri == 0 && errno != 0)));
+}
+
+static boolean_t
+vrrp_rd_prop_adver_int(vrrp_vr_conf_t *conf, const char *str)
+{
+	conf->vvc_adver_int = strtol(str, NULL, 0);
+	return (!(conf->vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
+	    conf->vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
+	    (conf->vvc_adver_int == 0 && errno != 0)));
+}
+
+static boolean_t
+vrrp_rd_prop_preempt(vrrp_vr_conf_t *conf, const char *str)
+{
+	if (strcasecmp(str, "true") == 0)
+		conf->vvc_preempt = _B_TRUE;
+	else if (strcasecmp(str, "false") == 0)
+		conf->vvc_preempt = _B_FALSE;
+	else
+		return (_B_FALSE);
+	return (_B_TRUE);
+}
+
+static boolean_t
+vrrp_rd_prop_accept(vrrp_vr_conf_t *conf, const char *str)
+{
+	if (strcasecmp(str, "true") == 0)
+		conf->vvc_accept = _B_TRUE;
+	else if (strcasecmp(str, "false") == 0)
+		conf->vvc_accept = _B_FALSE;
+	else
+		return (_B_FALSE);
+	return (_B_TRUE);
+}
+
+static boolean_t
+vrrp_rd_prop_enabled(vrrp_vr_conf_t *conf, const char *str)
+{
+	if (strcasecmp(str, "enabled") == 0)
+		conf->vvc_enabled = _B_TRUE;
+	else if (strcasecmp(str, "disabled") == 0)
+		conf->vvc_enabled = _B_FALSE;
+	else
+		return (_B_FALSE);
+	return (_B_TRUE);
+}
+
+static boolean_t
+vrrp_rd_prop_ifname(vrrp_vr_conf_t *conf, const char *str)
+{
+	size_t size = sizeof (conf->vvc_link);
+	return (strlcpy(conf->vvc_link, str, size) < size);
+}
+
+static int
+vrrp_wt_prop_name(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%s", conf->vvc_name));
+}
+
+static int
+vrrp_wt_prop_pri(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%d", conf->vvc_pri));
+}
+
+static int
+vrrp_wt_prop_adver_int(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%d", conf->vvc_adver_int));
+}
+
+static int
+vrrp_wt_prop_preempt(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%s",
+	    conf->vvc_preempt ? "true" : "false"));
+}
+
+static int
+vrrp_wt_prop_accept(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%s",
+	    conf->vvc_accept ? "true" : "false"));
+}
+
+static int
+vrrp_wt_prop_enabled(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%s",
+	    conf->vvc_enabled ? "enabled" : "disabled"));
+}
+
+static int
+vrrp_wt_prop_vrid(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%d", conf->vvc_vrid));
+}
+
+static int
+vrrp_wt_prop_af(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%s",
+	    conf->vvc_af == AF_INET ? "AF_INET" : "AF_INET6"));
+}
+
+static int
+vrrp_wt_prop_ifname(vrrp_vr_conf_t *conf, char *str, size_t size)
+{
+	return (snprintf(str, size, "%s", conf->vvc_link));
+}
+
+static char *
+af_str(int af)
+{
+	if (af == 4 || af == AF_INET)
+		return ("AF_INET");
+	else if (af == 6 || af == AF_INET6)
+		return ("AF_INET6");
+	else if (af == AF_UNSPEC)
+		return ("AF_UNSPEC");
+	else
+		return ("AF_error");
+}
+
+static vrrp_err_t
+vrrpd_create_vr(vrrp_vr_conf_t *conf)
+{
+	vrrp_vr_t	*vr;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_create_vr(%s)", conf->vvc_name);
+
+	if ((vr = malloc(sizeof (vrrp_vr_t))) == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_create_vr(): memory allocation for %s"
+		    " failed", conf->vvc_name);
+		return (VRRP_ENOMEM);
+	}
+
+	bzero(vr, sizeof (vrrp_vr_t));
+	vr->vvr_state = VRRP_STATE_NONE;
+	vr->vvr_timer_id = -1;
+	vrrpd_state_trans(VRRP_STATE_NONE, VRRP_STATE_INIT, vr);
+	(void) memcpy(&vr->vvr_conf, conf, sizeof (vrrp_vr_conf_t));
+	vr->vvr_conf.vvc_enabled = _B_FALSE;
+	TAILQ_INSERT_HEAD(&vrrp_vr_list, vr, vvr_next);
+	return (VRRP_SUCCESS);
+}
+
+static void
+vrrpd_delete_vr(vrrp_vr_t *vr)
+{
+	vrrp_log(VRRP_DBG0, "vrrpd_delete_vr(%s)", vr->vvr_conf.vvc_name);
+	if (vr->vvr_conf.vvc_enabled)
+		vrrpd_disable_vr(vr, NULL, _B_FALSE);
+	assert(vr->vvr_state == VRRP_STATE_INIT);
+	vrrpd_state_trans(VRRP_STATE_INIT, VRRP_STATE_NONE, vr);
+	TAILQ_REMOVE(&vrrp_vr_list, vr, vvr_next);
+	(void) free(vr);
+}
+
+static vrrp_err_t
+vrrpd_enable_vr(vrrp_vr_t *vr)
+{
+	vrrp_err_t	rx_err, tx_err, err = VRRP_EINVAL;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_enable_vr(%s)", vr->vvr_conf.vvc_name);
+
+	assert(vr->vvr_conf.vvc_enabled);
+
+	/*
+	 * This VRRP router has been successfully enabled and start
+	 * participating.
+	 */
+	if (vr->vvr_state != VRRP_STATE_INIT)
+		return (VRRP_SUCCESS);
+
+	if ((rx_err = vrrpd_init_rxsock(vr)) == VRRP_SUCCESS) {
+		/*
+		 * Select the primary IP address. Even if this time
+		 * primary IP selection failed, we will reselect the
+		 * primary IP address when new IP address comes up.
+		 */
+		vrrpd_reselect_primary(vr->vvr_pif);
+		if (vr->vvr_pif->vvi_pip == NULL) {
+			vrrp_log(VRRP_DBG0, "vrrpd_enable_vr(%s): "
+			    "select_primary over %s failed",
+			    vr->vvr_conf.vvc_name, vr->vvr_pif->vvi_ifname);
+			rx_err = VRRP_ENOPRIM;
+		}
+	}
+
+	/*
+	 * Initialize the TX socket used for this vrrp_vr_t to send the
+	 * multicast packets.
+	 */
+	tx_err = vrrpd_init_txsock(vr);
+
+	/*
+	 * Only start the state transition if sockets for both RX and TX are
+	 * initialized correctly.
+	 */
+	if (rx_err != VRRP_SUCCESS || tx_err != VRRP_SUCCESS) {
+		/*
+		 * Record the error information for diagnose purpose.
+		 */
+		vr->vvr_err = (rx_err == VRRP_SUCCESS) ? tx_err : rx_err;
+		return (err);
+	}
+
+	if (vr->vvr_conf.vvc_pri == 255)
+		err = vrrpd_state_i2m(vr);
+	else
+		err = vrrpd_state_i2b(vr);
+
+	if (err != VRRP_SUCCESS) {
+		vr->vvr_err = err;
+		vr->vvr_pif->vvi_pip = NULL;
+		vrrpd_fini_txsock(vr);
+		vrrpd_fini_rxsock(vr);
+	}
+	return (err);
+}
+
+/*
+ * Given the removed interface, see whether the given VRRP router would
+ * be affected and stop participating the VRRP protocol.
+ *
+ * If intf is NULL, VR disabling request is coming from the admin.
+ */
+static void
+vrrpd_disable_vr(vrrp_vr_t *vr, vrrp_intf_t *intf, boolean_t primary_addr_gone)
+{
+	vrrp_log(VRRP_DBG0, "vrrpd_disable_vr(%s): %s%s", vr->vvr_conf.vvc_name,
+	    intf == NULL ? "requested by admin" : intf->vvi_ifname,
+	    intf == NULL ? "" : (primary_addr_gone ? "primary address gone" :
+	    "interface deleted"));
+
+	/*
+	 * An interface is deleted, see whether this interface is the
+	 * physical interface or the VNIC of the given VRRP router.
+	 * If so, continue to disable the VRRP router.
+	 */
+	if (!primary_addr_gone && (intf != NULL) && (intf != vr->vvr_pif) &&
+	    (intf != vr->vvr_vif)) {
+		return;
+	}
+
+	/*
+	 * If this is the case that the primary IP address is gone,
+	 * and we failed to reselect another primary IP address,
+	 * continue to disable the VRRP router.
+	 */
+	if (primary_addr_gone && intf != vr->vvr_pif)
+		return;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): disabling",
+	    vr->vvr_conf.vvc_name);
+
+	if (vr->vvr_state == VRRP_STATE_MASTER) {
+		/*
+		 * If this router is disabled by the administrator, send
+		 * the zero-priority advertisement to indicate the Master
+		 * stops participating VRRP.
+		 */
+		if (intf == NULL)
+			(void) vrrpd_send_adv(vr, _B_TRUE);
+
+		vrrpd_state_m2i(vr);
+	} else  if (vr->vvr_state == VRRP_STATE_BACKUP) {
+		vrrpd_state_b2i(vr);
+	}
+
+	/*
+	 * If no primary IP address can be selected, the VRRP router
+	 * stays at the INIT state and will become BACKUP and MASTER when
+	 * a primary IP address is reselected.
+	 */
+	if (primary_addr_gone) {
+		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): primary IP "
+		    "is removed", vr->vvr_conf.vvc_name);
+		vr->vvr_err = VRRP_ENOPRIM;
+	} else if (intf == NULL) {
+		/*
+		 * The VRRP router is disable by the administrator
+		 */
+		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): disabled by admin",
+		    vr->vvr_conf.vvc_name);
+		vr->vvr_err = VRRP_SUCCESS;
+		vrrpd_fini_txsock(vr);
+		vrrpd_fini_rxsock(vr);
+	} else if (intf == vr->vvr_pif) {
+		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): physical interface "
+		    "%s removed", vr->vvr_conf.vvc_name, intf->vvi_ifname);
+		vr->vvr_err = VRRP_ENOPRIM;
+		vrrpd_fini_rxsock(vr);
+	} else if (intf == vr->vvr_vif) {
+		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): VNIC interface %s"
+		    " removed", vr->vvr_conf.vvc_name, intf->vvi_ifname);
+		vr->vvr_err = VRRP_ENOVIRT;
+		vrrpd_fini_txsock(vr);
+	}
+}
+
+vrrp_err_t
+vrrpd_create(vrrp_vr_conf_t *conf, boolean_t updateconf)
+{
+	vrrp_err_t	err = VRRP_SUCCESS;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_create(%s, %s, %d)", conf->vvc_name,
+	    conf->vvc_link, conf->vvc_vrid);
+
+	assert(conf != NULL);
+
+	/*
+	 * Sanity check
+	 */
+	if ((strlen(conf->vvc_name) == 0) ||
+	    (strlen(conf->vvc_link) == 0) ||
+	    (conf->vvc_vrid < VRRP_VRID_MIN ||
+	    conf->vvc_vrid > VRRP_VRID_MAX) ||
+	    (conf->vvc_pri < VRRP_PRI_MIN ||
+	    conf->vvc_pri > VRRP_PRI_OWNER) ||
+	    (conf->vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
+	    conf->vvc_adver_int > VRRP_MAX_ADVER_INT_MAX) ||
+	    (conf->vvc_af != AF_INET && conf->vvc_af != AF_INET6) ||
+	    (conf->vvc_pri == VRRP_PRI_OWNER && !conf->vvc_accept)) {
+		vrrp_log(VRRP_DBG1, "vrrpd_create(%s): invalid argument",
+		    conf->vvc_name);
+		return (VRRP_EINVAL);
+	}
+
+	if (!vrrp_valid_name(conf->vvc_name)) {
+		vrrp_log(VRRP_DBG1, "vrrpd_create(): %s is not a valid router "
+		    "name", conf->vvc_name);
+		return (VRRP_EINVALVRNAME);
+	}
+
+	if (vrrpd_lookup_vr_by_name(conf->vvc_name) != NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_create(): %s already exists",
+		    conf->vvc_name);
+		return (VRRP_EINSTEXIST);
+	}
+
+	if (vrrpd_lookup_vr_by_vrid(conf->vvc_link, conf->vvc_vrid,
+	    conf->vvc_af) != NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_create(): VRID %d/%s over %s "
+		    "already exists", conf->vvc_vrid, af_str(conf->vvc_af),
+		    conf->vvc_link);
+		return (VRRP_EVREXIST);
+	}
+
+	if (updateconf && (err = vrrpd_updateconf(conf,
+	    VRRP_CONF_UPDATE)) != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_create(): failed to update "
+		    "configuration for %s", conf->vvc_name);
+		return (err);
+	}
+
+	err = vrrpd_create_vr(conf);
+	if (err != VRRP_SUCCESS && updateconf)
+		(void) vrrpd_updateconf(conf, VRRP_CONF_DELETE);
+
+	return (err);
+}
+
+static vrrp_err_t
+vrrpd_delete(const char *vn)
+{
+	vrrp_vr_t	*vr;
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_delete(%s)", vn);
+
+	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_delete(): %s not exists", vn);
+		return (VRRP_ENOTFOUND);
+	}
+
+	err = vrrpd_updateconf(&vr->vvr_conf, VRRP_CONF_DELETE);
+	if (err != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_delete(): failed to delete "
+		    "configuration for %s", vr->vvr_conf.vvc_name);
+		return (err);
+	}
+
+	vrrpd_delete_vr(vr);
+	return (VRRP_SUCCESS);
+}
+
+static vrrp_err_t
+vrrpd_enable(const char *vn, boolean_t updateconf)
+{
+	vrrp_vr_t		*vr;
+	vrrp_vr_conf_t		*conf;
+	uint32_t		flags;
+	datalink_class_t	class;
+	vrrp_err_t		err = VRRP_SUCCESS;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_enable(%s)", vn);
+
+	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_enable(): %s does not exist", vn);
+		return (VRRP_ENOTFOUND);
+	}
+
+	/*
+	 * The VR is already enabled.
+	 */
+	conf = &vr->vvr_conf;
+	if (conf->vvc_enabled) {
+		vrrp_log(VRRP_DBG1, "vrrpd_enable(): %s is already "
+		    "enabled", vn);
+		return (VRRP_EALREADY);
+	}
+
+	/*
+	 * Check whether the link exists.
+	 */
+	if ((strlen(conf->vvc_link) == 0) || dladm_name2info(vrrpd_vh->vh_dh,
+	    conf->vvc_link, NULL, &flags, &class, NULL) != DLADM_STATUS_OK ||
+	    !(flags & DLADM_OPT_ACTIVE) || ((class != DATALINK_CLASS_PHYS) &&
+	    (class != DATALINK_CLASS_VLAN) && (class != DATALINK_CLASS_AGGR))) {
+		vrrp_log(VRRP_DBG1, "vrrpd_enable(%s): invalid link %s",
+		    vn, conf->vvc_link);
+		return (VRRP_EINVALLINK);
+	}
+
+	/*
+	 * Get the associated VNIC name by the given interface/vrid/
+	 * address famitly.
+	 */
+	err = vrrp_get_vnicname(vrrpd_vh, conf->vvc_vrid,
+	    conf->vvc_af, conf->vvc_link, NULL, NULL, vr->vvr_vnic,
+	    sizeof (vr->vvr_vnic));
+	if (err != VRRP_SUCCESS) {
+		vrrp_log(VRRP_DBG1, "vrrpd_enable(%s): no VNIC for VRID %d/%s "
+		    "over %s", vn, conf->vvc_vrid, af_str(conf->vvc_af),
+		    conf->vvc_link);
+		err = VRRP_ENOVNIC;
+		goto fail;
+	}
+
+	/*
+	 * Find the right VNIC, primary interface and get the list of the
+	 * protected IP adressses and primary IP address. Note that if
+	 * either interface is NULL (no IP addresses configured over the
+	 * interface), we will still continue and mark this VRRP router
+	 * as "enabled".
+	 */
+	vr->vvr_conf.vvc_enabled = _B_TRUE;
+	if (updateconf && (err = vrrpd_updateconf(&vr->vvr_conf,
+	    VRRP_CONF_UPDATE)) != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_enable(): failed to update "
+		    "configuration for %s", vr->vvr_conf.vvc_name);
+		goto fail;
+	}
+
+	/*
+	 * If vrrpd_setup_vr() fails, it is possible that there is no IP
+	 * addresses over ether the primary interface or the VNIC yet,
+	 * return success in this case, the VRRP router will stay in
+	 * the initialized state and start to work when the IP address is
+	 * configured.
+	 */
+	(void) vrrpd_enable_vr(vr);
+	return (VRRP_SUCCESS);
+
+fail:
+	vr->vvr_conf.vvc_enabled = _B_FALSE;
+	vr->vvr_vnic[0] = '\0';
+	return (err);
+}
+
+static vrrp_err_t
+vrrpd_disable(const char *vn)
+{
+	vrrp_vr_t	*vr;
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_disable(%s)", vn);
+
+	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_disable(): %s does not exist", vn);
+		return (VRRP_ENOTFOUND);
+	}
+
+	/*
+	 * The VR is already disable.
+	 */
+	if (!vr->vvr_conf.vvc_enabled) {
+		vrrp_log(VRRP_DBG1, "vrrpd_disable(): %s was not enabled", vn);
+		return (VRRP_EALREADY);
+	}
+
+	vr->vvr_conf.vvc_enabled = _B_FALSE;
+	err = vrrpd_updateconf(&vr->vvr_conf, VRRP_CONF_UPDATE);
+	if (err != VRRP_SUCCESS) {
+		vr->vvr_conf.vvc_enabled = _B_TRUE;
+		vrrp_log(VRRP_ERR, "vrrpd_disable(): failed to update "
+		    "configuration for %s", vr->vvr_conf.vvc_name);
+		return (err);
+	}
+
+	vrrpd_disable_vr(vr, NULL, _B_FALSE);
+	vr->vvr_vnic[0] = '\0';
+	return (VRRP_SUCCESS);
+}
+
+static vrrp_err_t
+vrrpd_modify(vrrp_vr_conf_t *conf, uint32_t mask)
+{
+	vrrp_vr_t	*vr;
+	vrrp_vr_conf_t	savconf;
+	int		pri;
+	boolean_t	accept, set_accept = _B_FALSE;
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_modify(%s)", conf->vvc_name);
+
+	if (mask == 0)
+		return (VRRP_SUCCESS);
+
+	if ((vr = vrrpd_lookup_vr_by_name(conf->vvc_name)) == NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_modify(): cannot find the given "
+		    "VR instance: %s", conf->vvc_name);
+		return (VRRP_ENOTFOUND);
+	}
+
+	if (mask & VRRP_CONF_INTERVAL) {
+		if (conf->vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
+		    conf->vvc_adver_int > VRRP_MAX_ADVER_INT_MAX) {
+			vrrp_log(VRRP_DBG1, "vrrpd_modify(%s): invalid "
+			    "adver_interval %d", conf->vvc_name,
+			    conf->vvc_adver_int);
+			return (VRRP_EINVAL);
+		}
+	}
+
+	pri = vr->vvr_conf.vvc_pri;
+	if (mask & VRRP_CONF_PRIORITY) {
+		if (conf->vvc_pri < VRRP_PRI_MIN ||
+		    conf->vvc_pri > VRRP_PRI_OWNER) {
+			vrrp_log(VRRP_DBG1, "vrrpd_modify(%s): invalid "
+			    "priority %d", conf->vvc_name, conf->vvc_pri);
+			return (VRRP_EINVAL);
+		}
+		pri = conf->vvc_pri;
+	}
+
+	accept = vr->vvr_conf.vvc_accept;
+	if (mask & VRRP_CONF_ACCEPT)
+		accept = conf->vvc_accept;
+
+	if (pri == VRRP_PRI_OWNER && !accept) {
+		vrrp_log(VRRP_DBG1, "vrrpd_modify(%s): accept mode must be "
+		    "true for VRRP address owner", conf->vvc_name);
+		return (VRRP_EINVAL);
+	}
+
+	if ((mask & VRRP_CONF_ACCEPT) && (vr->vvr_conf.vvc_accept != accept)) {
+		err = vrrpd_set_noaccept(vr, !accept);
+		if (err != VRRP_SUCCESS) {
+			vrrp_log(VRRP_ERR, "vrrpd_modify(%s): access mode "
+			    "updating failed: %s", conf->vvc_name,
+			    vrrp_err2str(err));
+			return (err);
+		}
+		set_accept = _B_TRUE;
+	}
+
+	/*
+	 * Save the current configuration, so it can be restored if the
+	 * following fails.
+	 */
+	(void) memcpy(&savconf, &vr->vvr_conf, sizeof (vrrp_vr_conf_t));
+	if (mask & VRRP_CONF_PREEMPT)
+		vr->vvr_conf.vvc_preempt = conf->vvc_preempt;
+
+	if (mask & VRRP_CONF_ACCEPT)
+		vr->vvr_conf.vvc_accept = accept;
+
+	if (mask & VRRP_CONF_PRIORITY)
+		vr->vvr_conf.vvc_pri = pri;
+
+	if (mask & VRRP_CONF_INTERVAL)
+		vr->vvr_conf.vvc_adver_int = conf->vvc_adver_int;
+
+	err = vrrpd_updateconf(&vr->vvr_conf, VRRP_CONF_UPDATE);
+	if (err != VRRP_SUCCESS) {
+		vrrp_log(VRRP_ERR, "vrrpd_modify(%s): configuration update "
+		    "failed: %s", conf->vvc_name, vrrp_err2str(err));
+		if (set_accept)
+			(void) vrrpd_set_noaccept(vr, accept);
+		(void) memcpy(&vr->vvr_conf, &savconf, sizeof (vrrp_vr_conf_t));
+		return (err);
+	}
+
+	if ((mask & VRRP_CONF_PRIORITY) && (vr->vvr_state == VRRP_STATE_BACKUP))
+		vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
+
+	if ((mask & VRRP_CONF_INTERVAL) && (vr->vvr_state == VRRP_STATE_MASTER))
+		vr->vvr_timeout = conf->vvc_adver_int;
+
+	return (VRRP_SUCCESS);
+}
+
+static void
+vrrpd_list(vrid_t vrid, char *ifname, int af, vrrp_ret_list_t *ret,
+    size_t *sizep)
+{
+	vrrp_vr_t	*vr;
+	char		*p = (char *)ret + sizeof (vrrp_ret_list_t);
+	size_t		size = (*sizep) - sizeof (vrrp_ret_list_t);
+
+	vrrp_log(VRRP_DBG0, "vrrpd_list(%d_%s_%s)", vrid, ifname, af_str(af));
+
+	ret->vrl_cnt = 0;
+	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
+		if (vrid !=  VRRP_VRID_NONE && vr->vvr_conf.vvc_vrid != vrid)
+			continue;
+
+		if (strlen(ifname) != 0 && strcmp(ifname,
+		    vr->vvr_conf.vvc_link) == 0) {
+			continue;
+		}
+
+		if ((af == AF_INET || af == AF_INET6) &&
+		    vr->vvr_conf.vvc_af != af)
+			continue;
+
+		if (size < VRRP_NAME_MAX) {
+			vrrp_log(VRRP_DBG1, "vrrpd_list(): buffer size too "
+			    "small to hold %d router names", ret->vrl_cnt);
+			*sizep = sizeof (vrrp_ret_list_t);
+			ret->vrl_err = VRRP_ETOOSMALL;
+			return;
+		}
+		(void) strlcpy(p, vr->vvr_conf.vvc_name, VRRP_NAME_MAX);
+		p += (strlen(vr->vvr_conf.vvc_name) + 1);
+		ret->vrl_cnt++;
+		size -= VRRP_NAME_MAX;
+	}
+
+	*sizep = sizeof (vrrp_ret_list_t) + ret->vrl_cnt * VRRP_NAME_MAX;
+	vrrp_log(VRRP_DBG1, "vrrpd_list() return %d", ret->vrl_cnt);
+	ret->vrl_err = VRRP_SUCCESS;
+}
+
+static void
+vrrpd_query(const char *vn, vrrp_ret_query_t *ret, size_t *sizep)
+{
+	vrrp_queryinfo_t	*infop;
+	vrrp_vr_t		*vr;
+	vrrp_intf_t		*vif;
+	vrrp_ip_t		*ip;
+	struct timeval		now;
+	uint32_t		vipcnt = 0;
+	size_t			size = *sizep;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_query(%s)", vn);
+
+	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_query(): %s does not exist", vn);
+		*sizep = sizeof (vrrp_ret_query_t);
+		ret->vrq_err = VRRP_ENOTFOUND;
+		return;
+	}
+
+	/*
+	 * Get the virtual IP list if the router is not in the INIT state.
+	 */
+	if (vr->vvr_state != VRRP_STATE_INIT) {
+		vif = vr->vvr_vif;
+		TAILQ_FOREACH(ip, &vif->vvi_iplist, vip_next) {
+			vipcnt++;
+		}
+	}
+
+	*sizep = sizeof (vrrp_ret_query_t);
+	*sizep += (vipcnt == 0) ? 0 : (vipcnt - 1) * sizeof (vrrp_addr_t);
+	if (*sizep > size) {
+		vrrp_log(VRRP_ERR, "vrrpd_query(): not enough space to hold "
+		    "%d virtual IPs", vipcnt);
+		*sizep = sizeof (vrrp_ret_query_t);
+		ret->vrq_err = VRRP_ETOOSMALL;
+		return;
+	}
+
+	(void) gettimeofday(&now, NULL);
+
+	bzero(ret, *sizep);
+	infop = &ret->vrq_qinfo;
+	(void) memcpy(&infop->show_vi,
+	    &(vr->vvr_conf), sizeof (vrrp_vr_conf_t));
+	(void) memcpy(&infop->show_vs,
+	    &(vr->vvr_sinfo), sizeof (vrrp_stateinfo_t));
+	(void) strlcpy(infop->show_va.va_vnic, vr->vvr_vnic, MAXLINKNAMELEN);
+	infop->show_vt.vt_since_last_tran = timeval_to_milli(
+	    timeval_delta(now, vr->vvr_sinfo.vs_st_time));
+
+	if (vr->vvr_state == VRRP_STATE_INIT) {
+		ret->vrq_err = VRRP_SUCCESS;
+		return;
+	}
+
+	vipcnt = 0;
+	TAILQ_FOREACH(ip, &vif->vvi_iplist, vip_next) {
+		(void) memcpy(&infop->show_va.va_vips[vipcnt++],
+		    &ip->vip_addr, sizeof (vrrp_addr_t));
+	}
+	infop->show_va.va_vipcnt = vipcnt;
+
+	(void) memcpy(&infop->show_va.va_primary,
+	    &vr->vvr_pif->vvi_pip->vip_addr, sizeof (vrrp_addr_t));
+
+	(void) memcpy(&infop->show_vp, &(vr->vvr_peer), sizeof (vrrp_peer_t));
+
+	/*
+	 * Check whether there is a peer.
+	 */
+	if (!VRRPADDR_UNSPECIFIED(vr->vvr_conf.vvc_af,
+	    &(vr->vvr_peer.vp_addr))) {
+		infop->show_vt.vt_since_last_adv = timeval_to_milli(
+		    timeval_delta(now, vr->vvr_peer.vp_time));
+	}
+
+	if (vr->vvr_state == VRRP_STATE_BACKUP) {
+		infop->show_vt.vt_master_down_intv =
+		    MASTER_DOWN_INTERVAL_VR(vr);
+	}
+
+	ret->vrq_err = VRRP_SUCCESS;
+}
+
+/*
+ * Build the VRRP packet (not including the IP header). Return the
+ * payload length.
+ *
+ * If zero_pri is set to be B_TRUE, then this is the specical zero-priority
+ * advertisement which is sent by the Master to indicate that it has been
+ * stopped participating in VRRP.
+ */
+static size_t
+vrrpd_build_vrrp(vrrp_vr_t *vr, uchar_t *buf, int buflen, boolean_t zero_pri)
+{
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	vrrp_pkt_t	*vp = (vrrp_pkt_t *)buf;
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	struct in_addr	*a4 = (struct in_addr *)(vp + 1);
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	struct in6_addr *a6 = (struct in6_addr *)(vp + 1);
+	vrrp_intf_t	*vif = vr->vvr_vif;
+	vrrp_ip_t	*vip;
+	int		af = vif->vvi_af;
+	size_t		size = sizeof (vrrp_pkt_t);
+	uint16_t	rsvd_adver_int;
+	int		nip = 0;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_build_vrrp(%s, %s_priority): intv %d",
+	    vr->vvr_conf.vvc_name, zero_pri ? "zero" : "non-zero",
+	    vr->vvr_conf.vvc_adver_int);
+
+	TAILQ_FOREACH(vip, &vif->vvi_iplist, vip_next) {
+		if ((size += ((af == AF_INET) ? sizeof (struct in_addr) :
+		    sizeof (struct in6_addr))) > buflen) {
+			vrrp_log(VRRP_ERR, "vrrpd_build_vrrp(%s): buffer size "
+			    "not big enough %d", vr->vvr_conf.vvc_name, size);
+			return (0);
+		}
+
+		if (af == AF_INET)
+			a4[nip++] = vip->vip_addr.in4.sin_addr;
+		else
+			a6[nip++] = vip->vip_addr.in6.sin6_addr;
+	}
+
+	if (nip == 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_build_vrrp(%s): no virtual IP "
+		    "address", vr->vvr_conf.vvc_name);
+		return (0);
+	}
+
+	vp->vp_vers_type = (VRRP_VERSION << 4) | VRRP_PKT_ADVERT;
+	vp->vp_vrid = vr->vvr_conf.vvc_vrid;
+	vp->vp_prio = zero_pri ? VRRP_PRIO_ZERO : vr->vvr_conf.vvc_pri;
+
+	rsvd_adver_int = MSEC2CENTISEC(vr->vvr_conf.vvc_adver_int) & 0x0fff;
+	vp->vp_rsvd_adver_int = htons(rsvd_adver_int);
+	vp->vp_ipnum = nip;
+
+	/*
+	 * Set the checksum to 0 first, then caculate it.
+	 */
+	vp->vp_chksum = 0;
+	if (af == AF_INET) {
+		vp->vp_chksum = vrrp_cksum4(
+		    &vr->vvr_pif->vvi_pip->vip_addr.in4.sin_addr,
+		    &vrrp_muladdr4.in4.sin_addr, size, vp);
+	} else {
+		vp->vp_chksum = vrrp_cksum6(
+		    &vr->vvr_pif->vvi_pip->vip_addr.in6.sin6_addr,
+		    &vrrp_muladdr6.in6.sin6_addr, size, vp);
+	}
+
+	return (size);
+}
+
+/*
+ * We need to build the IPv4 header on our own.
+ */
+static vrrp_err_t
+vrrpd_send_adv_v4(vrrp_vr_t *vr, uchar_t *buf, size_t len, boolean_t zero_pri)
+{
+	/* LINTED E_BAD_PTR_CAST_ALIGN */
+	struct ip *ip = (struct ip *)buf;
+	size_t plen;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v4(%s)", vr->vvr_conf.vvc_name);
+
+	if ((plen = vrrpd_build_vrrp(vr, buf + sizeof (struct ip),
+	    len - sizeof (struct ip), zero_pri)) == 0) {
+		return (VRRP_ETOOSMALL);
+	}
+
+	ip->ip_hl = sizeof (struct ip) >> 2;
+	ip->ip_v = IPV4_VERSION;
+	ip->ip_tos = 0;
+	plen += sizeof (struct ip);
+	ip->ip_len = htons(plen);
+	ip->ip_off = 0;
+	ip->ip_ttl = VRRP_IP_TTL;
+	ip->ip_p = IPPROTO_VRRP;
+	ip->ip_src = vr->vvr_pif->vvi_pip->vip_addr.in4.sin_addr;
+	ip->ip_dst = vrrp_muladdr4.in4.sin_addr;
+
+	/*
+	 * The kernel will set the IP cksum and the IPv4 identification.
+	 */
+	ip->ip_id = 0;
+	ip->ip_sum = 0;
+
+	if ((len = sendto(vr->vvr_vif->vvi_sockfd, buf, plen, 0,
+	    (const struct sockaddr *)&vrrp_muladdr4,
+	    sizeof (struct sockaddr_in))) != plen) {
+		vrrp_log(VRRP_ERR, "vrrpd_send_adv_v4(): sendto() on "
+		    "(vrid:%d, %s, %s) failed: %s sent:%d expect:%d",
+		    vr->vvr_conf.vvc_vrid, vr->vvr_vif->vvi_ifname,
+		    af_str(vr->vvr_conf.vvc_af), strerror(errno), len, plen);
+		return (VRRP_ESYS);
+	}
+
+	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v4(%s) succeed",
+	    vr->vvr_conf.vvc_name);
+	return (VRRP_SUCCESS);
+}
+
+static vrrp_err_t
+vrrpd_send_adv_v6(vrrp_vr_t *vr, uchar_t *buf, size_t len, boolean_t zero_pri)
+{
+	struct msghdr msg6;
+	size_t hoplimit_space = 0;
+	size_t pktinfo_space = 0;
+	size_t bufspace = 0;
+	struct in6_pktinfo *pktinfop;
+	struct cmsghdr *cmsgp;
+	uchar_t *cmsg_datap;
+	struct iovec iov;
+	size_t plen;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v6(%s)", vr->vvr_conf.vvc_name);
+
+	if ((plen = vrrpd_build_vrrp(vr, buf, len, zero_pri)) == 0)
+		return (VRRP_ETOOSMALL);
+
+	msg6.msg_control = NULL;
+	msg6.msg_controllen = 0;
+
+	hoplimit_space = sizeof (int);
+	bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+	    hoplimit_space + _MAX_ALIGNMENT;
+
+	pktinfo_space = sizeof (struct in6_pktinfo);
+	bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
+	    pktinfo_space + _MAX_ALIGNMENT;
+
+	/*
+	 * We need to temporarily set the msg6.msg_controllen to bufspace
+	 * (we will later trim it to actual length used). This is needed because
+	 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
+	 */
+	bufspace += sizeof (struct cmsghdr);
+	msg6.msg_controllen = bufspace;
+
+	msg6.msg_control = (struct cmsghdr *)malloc(bufspace);
+	if (msg6.msg_control == NULL) {
+		vrrp_log(VRRP_ERR, "vrrpd_send_adv_v6(%s): memory allocation "
+		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
+		return (VRRP_ENOMEM);
+	}
+
+	cmsgp = CMSG_FIRSTHDR(&msg6);
+
+	cmsgp->cmsg_level = IPPROTO_IPV6;
+	cmsgp->cmsg_type = IPV6_HOPLIMIT;
+	cmsg_datap = CMSG_DATA(cmsgp);
+	/* LINTED */
+	*(int *)cmsg_datap = VRRP_IP_TTL;
+	cmsgp->cmsg_len = cmsg_datap + hoplimit_space - (uchar_t *)cmsgp;
+	cmsgp = CMSG_NXTHDR(&msg6, cmsgp);
+
+	cmsgp->cmsg_level = IPPROTO_IPV6;
+	cmsgp->cmsg_type = IPV6_PKTINFO;
+	cmsg_datap = CMSG_DATA(cmsgp);
+
+	/* LINTED */
+	pktinfop = (struct in6_pktinfo *)cmsg_datap;
+	/*
+	 * We don't know if pktinfop->ipi6_addr is aligned properly,
+	 * therefore let's use bcopy, instead of assignment.
+	 */
+	(void) bcopy(&vr->vvr_pif->vvi_pip->vip_addr.in6.sin6_addr,
+	    &pktinfop->ipi6_addr, sizeof (struct in6_addr));
+
+	/*
+	 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
+	 */
+	pktinfop->ipi6_ifindex = vr->vvr_vif->vvi_ifindex;
+	cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
+	cmsgp = CMSG_NXTHDR(&msg6, cmsgp);
+	msg6.msg_controllen = (char *)cmsgp - (char *)msg6.msg_control;
+
+	msg6.msg_name = &vrrp_muladdr6;
+	msg6.msg_namelen = sizeof (struct sockaddr_in6);
+
+	iov.iov_base = buf;
+	iov.iov_len = plen;
+	msg6.msg_iov = &iov;
+	msg6.msg_iovlen = 1;
+
+	if ((len = sendmsg(vr->vvr_vif->vvi_sockfd,
+	    (const struct msghdr *)&msg6, 0)) != plen) {
+		vrrp_log(VRRP_ERR, "vrrpd_send_adv_v6(%s): sendmsg() failed: "
+		    "%s expect %d sent %d", vr->vvr_conf.vvc_name,
+		    strerror(errno), plen, len);
+		(void) free(msg6.msg_control);
+		return (VRRP_ESYS);
+	}
+
+	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v6(%s) succeed",
+	    vr->vvr_conf.vvc_name);
+	(void) free(msg6.msg_control);
+	return (VRRP_SUCCESS);
+}
+
+/*
+ * Send the VRRP advertisement packets.
+ */
+static vrrp_err_t
+vrrpd_send_adv(vrrp_vr_t *vr, boolean_t zero_pri)
+{
+	uint64_t buf[(IP_MAXPACKET + 1)/8];
+
+	vrrp_log(VRRP_DBG1, "vrrpd_send_adv(%s, %s_priority)",
+	    vr->vvr_conf.vvc_name, zero_pri ? "zero" : "non_zero");
+
+	assert(vr->vvr_pif->vvi_pip != NULL);
+
+	if (vr->vvr_pif->vvi_pip == NULL) {
+		vrrp_log(VRRP_DBG0, "vrrpd_send_adv(%s): no primary IP "
+		    "address", vr->vvr_conf.vvc_name);
+		return (VRRP_EINVAL);
+	}
+
+	if (vr->vvr_conf.vvc_af == AF_INET) {
+		return (vrrpd_send_adv_v4(vr, (uchar_t *)buf,
+		    sizeof (buf), zero_pri));
+	} else {
+		return (vrrpd_send_adv_v6(vr, (uchar_t *)buf,
+		    sizeof (buf), zero_pri));
+	}
+}
+
+static void
+vrrpd_process_adv(vrrp_vr_t *vr, vrrp_addr_t *from, vrrp_pkt_t *vp)
+{
+	vrrp_vr_conf_t *conf = &vr->vvr_conf;
+	char		peer[INET6_ADDRSTRLEN];
+	char		local[INET6_ADDRSTRLEN];
+	int		addr_cmp;
+	uint16_t	peer_adver_int;
+
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(vr->vvr_conf.vvc_af, from, peer, INET6_ADDRSTRLEN,
+	    _B_FALSE);
+	vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s) from %s", conf->vvc_name,
+	    peer);
+
+	if (vr->vvr_state <= VRRP_STATE_INIT) {
+		vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): state: %s, not "
+		    "ready", conf->vvc_name, vrrp_state2str(vr->vvr_state));
+		return;
+	}
+
+	peer_adver_int = CENTISEC2MSEC(ntohs(vp->vp_rsvd_adver_int) & 0x0fff);
+
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(vr->vvr_pif->vvi_af, &vr->vvr_pif->vvi_pip->vip_addr,
+	    local, INET6_ADDRSTRLEN, _B_FALSE);
+	vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): local/state/pri"
+	    "(%s/%s/%d) peer/pri/intv(%s/%d/%d)", conf->vvc_name, local,
+	    vrrp_state2str(vr->vvr_state), conf->vvc_pri, peer,
+	    vp->vp_prio, peer_adver_int);
+
+	addr_cmp = ipaddr_cmp(vr->vvr_pif->vvi_af, from,
+	    &vr->vvr_pif->vvi_pip->vip_addr);
+	if (addr_cmp == 0) {
+		vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): local message",
+		    conf->vvc_name);
+		return;
+	} else if (conf->vvc_pri == vp->vp_prio) {
+		vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): peer IP %s is %s"
+		    " than the local IP %s", conf->vvc_name, peer,
+		    addr_cmp > 0 ? "greater" : "less", local);
+	}
+
+	if (conf->vvc_pri == 255) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_adv(%s): virtual address "
+		    "owner received advertisement from %s", conf->vvc_name,
+		    peer);
+		return;
+	}
+
+	(void) gettimeofday(&vr->vvr_peer_time, NULL);
+	(void) memcpy(&vr->vvr_peer_addr, from, sizeof (vrrp_addr_t));
+	vr->vvr_peer_prio = vp->vp_prio;
+	vr->vvr_peer_adver_int = peer_adver_int;
+
+	if (vr->vvr_state == VRRP_STATE_BACKUP) {
+		vr->vvr_master_adver_int = vr->vvr_peer_adver_int;
+		if ((vp->vp_prio == VRRP_PRIO_ZERO) ||
+		    (conf->vvc_preempt == _B_FALSE ||
+		    vp->vp_prio >= conf->vvc_pri)) {
+			(void) iu_cancel_timer(vrrpd_timerq,
+			    vr->vvr_timer_id, NULL);
+			if (vp->vp_prio == VRRP_PRIO_ZERO) {
+				/* the master stops participating in VRRP */
+				vr->vvr_timeout = SKEW_TIME_VR(vr);
+			} else {
+				vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
+			}
+			if ((vr->vvr_timer_id = iu_schedule_timer_ms(
+			    vrrpd_timerq, vr->vvr_timeout, vrrp_b2m_timeout,
+			    vr)) == -1) {
+				vrrp_log(VRRP_ERR, "vrrpd_process_adv(%s): "
+				    "start vrrp_b2m_timeout(%d) failed",
+				    conf->vvc_name, vr->vvr_timeout);
+			} else {
+				vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): "
+				    "start vrrp_b2m_timeout(%d)",
+				    conf->vvc_name, vr->vvr_timeout);
+			}
+		}
+	} else if (vr->vvr_state == VRRP_STATE_MASTER) {
+		if (vp->vp_prio == VRRP_PRIO_ZERO) {
+			(void) vrrpd_send_adv(vr, _B_FALSE);
+			(void) iu_cancel_timer(vrrpd_timerq,
+			    vr->vvr_timer_id, NULL);
+			if ((vr->vvr_timer_id = iu_schedule_timer_ms(
+			    vrrpd_timerq, vr->vvr_timeout, vrrp_adv_timeout,
+			    vr)) == -1) {
+				vrrp_log(VRRP_ERR, "vrrpd_process_adv(%s): "
+				    "start vrrp_adv_timeout(%d) failed",
+				    conf->vvc_name, vr->vvr_timeout);
+			} else {
+				vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): "
+				    "start vrrp_adv_timeout(%d)",
+				    conf->vvc_name, vr->vvr_timeout);
+			}
+		} else if (vp->vp_prio > conf->vvc_pri ||
+		    (vp->vp_prio == conf->vvc_pri && addr_cmp > 0)) {
+			(void) vrrpd_state_m2b(vr);
+		}
+	} else {
+		assert(_B_FALSE);
+	}
+}
+
+static vrrp_err_t
+vrrpd_process_vrrp(vrrp_intf_t *pif, vrrp_pkt_t *vp, size_t len,
+    vrrp_addr_t *from)
+{
+	vrrp_vr_t	*vr;
+	uint8_t		vers_type;
+	uint16_t	saved_cksum, cksum;
+	char		peer[INET6_ADDRSTRLEN];
+
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(pif->vvi_af, from, peer, INET6_ADDRSTRLEN, _B_FALSE);
+	vrrp_log(VRRP_DBG0, "vrrpd_process_vrrp(%s) from %s", pif->vvi_ifname,
+	    peer);
+
+	if (len < sizeof (vrrp_pkt_t)) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s): invalid message "
+		    "length %d", len);
+		return (VRRP_EINVAL);
+	}
+
+	/*
+	 * Verify: VRRP version number and packet type.
+	 */
+	vers_type = ((vp->vp_vers_type & VRRP_VER_MASK) >> 4);
+	if (vers_type != VRRP_VERSION) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s) unsupported "
+		    "version %d", pif->vvi_ifname, vers_type);
+		return (VRRP_EINVAL);
+	}
+
+	if (vp->vp_ipnum == 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s): zero IPvX count",
+		    pif->vvi_ifname);
+		return (VRRP_EINVAL);
+	}
+
+	if (len - sizeof (vrrp_pkt_t) !=
+	    vp->vp_ipnum * (pif->vvi_af == AF_INET ? sizeof (struct in_addr) :
+	    sizeof (struct in6_addr))) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s): invalid IPvX count"
+		    " %d", pif->vvi_ifname, vp->vp_ipnum);
+		return (VRRP_EINVAL);
+	}
+
+	vers_type = (vp->vp_vers_type & VRRP_TYPE_MASK);
+
+	/*
+	 * verify: VRRP checksum. Note that vrrp_cksum returns network byte
+	 * order checksum value;
+	 */
+	saved_cksum = vp->vp_chksum;
+	vp->vp_chksum = 0;
+	if (pif->vvi_af == AF_INET) {
+		cksum = vrrp_cksum4(&from->in4.sin_addr,
+		    &vrrp_muladdr4.in4.sin_addr, len, vp);
+	} else {
+		cksum = vrrp_cksum6(&from->in6.sin6_addr,
+		    &vrrp_muladdr6.in6.sin6_addr, len, vp);
+	}
+
+	if (cksum != saved_cksum) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s) invalid "
+		    "checksum: expected/real(0x%x/0x%x)", pif->vvi_ifname,
+		    cksum, saved_cksum);
+		return (VRRP_EINVAL);
+	}
+
+	if ((vr = vrrpd_lookup_vr_by_vrid(pif->vvi_ifname, vp->vp_vrid,
+	    pif->vvi_af)) != NULL && vers_type == VRRP_PKT_ADVERT) {
+		vrrpd_process_adv(vr, from, vp);
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_process_vrrp(%s) VRID(%d/%s) "
+		    "not configured", pif->vvi_ifname, vp->vp_vrid,
+		    af_str(pif->vvi_af));
+	}
+	return (VRRP_SUCCESS);
+}
+
+/*
+ * IPv4 socket, the IPv4 header is included.
+ */
+static vrrp_err_t
+vrrpd_process_adv_v4(vrrp_intf_t *pif, struct msghdr *msgp, size_t len)
+{
+	char		abuf[INET6_ADDRSTRLEN];
+	struct ip	*ip;
+
+	vrrp_log(VRRP_DBG0, "vrrpd_process_adv_v4(%s, %d)",
+	    pif->vvi_ifname, len);
+
+	ip = (struct ip *)msgp->msg_iov->iov_base;
+
+	/* Sanity check */
+	if (len < sizeof (struct ip) || len < ntohs(ip->ip_len)) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid length "
+		    "%d", pif->vvi_ifname, len);
+		return (VRRP_EINVAL);
+	}
+
+	assert(ip->ip_v == IPV4_VERSION);
+	assert(ip->ip_p == IPPROTO_VRRP);
+	assert(msgp->msg_namelen == sizeof (struct sockaddr_in));
+
+	if (vrrp_muladdr4.in4.sin_addr.s_addr != ip->ip_dst.s_addr) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
+		    "destination %s", pif->vvi_ifname,
+		    inet_ntop(pif->vvi_af, &(ip->ip_dst), abuf, sizeof (abuf)));
+		return (VRRP_EINVAL);
+	}
+
+	if (ip->ip_ttl != VRRP_IP_TTL) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
+		    "ttl %d", pif->vvi_ifname, ip->ip_ttl);
+		return (VRRP_EINVAL);
+	}
+
+	/*
+	 * Note that the ip_len contains only the IP payload length.
+	 */
+	return (vrrpd_process_vrrp(pif,
+	    /* LINTED E_BAD_PTR_CAST_ALIGN */
+	    (vrrp_pkt_t *)((char *)ip + ip->ip_hl * 4), ntohs(ip->ip_len),
+	    (vrrp_addr_t *)msgp->msg_name));
+}
+
+/*
+ * IPv6 socket, check the ancillary_data.
+ */
+static vrrp_err_t
+vrrpd_process_adv_v6(vrrp_intf_t *pif, struct msghdr *msgp, size_t len)
+{
+	struct cmsghdr		*cmsgp;
+	uchar_t			*cmsg_datap;
+	struct in6_pktinfo	*pktinfop;
+	char			abuf[INET6_ADDRSTRLEN];
+	int			ttl;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_process_adv_v6(%s, %d)",
+	    pif->vvi_ifname, len);
+
+	/* Sanity check */
+	if (len < sizeof (vrrp_pkt_t)) {
+		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v6(%s): invalid length "
+		    "%d", pif->vvi_ifname, len);
+		return (VRRP_EINVAL);
+	}
+
+	assert(msgp->msg_namelen == sizeof (struct sockaddr_in6));
+
+	for (cmsgp = CMSG_FIRSTHDR(msgp); cmsgp != NULL;
+	    cmsgp = CMSG_NXTHDR(msgp, cmsgp)) {
+		assert(cmsgp->cmsg_level == IPPROTO_IPV6);
+		cmsg_datap = CMSG_DATA(cmsgp);
+
+		switch (cmsgp->cmsg_type) {
+		case IPV6_HOPLIMIT:
+			/* LINTED E_BAD_PTR_CAST_ALIGN */
+			if ((ttl = *(int *)cmsg_datap) == VRRP_IP_TTL)
+				break;
+
+			vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
+			    "ttl %d", pif->vvi_ifname, ttl);
+			return (VRRP_EINVAL);
+		case IPV6_PKTINFO:
+			/* LINTED E_BAD_PTR_CAST_ALIGN */
+			pktinfop = (struct in6_pktinfo *)cmsg_datap;
+			if (IN6_ARE_ADDR_EQUAL(&pktinfop->ipi6_addr,
+			    &vrrp_muladdr6.in6.sin6_addr)) {
+				break;
+			}
+
+			vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
+			    "destination %s", pif->vvi_ifname,
+			    inet_ntop(pif->vvi_af, &pktinfop->ipi6_addr, abuf,
+			    sizeof (abuf)));
+			return (VRRP_EINVAL);
+		}
+	}
+
+	return (vrrpd_process_vrrp(pif, msgp->msg_iov->iov_base, len,
+	    msgp->msg_name));
+}
+
+/* ARGSUSED */
+static void
+vrrpd_sock_handler(iu_eh_t *eh, int s, short events, iu_event_id_t id,
+    void *arg)
+{
+	struct msghdr		msg;
+	vrrp_addr_t		from;
+	uint64_t		buf[(IP_MAXPACKET + 1)/8];
+	uint64_t		ancillary_data[(IP_MAXPACKET + 1)/8];
+	vrrp_intf_t		*pif = arg;
+	int			af = pif->vvi_af;
+	int			len;
+	struct iovec		iov;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_sock_handler(%s)", pif->vvi_ifname);
+
+	msg.msg_name = (struct sockaddr *)&from;
+	msg.msg_namelen = (af == AF_INET) ? sizeof (struct sockaddr_in) :
+	    sizeof (struct sockaddr_in6);
+	iov.iov_base = (char *)buf;
+	iov.iov_len = sizeof (buf);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = ancillary_data;
+	msg.msg_controllen = sizeof (ancillary_data);
+
+	if ((len = recvmsg(s, &msg, 0)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_sock_handler() recvmsg(%s) "
+		    "failed: %s", pif->vvi_ifname, strerror(errno));
+		return;
+	}
+
+	/*
+	 * Ignore packets whose control buffers that don't fit
+	 */
+	if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+		vrrp_log(VRRP_ERR, "vrrpd_sock_handler() %s buffer not "
+		    "big enough", pif->vvi_ifname);
+		return;
+	}
+
+	if (af == AF_INET)
+		(void) vrrpd_process_adv_v4(pif, &msg, len);
+	else
+		(void) vrrpd_process_adv_v6(pif, &msg, len);
+}
+
+/*
+ * Create the socket which is used to receive VRRP packets. Virtual routers
+ * that configured on the same physical interface share the same socket.
+ */
+static vrrp_err_t
+vrrpd_init_rxsock(vrrp_vr_t *vr)
+{
+	vrrp_intf_t *pif;	/* Physical interface used to recv packets */
+	struct group_req greq;
+	struct sockaddr_storage *muladdr;
+	int af, proto;
+	int on = 1;
+	vrrp_err_t err = VRRP_SUCCESS;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s)", vr->vvr_conf.vvc_name);
+
+	/*
+	 * The RX sockets may already been initialized.
+	 */
+	if ((pif = vr->vvr_pif) != NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s) already done on %s",
+		    vr->vvr_conf.vvc_name, pif->vvi_ifname);
+		assert(pif->vvi_sockfd != -1);
+		return (VRRP_SUCCESS);
+	}
+
+	/*
+	 * If no IP addresses configured on the primary interface,
+	 * return failure.
+	 */
+	af = vr->vvr_conf.vvc_af;
+	pif = vrrpd_lookup_if(vr->vvr_conf.vvc_link, af);
+	if (pif == NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s): no IP address "
+		    "over %s/%s", vr->vvr_conf.vvc_name,
+		    vr->vvr_conf.vvc_link, af_str(af));
+		return (VRRP_ENOPRIM);
+	}
+
+	proto = (af == AF_INET ? IPPROTO_IP : IPPROTO_IPV6);
+	if (pif->vvi_nvr++ == 0) {
+		assert(pif->vvi_sockfd < 0);
+		pif->vvi_sockfd = socket(af, SOCK_RAW, IPPROTO_VRRP);
+		if (pif->vvi_sockfd < 0) {
+			vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): socket() "
+			    "failed %s", vr->vvr_conf.vvc_name,
+			    strerror(errno));
+			err = VRRP_ESYS;
+			goto done;
+		}
+
+		/*
+		 * Join the multicast group to receive VRRP packets.
+		 */
+		if (af == AF_INET) {
+			muladdr = (struct sockaddr_storage *)
+			    (void *)&vrrp_muladdr4;
+		} else {
+			muladdr = (struct sockaddr_storage *)
+			    (void *)&vrrp_muladdr6;
+		}
+
+		greq.gr_interface = pif->vvi_ifindex;
+		(void) memcpy(&greq.gr_group, muladdr,
+		    sizeof (struct sockaddr_storage));
+		if (setsockopt(pif->vvi_sockfd, proto, MCAST_JOIN_GROUP, &greq,
+		    sizeof (struct group_req)) < 0) {
+			vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
+			    "join_group(%d) failed: %s", vr->vvr_conf.vvc_name,
+			    pif->vvi_ifindex, strerror(errno));
+			err = VRRP_ESYS;
+			goto done;
+		} else {
+			vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s): "
+			    "join_group(%d) succeeded", vr->vvr_conf.vvc_name,
+			    pif->vvi_ifindex);
+		}
+
+		/*
+		 * Unlike IPv4, the IPv6 raw socket does not pass the IP header
+		 * when a packet is received. Call setsockopt() to receive such
+		 * information.
+		 */
+		if (af == AF_INET6) {
+			/*
+			 * Enable receipt of destination address info
+			 */
+			if (setsockopt(pif->vvi_sockfd, proto, IPV6_RECVPKTINFO,
+			    (char *)&on, sizeof (on)) < 0) {
+				vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
+				    "enable recvpktinfo failed: %s",
+				    vr->vvr_conf.vvc_name, strerror(errno));
+				err = VRRP_ESYS;
+				goto done;
+			}
+
+			/*
+			 * Enable receipt of hoplimit info
+			 */
+			if (setsockopt(pif->vvi_sockfd, proto,
+			    IPV6_RECVHOPLIMIT, (char *)&on, sizeof (on)) < 0) {
+				vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
+				    "enable recvhoplimit failed: %s",
+				    vr->vvr_conf.vvc_name, strerror(errno));
+				err = VRRP_ESYS;
+				goto done;
+			}
+		}
+
+		if ((pif->vvi_eid = iu_register_event(vrrpd_eh,
+		    pif->vvi_sockfd, POLLIN, vrrpd_sock_handler, pif)) == -1) {
+			vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
+			    "iu_register_event() failed",
+			    vr->vvr_conf.vvc_name);
+			err = VRRP_ESYS;
+			goto done;
+		}
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s) over %s already "
+		    "done %d", vr->vvr_conf.vvc_name, pif->vvi_ifname,
+		    pif->vvi_nvr);
+		assert(IS_PRIMARY_INTF(pif));
+	}
+
+done:
+	vr->vvr_pif = pif;
+	if (err != VRRP_SUCCESS)
+		vrrpd_fini_rxsock(vr);
+
+	return (err);
+}
+
+/*
+ * Delete the socket which is used to receive VRRP packets for the given
+ * VRRP router. Since all virtual routers that configured on the same
+ * physical interface share the same socket, the socket is only closed
+ * when the last VRRP router share this socket is deleted.
+ */
+static void
+vrrpd_fini_rxsock(vrrp_vr_t *vr)
+{
+	vrrp_intf_t	*pif = vr->vvr_pif;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_fini_rxsock(%s)", vr->vvr_conf.vvc_name);
+
+	if (pif == NULL)
+		return;
+
+	if (--pif->vvi_nvr == 0) {
+		vrrp_log(VRRP_DBG1, "vrrpd_fini_rxsock(%s) over %s",
+		    vr->vvr_conf.vvc_name, pif->vvi_ifname);
+		(void) iu_unregister_event(vrrpd_eh, pif->vvi_eid, NULL);
+		(void) close(pif->vvi_sockfd);
+		pif->vvi_pip = NULL;
+		pif->vvi_sockfd = -1;
+		pif->vvi_eid = -1;
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_fini_rxsock(%s) over %s %d",
+		    vr->vvr_conf.vvc_name, pif->vvi_ifname, pif->vvi_nvr);
+	}
+	vr->vvr_pif = NULL;
+}
+
+/*
+ * Create the socket which is used to send VRRP packets. Further, set
+ * the IFF_NOACCEPT flag based on the VRRP router's accept mode.
+ */
+static vrrp_err_t
+vrrpd_init_txsock(vrrp_vr_t *vr)
+{
+	int		af;
+	vrrp_intf_t	*vif;
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_init_txsock(%s)", vr->vvr_conf.vvc_name);
+
+	if (vr->vvr_vif != NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock(%s) already done on %s",
+		    vr->vvr_conf.vvc_name, vr->vvr_vif->vvi_ifname);
+		return (VRRP_SUCCESS);
+	}
+
+	af = vr->vvr_conf.vvc_af;
+	if ((vif = vrrpd_lookup_if(vr->vvr_vnic, af)) == NULL) {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock(%s) no IP address over "
+		    "%s/%s", vr->vvr_conf.vvc_name, vr->vvr_vnic, af_str(af));
+		return (VRRP_ENOVIRT);
+	}
+
+	vr->vvr_vif = vif;
+	if (vr->vvr_conf.vvc_af == AF_INET)
+		err = vrrpd_init_txsock_v4(vr);
+	else
+		err = vrrpd_init_txsock_v6(vr);
+
+	if (err != VRRP_SUCCESS)
+		goto done;
+
+	/*
+	 * The interface should start with IFF_NOACCEPT flag not set, only
+	 * call this function when the VRRP router requires IFF_NOACCEPT.
+	 */
+	if (!vr->vvr_conf.vvc_accept)
+		err = vrrpd_set_noaccept(vr, _B_TRUE);
+
+done:
+	if (err != VRRP_SUCCESS) {
+		(void) close(vif->vvi_sockfd);
+		vif->vvi_sockfd = -1;
+		vr->vvr_vif = NULL;
+	}
+
+	return (err);
+}
+
+/*
+ * Create the IPv4 socket which is used to send VRRP packets. Note that
+ * the destination MAC address of VRRP advertisement must be the virtual
+ * MAC address, so we specify the output interface to be the specific VNIC.
+ */
+static vrrp_err_t
+vrrpd_init_txsock_v4(vrrp_vr_t *vr)
+{
+	vrrp_intf_t *vif;	/* VNIC interface used to send packets */
+	vrrp_ip_t *vip;		/* The first IP over the VNIC */
+	int on = 1;
+	char off = 0;
+	vrrp_err_t err = VRRP_SUCCESS;
+	char abuf[INET6_ADDRSTRLEN];
+
+	vif = vr->vvr_vif;
+	assert(vr->vvr_conf.vvc_af == AF_INET);
+	assert(vif != NULL);
+
+	vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v4(%s) over %s",
+	    vr->vvr_conf.vvc_name, vif->vvi_ifname);
+
+	if (vif->vvi_sockfd != -1) {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v4(%s) already done "
+		    "over %s", vr->vvr_conf.vvc_name, vif->vvi_ifname);
+		return (VRRP_SUCCESS);
+	}
+
+	vif->vvi_sockfd = socket(vif->vvi_af, SOCK_RAW, IPPROTO_VRRP);
+	if (vif->vvi_sockfd < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): socket() "
+		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
+		err = VRRP_ESYS;
+		goto done;
+	}
+
+	/*
+	 * Include the IP header, so that we can specify the IP address/ttl.
+	 */
+	if (setsockopt(vif->vvi_sockfd, IPPROTO_IP, IP_HDRINCL, (char *)&on,
+	    sizeof (on)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): ip_hdrincl "
+		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
+		err = VRRP_ESYS;
+		goto done;
+	}
+
+	/*
+	 * Disable multicast loopback.
+	 */
+	if (setsockopt(vif->vvi_sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &off,
+	    sizeof (char)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): disable "
+		    "multicast_loop failed: %s", vr->vvr_conf.vvc_name,
+		    strerror(errno));
+		err = VRRP_ESYS;
+		goto done;
+	}
+
+	vip = TAILQ_FIRST(&vif->vvi_iplist);
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(vif->vvi_af, &vip->vip_addr, abuf, INET6_ADDRSTRLEN,
+	    _B_FALSE);
+
+	/*
+	 * Set the output interface to send the VRRP packet.
+	 */
+	if (setsockopt(vif->vvi_sockfd, IPPROTO_IP, IP_MULTICAST_IF,
+	    &vip->vip_addr.in4.sin_addr, sizeof (struct in_addr)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): multcast_if(%s) "
+		    "failed: %s", vr->vvr_conf.vvc_name, abuf, strerror(errno));
+		err = VRRP_ESYS;
+	} else {
+		vrrp_log(VRRP_DBG0, "vrrpd_init_txsock_v4(%s): multcast_if(%s) "
+		    "succeed", vr->vvr_conf.vvc_name, abuf);
+	}
+
+done:
+	if (err != VRRP_SUCCESS) {
+		(void) close(vif->vvi_sockfd);
+		vif->vvi_sockfd = -1;
+	}
+
+	return (err);
+}
+
+/*
+ * Create the IPv6 socket which is used to send VRRP packets. Note that
+ * the destination must be the virtual MAC address, so we specify the output
+ * interface to be the specific VNIC.
+ */
+static vrrp_err_t
+vrrpd_init_txsock_v6(vrrp_vr_t *vr)
+{
+	vrrp_intf_t *vif;	/* VNIC interface used to send packets */
+	int off = 0, ttl = VRRP_IP_TTL;
+	vrrp_err_t err = VRRP_SUCCESS;
+
+	vif = vr->vvr_vif;
+	assert(vr->vvr_conf.vvc_af == AF_INET6);
+	assert(vif != NULL);
+
+	vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v6(%s) over %s",
+	    vr->vvr_conf.vvc_name, vif->vvi_ifname);
+
+	if (vif->vvi_sockfd != -1) {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v6(%s) already done "
+		    "over %s", vr->vvr_conf.vvc_name, vif->vvi_ifname);
+		return (VRRP_SUCCESS);
+	}
+
+	vif->vvi_sockfd = socket(vif->vvi_af, SOCK_RAW, IPPROTO_VRRP);
+	if (vif->vvi_sockfd < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): socket() "
+		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
+		err = VRRP_ESYS;
+		goto done;
+	}
+
+	/*
+	 * Disable multicast loopback.
+	 */
+	if (setsockopt(vif->vvi_sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+	    &off, sizeof (int)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): disable "
+		    "multicast_loop failed: %s", vr->vvr_conf.vvc_name,
+		    strerror(errno));
+		err = VRRP_ESYS;
+		goto done;
+	}
+
+	/*
+	 * Set the multicast TTL.
+	 */
+	if (setsockopt(vif->vvi_sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+	    &ttl, sizeof (int)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): enable "
+		    "multicast_hops %d failed: %s", vr->vvr_conf.vvc_name,
+		    ttl, strerror(errno));
+		err = VRRP_ESYS;
+		goto done;
+	}
+
+	/*
+	 * Set the output interface to send the VRRP packet.
+	 */
+	if (setsockopt(vif->vvi_sockfd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+	    &vif->vvi_ifindex, sizeof (uint32_t)) < 0) {
+		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): multicast_if(%d) "
+		    "failed: %s", vr->vvr_conf.vvc_name, vif->vvi_ifindex,
+		    strerror(errno));
+		err = VRRP_ESYS;
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v6(%s): multicast_if(%d)"
+		    " succeed", vr->vvr_conf.vvc_name, vif->vvi_ifindex);
+	}
+
+done:
+	if (err != VRRP_SUCCESS) {
+		(void) close(vif->vvi_sockfd);
+		vif->vvi_sockfd = -1;
+	}
+
+	return (err);
+}
+
+/*
+ * Delete the socket which is used to send VRRP packets. Further, clear
+ * the IFF_NOACCEPT flag based on the VRRP router's accept mode.
+ */
+static void
+vrrpd_fini_txsock(vrrp_vr_t *vr)
+{
+	vrrp_intf_t *vif = vr->vvr_vif;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_fini_txsock(%s)", vr->vvr_conf.vvc_name);
+
+	if (vif != NULL) {
+		if (!vr->vvr_conf.vvc_accept)
+			(void) vrrpd_set_noaccept(vr, _B_FALSE);
+		(void) close(vif->vvi_sockfd);
+		vif->vvi_sockfd = -1;
+		vr->vvr_vif = NULL;
+	}
+}
+
+/*
+ * Given the the pseudo header cksum value (sum), caculate the cksum with
+ * the rest of VRRP packet.
+ */
+static uint16_t
+in_cksum(int sum, uint16_t plen, void *p)
+{
+	int nleft;
+	uint16_t *w;
+	uint16_t answer;
+	uint16_t odd_byte = 0;
+
+	nleft = plen;
+	w = (uint16_t *)p;
+	while (nleft > 1) {
+		sum += *w++;
+		nleft -= 2;
+	}
+
+	/* mop up an odd byte, if necessary */
+	if (nleft == 1) {
+		*(uchar_t *)(&odd_byte) = *(uchar_t *)w;
+		sum += odd_byte;
+	}
+
+	/*
+	 * add back carry outs from top 16 bits to low 16 bits
+	 */
+	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
+	sum += (sum >> 16);			/* add carry */
+	answer = ~sum;				/* truncate to 16 bits */
+	return (answer == 0 ? ~0 : answer);
+}
+
+/* Pseudo header for v4 */
+struct pshv4 {
+	struct in_addr	ph4_src;
+	struct in_addr	ph4_dst;
+	uint8_t		ph4_zero;	/* always zero */
+	uint8_t		ph4_protocol;	/* protocol used, IPPROTO_VRRP */
+	uint16_t	ph4_len;	/* VRRP payload len */
+};
+
+/*
+ * Checksum routine for VRRP checksum. Note that plen is the upper-layer
+ * packet length (in the host byte order), and both IP source and destination
+ * addresses are in the network byte order.
+ */
+static uint16_t
+vrrp_cksum4(struct in_addr *src, struct in_addr *dst, uint16_t plen,
+    vrrp_pkt_t *vp)
+{
+	struct pshv4 ph4;
+	int nleft;
+	uint16_t *w;
+	int sum = 0;
+
+	ph4.ph4_src = *src;
+	ph4.ph4_dst = *dst;
+	ph4.ph4_zero = 0;
+	ph4.ph4_protocol = IPPROTO_VRRP;
+	ph4.ph4_len = htons(plen);
+
+	/*
+	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
+	 *  we add sequential 16 bit words to it, and at the end, fold
+	 *  back all the carry bits from the top 16 bits into the lower
+	 *  16 bits.
+	 */
+	nleft = sizeof (struct pshv4);
+	w = (uint16_t *)&ph4;
+	while (nleft > 0) {
+		sum += *w++;
+		nleft -= 2;
+	}
+
+	return (in_cksum(sum, plen, vp));
+}
+
+/* Pseudo header for v6 */
+struct pshv6 {
+	struct in6_addr	ph6_src;
+	struct in6_addr	ph6_dst;
+	uint32_t	ph6_len;	/* VRRP payload len */
+	uint32_t	ph6_zero : 24,
+			ph6_protocol : 8; /* protocol used, IPPROTO_VRRP */
+};
+
+/*
+ * Checksum routine for VRRP checksum. Note that plen is the upper-layer
+ * packet length (in the host byte order), and both IP source and destination
+ * addresses are in the network byte order.
+ */
+static uint16_t
+vrrp_cksum6(struct in6_addr *src, struct in6_addr *dst, uint16_t plen,
+    vrrp_pkt_t *vp)
+{
+	struct pshv6 ph6;
+	int nleft;
+	uint16_t *w;
+	int sum = 0;
+
+	ph6.ph6_src = *src;
+	ph6.ph6_dst = *dst;
+	ph6.ph6_zero = 0;
+	ph6.ph6_protocol = IPPROTO_VRRP;
+	ph6.ph6_len = htonl((uint32_t)plen);
+
+	/*
+	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
+	 *  we add sequential 16 bit words to it, and at the end, fold
+	 *  back all the carry bits from the top 16 bits into the lower
+	 *  16 bits.
+	 */
+	nleft = sizeof (struct pshv6);
+	w = (uint16_t *)&ph6;
+	while (nleft > 0) {
+		sum += *w++;
+		nleft -= 2;
+	}
+
+	return (in_cksum(sum, plen, vp));
+}
+
+vrrp_err_t
+vrrpd_state_i2m(vrrp_vr_t *vr)
+{
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_state_i2m(%s)", vr->vvr_conf.vvc_name);
+
+	vrrpd_state_trans(VRRP_STATE_INIT, VRRP_STATE_MASTER, vr);
+	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
+		return (err);
+
+	(void) vrrpd_send_adv(vr, _B_FALSE);
+
+	vr->vvr_err = VRRP_SUCCESS;
+	vr->vvr_timeout = vr->vvr_conf.vvc_adver_int;
+	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
+	    vr->vvr_timeout, vrrp_adv_timeout, vr)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_state_i2m(): unable to start timer");
+		return (VRRP_ESYS);
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_state_i2m(%s): start "
+		    "vrrp_adv_timeout(%d)", vr->vvr_conf.vvc_name,
+		    vr->vvr_timeout);
+	}
+	return (VRRP_SUCCESS);
+}
+
+vrrp_err_t
+vrrpd_state_i2b(vrrp_vr_t *vr)
+{
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_state_i2b(%s)", vr->vvr_conf.vvc_name);
+
+	vrrpd_state_trans(VRRP_STATE_INIT, VRRP_STATE_BACKUP, vr);
+	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
+		return (err);
+
+	/*
+	 * Reinitialize the Master advertisement interval to be the configured
+	 * value.
+	 */
+	vr->vvr_err = VRRP_SUCCESS;
+	vr->vvr_master_adver_int = vr->vvr_conf.vvc_adver_int;
+	vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
+	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
+	    vr->vvr_timeout, vrrp_b2m_timeout, vr)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_state_i2b(): unable to set timer");
+		return (VRRP_ESYS);
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_state_i2b(%s): start "
+		    "vrrp_b2m_timeout(%d)", vr->vvr_conf.vvc_name,
+		    vr->vvr_timeout);
+	}
+	return (VRRP_SUCCESS);
+}
+
+void
+vrrpd_state_m2i(vrrp_vr_t *vr)
+{
+	vrrp_log(VRRP_DBG1, "vrrpd_state_m2i(%s)", vr->vvr_conf.vvc_name);
+
+	vrrpd_state_trans(VRRP_STATE_MASTER, VRRP_STATE_INIT, vr);
+	(void) vrrpd_virtualip_update(vr, _B_TRUE);
+	bzero(&vr->vvr_peer, sizeof (vrrp_peer_t));
+	(void) iu_cancel_timer(vrrpd_timerq, vr->vvr_timer_id, NULL);
+}
+
+void
+vrrpd_state_b2i(vrrp_vr_t *vr)
+{
+	vrrp_log(VRRP_DBG1, "vrrpd_state_b2i(%s)", vr->vvr_conf.vvc_name);
+
+	bzero(&vr->vvr_peer, sizeof (vrrp_peer_t));
+	(void) iu_cancel_timer(vrrpd_timerq, vr->vvr_timer_id, NULL);
+	vrrpd_state_trans(VRRP_STATE_BACKUP, VRRP_STATE_INIT, vr);
+	(void) vrrpd_virtualip_update(vr, _B_TRUE);
+}
+
+/* ARGSUSED */
+static void
+vrrp_b2m_timeout(iu_tq_t *tq, void *arg)
+{
+	vrrp_vr_t *vr = (vrrp_vr_t *)arg;
+
+	vrrp_log(VRRP_DBG1, "vrrp_b2m_timeout(%s)", vr->vvr_conf.vvc_name);
+	(void) vrrpd_state_b2m(vr);
+}
+
+/* ARGSUSED */
+static void
+vrrp_adv_timeout(iu_tq_t *tq, void *arg)
+{
+	vrrp_vr_t *vr = (vrrp_vr_t *)arg;
+
+	vrrp_log(VRRP_DBG1, "vrrp_adv_timeout(%s)", vr->vvr_conf.vvc_name);
+
+	(void) vrrpd_send_adv(vr, _B_FALSE);
+	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
+	    vr->vvr_timeout, vrrp_adv_timeout, vr)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrp_adv_timeout(%s): start timer failed",
+		    vr->vvr_conf.vvc_name);
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrp_adv_timeout(%s): start "
+		    "vrrp_adv_timeout(%d)", vr->vvr_conf.vvc_name,
+		    vr->vvr_timeout);
+	}
+}
+
+vrrp_err_t
+vrrpd_state_b2m(vrrp_vr_t *vr)
+{
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_state_b2m(%s)", vr->vvr_conf.vvc_name);
+
+	vrrpd_state_trans(VRRP_STATE_BACKUP, VRRP_STATE_MASTER, vr);
+	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
+		return (err);
+	(void) vrrpd_send_adv(vr, _B_FALSE);
+
+	vr->vvr_timeout = vr->vvr_conf.vvc_adver_int;
+	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
+	    vr->vvr_timeout, vrrp_adv_timeout, vr)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_state_b2m(%s): start timer failed",
+		    vr->vvr_conf.vvc_name);
+		return (VRRP_ESYS);
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_state_b2m(%s): start "
+		    "vrrp_adv_timeout(%d)", vr->vvr_conf.vvc_name,
+		    vr->vvr_timeout);
+	}
+	return (VRRP_SUCCESS);
+}
+
+vrrp_err_t
+vrrpd_state_m2b(vrrp_vr_t *vr)
+{
+	vrrp_err_t	err;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_state_m2b(%s)", vr->vvr_conf.vvc_name);
+
+	vrrpd_state_trans(VRRP_STATE_MASTER, VRRP_STATE_BACKUP, vr);
+	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
+		return (err);
+
+	/*
+	 * Cancel the adver_timer.
+	 */
+	vr->vvr_master_adver_int = vr->vvr_peer_adver_int;
+	(void) iu_cancel_timer(vrrpd_timerq, vr->vvr_timer_id, NULL);
+	vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
+	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
+	    vr->vvr_timeout, vrrp_b2m_timeout, vr)) == -1) {
+		vrrp_log(VRRP_ERR, "vrrpd_state_m2b(%s): start timer failed",
+		    vr->vvr_conf.vvc_name);
+	} else {
+		vrrp_log(VRRP_DBG1, "vrrpd_state_m2b(%s) start "
+		    "vrrp_b2m_timeout(%d)", vr->vvr_conf.vvc_name,
+		    vr->vvr_timeout);
+	}
+	return (VRRP_SUCCESS);
+}
+
+/*
+ * Set the IFF_NOACCESS flag on the VNIC interface of the VRRP router
+ * based on its access mode.
+ */
+static vrrp_err_t
+vrrpd_set_noaccept(vrrp_vr_t *vr, boolean_t on)
+{
+	vrrp_intf_t *vif = vr->vvr_vif;
+	uint64_t curr_flags;
+	struct lifreq lifr;
+	int s;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_set_noaccept(%s, %s)",
+	    vr->vvr_conf.vvc_name, on ? "on" : "off");
+
+	/*
+	 * Possibly no virtual address exists on this VRRP router yet.
+	 */
+	if (vif == NULL)
+		return (VRRP_SUCCESS);
+
+	vrrp_log(VRRP_DBG1, "vrrpd_set_noaccept(%s, %s)",
+	    vif->vvi_ifname, vrrp_state2str(vr->vvr_state));
+
+	s = (vif->vvi_af == AF_INET) ? vrrpd_ctlsock_fd : vrrpd_ctlsock6_fd;
+	(void) strncpy(lifr.lifr_name, vif->vvi_ifname,
+	    sizeof (lifr.lifr_name));
+	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+		if (errno != ENXIO && errno != ENOENT) {
+			vrrp_log(VRRP_ERR, "vrrpd_set_noaccept(): "
+			    "SIOCGLIFFLAGS on %s failed: %s",
+			    vif->vvi_ifname, strerror(errno));
+		}
+		return (VRRP_ESYS);
+	}
+
+	curr_flags = lifr.lifr_flags;
+	if (on)
+		lifr.lifr_flags |= IFF_NOACCEPT;
+	else
+		lifr.lifr_flags &= ~IFF_NOACCEPT;
+
+	if (lifr.lifr_flags != curr_flags) {
+		if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+			if (errno != ENXIO && errno != ENOENT) {
+				vrrp_log(VRRP_ERR, "vrrpd_set_noaccept(%s): "
+				    "SIOCSLIFFLAGS 0x%llx on %s failed: %s",
+				    on ? "no_accept" : "accept",
+				    lifr.lifr_flags, vif->vvi_ifname,
+				    strerror(errno));
+			}
+			return (VRRP_ESYS);
+		}
+	}
+	return (VRRP_SUCCESS);
+}
+
+static vrrp_err_t
+vrrpd_virtualip_updateone(vrrp_intf_t *vif, vrrp_ip_t *ip, boolean_t checkonly)
+{
+	vrrp_state_t	state = vif->vvi_vr_state;
+	struct lifreq	lifr;
+	char		abuf[INET6_ADDRSTRLEN];
+	int		af = vif->vvi_af;
+	uint64_t	curr_flags;
+	int		s;
+
+	assert(IS_VIRTUAL_INTF(vif));
+
+	/* LINTED E_CONSTANT_CONDITION */
+	VRRPADDR2STR(af, &ip->vip_addr, abuf, INET6_ADDRSTRLEN, _B_FALSE);
+	vrrp_log(VRRP_DBG1, "vrrpd_virtualip_updateone(%s, %s%s)",
+	    vif->vvi_ifname, abuf, checkonly ? ", checkonly" : "");
+
+	s = (af == AF_INET) ? vrrpd_ctlsock_fd : vrrpd_ctlsock6_fd;
+	(void) strncpy(lifr.lifr_name, ip->vip_lifname,
+	    sizeof (lifr.lifr_name));
+	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+		if (errno != ENXIO && errno != ENOENT) {
+			vrrp_log(VRRP_ERR, "vrrpd_virtualip_updateone(%s): "
+			    "SIOCGLIFFLAGS on %s/%s failed: %s",
+			    vif->vvi_ifname, lifr.lifr_name, abuf,
+			    strerror(errno));
+		}
+		return (VRRP_ESYS);
+	}
+
+	curr_flags = lifr.lifr_flags;
+	if (state == VRRP_STATE_MASTER)
+		lifr.lifr_flags |= IFF_UP;
+	else
+		lifr.lifr_flags &= ~IFF_UP;
+
+	if (lifr.lifr_flags == curr_flags)
+		return (VRRP_SUCCESS);
+
+	if (checkonly) {
+		vrrp_log(VRRP_ERR, "VRRP virtual IP %s/%s was brought %s",
+		    ip->vip_lifname, abuf,
+		    state == VRRP_STATE_MASTER ? "down" : "up");
+		return (VRRP_ESYS);
+	} else if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+		if (errno != ENXIO && errno != ENOENT) {
+			vrrp_log(VRRP_ERR, "vrrpd_virtualip_updateone(%s, %s): "
+			    "bring %s %s/%s failed: %s",
+			    vif->vvi_ifname, vrrp_state2str(state),
+			    state == VRRP_STATE_MASTER ? "up" : "down",
+			    ip->vip_lifname, abuf, strerror(errno));
+		}
+		return (VRRP_ESYS);
+	}
+	return (VRRP_SUCCESS);
+}
+
+static vrrp_err_t
+vrrpd_virtualip_update(vrrp_vr_t *vr, boolean_t checkonly)
+{
+	vrrp_state_t		state;
+	vrrp_intf_t		*vif = vr->vvr_vif;
+	vrrp_ip_t		*ip, *nextip;
+	char			abuf[INET6_ADDRSTRLEN];
+	vrrp_err_t		err;
+
+	vrrp_log(VRRP_DBG1, "vrrpd_virtualip_update(%s, %s, %s)%s",
+	    vr->vvr_conf.vvc_name, vrrp_state2str(vr->vvr_state),
+	    vif->vvi_ifname, checkonly ? " checkonly" : "");
+
+	state = vr->vvr_state;
+	assert(vif != NULL);
+	assert(IS_VIRTUAL_INTF(vif));
+	assert(vif->vvi_vr_state != state);
+	vif->vvi_vr_state = state;
+	for (ip = TAILQ_FIRST(&vif->vvi_iplist); ip != NULL; ip = nextip) {
+		nextip = TAILQ_NEXT(ip, vip_next);
+		err = vrrpd_virtualip_updateone(vif, ip, _B_FALSE);
+		if (!checkonly && err != VRRP_SUCCESS) {
+			/* LINTED E_CONSTANT_CONDITION */
+			VRRPADDR2STR(vif->vvi_af, &ip->vip_addr, abuf,
+			    INET6_ADDRSTRLEN, _B_FALSE);
+			vrrp_log(VRRP_DBG1, "vrrpd_virtualip_update() update "
+			    "%s over %s failed", abuf, vif->vvi_ifname);
+			vrrpd_delete_ip(vif, ip);
+		}
+	}
+
+	/*
+	 * The IP address is deleted when it is failed to be brought
+	 * up. If no IP addresses are left, delete this interface.
+	 */
+	if (!checkonly && TAILQ_EMPTY(&vif->vvi_iplist)) {
+		vrrp_log(VRRP_DBG0, "vrrpd_virtualip_update(): "
+		    "no IP left over %s", vif->vvi_ifname);
+		vrrpd_delete_if(vif, _B_TRUE);
+		return (VRRP_ENOVIRT);
+	}
+	return (VRRP_SUCCESS);
+}
+
+void
+vrrpd_state_trans(vrrp_state_t prev_s, vrrp_state_t s, vrrp_vr_t *vr)
+{
+	vrrp_log(VRRP_DBG1, "vrrpd_state_trans(%s): %s --> %s",
+	    vr->vvr_conf.vvc_name, vrrp_state2str(prev_s), vrrp_state2str(s));
+
+	assert(vr->vvr_state == prev_s);
+	vr->vvr_state = s;
+	vr->vvr_prev_state = prev_s;
+	(void) gettimeofday(&vr->vvr_st_time, NULL);
+	(void) vrrpd_post_event(vr->vvr_conf.vvc_name, prev_s, s);
+}
+
+static int
+vrrpd_post_event(const char *name, vrrp_state_t prev_st, vrrp_state_t st)
+{
+	sysevent_id_t	eid;
+	nvlist_t	*nvl = NULL;
+
+	/*
+	 * sysevent is not supported in the non-global zone
+	 */
+	if (getzoneid() != GLOBAL_ZONEID)
+		return (0);
+
+	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+		goto failed;
+
+	if (nvlist_add_uint8(nvl, VRRP_EVENT_VERSION,
+	    VRRP_EVENT_CUR_VERSION) != 0)
+		goto failed;
+
+	if (nvlist_add_string(nvl, VRRP_EVENT_ROUTER_NAME, name) != 0)
+		goto failed;
+
+	if (nvlist_add_uint8(nvl, VRRP_EVENT_STATE, st) != 0)
+		goto failed;
+
+	if (nvlist_add_uint8(nvl, VRRP_EVENT_PREV_STATE, prev_st) != 0)
+		goto failed;
+
+	if (sysevent_post_event(EC_VRRP, ESC_VRRP_STATE_CHANGE,
+	    SUNW_VENDOR, VRRP_EVENT_PUBLISHER, nvl, &eid) == 0) {
+		nvlist_free(nvl);
+		return (0);
+	}
+
+failed:
+	vrrp_log(VRRP_ERR, "vrrpd_post_event(): `state change (%s --> %s)' "
+	    "sysevent posting failed: %s", vrrp_state2str(prev_st),
+	    vrrp_state2str(st), strerror(errno));
+
+	if (nvl != NULL)
+		nvlist_free(nvl);
+	return (-1);
+}
+
+/*
+ * timeval processing functions
+ */
+static int
+timeval_to_milli(struct timeval tv)
+{
+	return ((int)(tv.tv_sec * 1000 + tv.tv_usec / 1000 + 0.5));
+}
+
+static struct timeval
+timeval_delta(struct timeval t1, struct timeval t2)
+{
+	struct timeval t;
+	t.tv_sec = t1.tv_sec - t2.tv_sec;
+	t.tv_usec = t1.tv_usec - t2.tv_usec;
+
+	if (t.tv_usec < 0) {
+		t.tv_usec += 1000000;
+		t.tv_sec--;
+	}
+	return (t);
+}
+
+/*
+ * print error messages to the terminal or to syslog
+ */
+static void
+vrrp_log(int level, char *message, ...)
+{
+	va_list ap;
+	int log_level = -1;
+
+	va_start(ap, message);
+
+	if (vrrp_logflag == 0) {
+		if (level <= vrrp_debug_level) {
+			/*
+			 * VRRP_ERR goes to stderr, others go to stdout
+			 */
+			FILE *out = (level <= VRRP_ERR) ? stderr : stdout;
+			/* LINTED: E_SEC_PRINTF_VAR_FMT */
+			(void) vfprintf(out, message, ap);
+			(void) fprintf(out, "\n");
+			(void) fflush(out);
+		}
+		va_end(ap);
+		return;
+	}
+
+	/*
+	 * translate VRRP_* to LOG_*
+	 */
+	switch (level) {
+	case VRRP_ERR:
+		log_level = LOG_ERR;
+		break;
+	case VRRP_WARNING:
+		log_level = LOG_WARNING;
+		break;
+	case VRRP_NOTICE:
+		log_level = LOG_NOTICE;
+		break;
+	case VRRP_DBG0:
+		log_level = LOG_INFO;
+		break;
+	default:
+		log_level = LOG_DEBUG;
+		break;
+	}
+
+	/* LINTED: E_SEC_PRINTF_VAR_FMT */
+	(void) vsyslog(log_level, message, ap);
+	va_end(ap);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd.xcl	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,162 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+msgid	"IP"
+msgid	"d:f:"
+msgid	"inet"
+msgid	"inet6"
+msgid	"SO_VRRP"
+msgid	"/etc/inet/vrrp.conf"
+msgid	"VRID"
+msgid	"VRRP"
+msgid	"IPvX"
+msgid	"VNIC"
+msgid	"AF_INET"
+msgid	"AF_INET6"
+msgid	"AF_UNSPEC"
+msgid	"AF_error"
+msgid	"RTM_NEWADDR"
+msgid	"RTM_DELADDR"
+msgid	"RTM_IFINFO"
+msgid	"RTM_ADD"
+msgid	"RTM_DELETE"
+msgid	"RTM_CHANGE"
+msgid	"RTM_OLDADD"
+msgid	"RTM_OLDDEL"
+msgid	"RTM_CHGADDR"
+msgid	"RTM_FREEADDR"
+msgid	"RTM_OTHER"
+msgid	"IFF_UP"
+msgid	"inet"
+msgid	"inet6"
+msgid	"vrrpd_create_if"
+msgid	"vrrpd_delete_if"
+msgid	"vrrpd_create_ip"
+msgid	"vrrpd_delete_ip"
+msgid	"main"
+msgid	"daemon_init"
+msgid	"setrlimit"
+msgid	"vrrpd_init"
+msgid	"iu_handle_events"
+msgid	"smf(5)"
+msgid	"vrrpd_init"
+msgid	"dladm_open"
+msgid	"iu_tq_create"
+msgid	"iu_eh_create"
+msgid	"vrrpd_cmdsock_create"
+msgid	"vrrpd_ctlsock_create"
+msgid	"vrrpd_rtsock_create"
+msgid	"vrrp"
+msgid	"bind"
+msgid	"listen"
+msgid	"iu_register_event"
+msgid	"vrrpd_cmdsock_destroy"
+msgid	"socket"
+msgid	"fcntl"
+msgid	"F_GETTL"
+msgid	"F_SETTL"
+msgid	"rtsock"
+msgid	"vrrpd_rtsock_destroy"
+msgid	"vrrpd_audit"
+msgid	"adt_start_session"
+msgid	"adt_alloc_event"
+msgid	"adt_put_event"
+msgid	"vrrp_auth_check"
+msgid	"getpeerucred"
+msgid	"vrrpd_cmdsock_handler"
+msgid	"accept"
+msgid	"vrrpd_rtsock_handler"
+msgid	"vrrpd_scan_timer
+msgid	"vrrpd_walk_ipaddr"
+msgid	"icfg_open"
+msgid	"icfg_get_flags"
+msgid	"icfg_get_index"
+msgid	"icfg_get_addr"
+msgid	"vrrpd_add_ipaddr"
+msgid	"ifindex"
+msgid	"vrrpd_update_ipcache"
+msgid	"vrrpd_reselect_primary"
+msgid	"vrrpd_select_primary"
+msgid	"vrrpd_restart_all_vr"
+msgid	"vrrpd_remove_if"
+msgid	"vrrpd_updateconf"
+msgid	"vrrpd_write_vrconf"
+msgid	"vrrpd_read_vrconf"
+msgid	"vrrpd_readprop"
+msgid	"join_group"
+msgid	"recvpkginfo"
+msgid	"recvhoplimit"
+msgid	"vrrpd_fini_rxsock"
+msgid	"vrrpd_init_txsock"
+msgid	"vrrpd_init_txsock_v4"
+msgid	"ip_hdrincl"
+msgid	"multicast_loop"
+msgid	"multicast_if"
+msgid	"multicast_hops"
+msgid	"vrrpd_init_txsock_v6"
+msgid	"vrrpd_fini_txsock"
+msgid	"vrrpd_state_i2m"
+msgid	"vrrpd_state_i2b"
+msgid	"vrrpd_state_m2i"
+msgid	"vrrpd_state_b2i"
+msgid	"vrrpd_state_b2m"
+msgid	"vrrpd_state_m2b"
+msgid	"vrrp_adv_timeout"
+msgid	"vrrpd_set_noaccept"
+msgid	"SIOCSLIFFLAGS"
+msgid	"vrrpd_virutalip_updateone"
+msgid	"SIOCGLIFFLAGS"
+msgid	"vrrpd_virtualip_update"
+msgid	"vrrpd_fini"
+msgid	"vrrpd_cleanup"
+msgid	"vrrpd_initconf"
+msgid	"vrrpd_scan"
+msgid	"vrrpd_init_ipcache"
+msgid	"vrrpd_create_vr"
+msgid	"vrrpd_delete_vr"
+msgid	"vrrpd_enable_vr"
+msgid	"vrrpd_disable_vr"
+msgid	"vrrpd_create"
+msgid	"vrrpd_delete"
+msgid	"vrrpd_enable"
+msgid	"vrrpd_disable"
+msgid	"vrrpd_modify"
+msgid	"adv_interval"
+msgid	"vrrpd_list"
+msgid	"vrrpd_query"
+msgid	"vrrpd_build_vrrp"
+msgid	"vrrpd_send_adv_v4"
+msgid	"vrrpd_send_adv_v6"
+msgid	"sendmsg"
+msgid	"vrrpd_send_adv"
+msgid	"vrrpd_process_adv"
+msgid	"vrrp_b2m_timeout"
+msgid	"vrrp_adv_timeout"
+msgid	"vrrpd_process_vrrp"
+msgid	"vrrpd_process_adv_v4"
+msgid	"ttl"
+msgid	"vrrpd_process_adv_v6"
+msgid	"vrrpd_sock_handler"
+msgid	"vrrpd_init_rxsock"
+msgid	"vrrpd_state_trans"
+msgid	"sysevent"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd_impl.h	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,178 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_VRRPD_IMPL_H
+#define	_VRRPD_IMPL_H
+
+#include <sys/queue.h>
+#include <libinetutil.h>
+#include <libvrrpadm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Internal data structs to store VRRP instance configuration information
+ * and run-time state information.
+ */
+typedef useconds_t	vrrp_timeout_t;
+
+typedef struct vrrp_vr_s {
+	vrrp_vr_conf_t		vvr_conf;
+	uint32_t		vvr_master_adver_int;
+	char			vvr_vnic[MAXLINKNAMELEN];
+	struct vrrp_intf_s	*vvr_pif;
+	struct vrrp_intf_s	*vvr_vif;
+
+	/*
+	 * Timer reused in master/backup state:
+	 *   Master: The Advertisement_Interval (Adver_Timer)
+	 *   Backup: The Master_Down_Intervel (Master_Down_timer)
+	 */
+	vrrp_timeout_t		vvr_timeout;
+	iu_timer_id_t		vvr_timer_id;
+
+	/*
+	 * Peer information, got from the last adv message received
+	 */
+	vrrp_peer_t		vvr_peer;
+#define	vvr_peer_addr		vvr_peer.vp_addr
+#define	vvr_peer_time		vvr_peer.vp_time
+#define	vvr_peer_prio		vvr_peer.vp_prio
+#define	vvr_peer_adver_int	vvr_peer.vp_adver_int
+
+	vrrp_stateinfo_t	vvr_sinfo;
+#define	vvr_state		vvr_sinfo.vs_state
+#define	vvr_prev_state		vvr_sinfo.vs_prev_state
+#define	vvr_st_time		vvr_sinfo.vs_st_time
+
+	/*
+	 * Record the reason why the virtual router stays at the INIT
+	 * state, for the diagnose purpose.
+	 */
+	vrrp_err_t		vvr_err;
+	TAILQ_ENTRY(vrrp_vr_s)	vvr_next;
+} vrrp_vr_t;
+
+/* IP address/interface cache state flags */
+typedef enum {
+	NODE_STATE_NONE		= 0,
+	NODE_STATE_STALE	= 1,
+	NODE_STATE_NEW		= 2
+} node_state_t;
+
+/*
+ * The ifindex is get by the SIOCGLIFINDEX ioctl, easy to make it part of
+ * vrrp_ip_t instead of vrrp_intf_t
+ */
+typedef struct vrrp_ip_s {
+	char			vip_lifname[LIFNAMSIZ];
+	vrrp_addr_t		vip_addr;
+	uint64_t		vip_flags;
+	node_state_t		vip_state;
+	TAILQ_ENTRY(vrrp_ip_s)	vip_next;
+} vrrp_ip_t;
+
+/*
+ * Used for primary interfaces
+ */
+typedef struct vrrp_primary_ifinfo {
+	uint32_t		vpii_nvr;	/* numbers of virtual routers */
+	vrrp_ip_t		*vpii_pip;	/* primary IP address */
+	iu_event_id_t		vpii_eid;	/* event id of RX socket */
+						/* non-zero on the primary if */
+} vrrp_primary_ifinfo_t;
+
+/*
+ * Used for virtual interfaces
+ */
+typedef struct vrrp_virtual_ifinfo {
+	/*
+	 * the state of the VRRP router, used to determine the up/down
+	 * state of the virtual IP addresses
+	 */
+	vrrp_state_t	vvii_state;
+} vrrp_virtual_ifinfo_t;
+
+/*
+ * VRRP interface structure
+ *
+ * An interface is either the primary interface which owns the primary IP
+ * address or a VNIC interface which owns the virtual IP addresses.
+ * As the primary interface, it can be shared by several VRRP routers.
+ */
+typedef struct vrrp_intf_s {
+	char			vvi_ifname[LIFNAMSIZ];
+	int			vvi_af;		/* address family */
+	node_state_t		vvi_state;
+	uint32_t		vvi_ifindex;	/* interface index */
+	TAILQ_HEAD(, vrrp_ip_s)	vvi_iplist;	/* IP adddress list */
+	TAILQ_ENTRY(vrrp_intf_s) vvi_next;
+
+	/*
+	 * Socket fd.
+	 * - physical interfaces: used to receive the VRRP packet, and shared
+	 *   by all virtual routers on this physical interface.
+	 * - vnic interfaces: used to send the VRRP packet.
+	 */
+	int			vvi_sockfd;
+
+	vrrp_primary_ifinfo_t	pifinfo;	/* Primary interface info */
+	vrrp_virtual_ifinfo_t	vifinfo;	/* VNIC interface info */
+#define	vvi_nvr		pifinfo.vpii_nvr
+#define	vvi_pip		pifinfo.vpii_pip
+#define	vvi_eid		pifinfo.vpii_eid
+#define	vvi_vr_state	vifinfo.vvii_state
+} vrrp_intf_t;
+
+#define	IS_PRIMARY_INTF(intf) \
+	(((intf)->vvi_sockfd >= 0) && ((intf)->vvi_eid != -1))
+
+#define	IS_VIRTUAL_INTF(intf) \
+	(((intf)->vvi_sockfd >= 0) && ((intf)->vvi_eid == -1))
+
+#define	VRRP_ERR	0	/* error message */
+#define	VRRP_WARNING	1
+#define	VRRP_NOTICE	2
+#define	VRRP_INFO	3
+#define	VRRP_DBG0	4	/* debug message, only function calls */
+#define	VRRP_DBG1	5	/* detailed debug message */
+
+/*
+ * The primary IP address must be brought up; further, in the case of IPv6,
+ * the link-local IP address is used as the primary IP address.
+ */
+#define	QUALIFY_PRIMARY_ADDR(intf, ip)					\
+	(((ip)->vip_flags & IFF_UP) && ((intf)->vvi_af != AF_INET6 ||	\
+	IN6_IS_ADDR_LINKLOCAL(&(ip)->vip_addr.in6.sin6_addr)))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _VRRPD_IMPL_H */
--- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c	Tue Nov 17 09:17:48 2009 -0800
@@ -68,7 +68,9 @@
 	{ IFF_FIXEDMTU,		"FIXEDMTU" },
 	{ IFF_VIRTUAL,		"VIRTUAL" },
 	{ IFF_DUPLICATE,	"DUPLICATE" },
-	{ IFF_IPMP,		"IPMP"}
+	{ IFF_IPMP,		"IPMP"},
+	{ IFF_VRRP,		"VRRP"},
+	{ IFF_NOACCEPT,		"NOACCEPT"}
 };
 
 typedef struct {
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/table.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/table.c	Tue Nov 17 09:17:48 2009 -0800
@@ -687,8 +687,8 @@
 		"RTM_NEWADDR",
 		"RTM_DELADDR",
 		"RTM_IFINFO",
-		"RTM_NEWMADDR",
-		"RTM_DELMADDR"
+		"RTM_CHGMADDR",
+		"RTM_FREEMADDR"
 	};
 #define	NEW_RTM_PAT	"RTM type %#x"
 	static char name0[sizeof (NEW_RTM_PAT) + 2];
@@ -716,6 +716,8 @@
 	switch (rtm->rtm_type) {
 	case RTM_NEWADDR:
 	case RTM_DELADDR:
+	case RTM_FREEADDR:
+	case RTM_CHGADDR:
 		mtype = "ifam";
 		break;
 	case RTM_IFINFO:
@@ -737,6 +739,8 @@
 	switch (rtm->rtm_type) {
 	case RTM_NEWADDR:
 	case RTM_DELADDR:
+	case RTM_CHGADDR:
+	case RTM_FREEADDR:
 		ifam = (struct ifa_msghdr *)rtm;
 		trace_misc("ifam: msglen %d version %d type %d addrs %X",
 		    ifam->ifam_msglen, ifam->ifam_version, ifam->ifam_type,
@@ -1638,6 +1642,9 @@
 			    IFF_UP) != 0)
 				ifscan_timer.tv_sec = now.tv_sec;
 			continue;
+		} else if (m.r.rtm.rtm_type == RTM_CHGADDR ||
+		    m.r.rtm.rtm_type == RTM_FREEADDR) {
+			continue;
 		} else {
 			if (m.r.rtm.rtm_index != 0)
 				ifp = ifwithindex(m.r.rtm.rtm_index, 1);
--- a/usr/src/cmd/cmd-inet/usr.sbin/route.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/cmd-inet/usr.sbin/route.c	Tue Nov 17 09:17:48 2009 -0800
@@ -2551,9 +2551,11 @@
 	"RTM_OLDADD: caused by SIOCADDRT",
 	"RTM_OLDDEL: caused by SIOCDELRT",
 	"RTM_RESOLVE: Route created by cloning",
-	"RTM_NEWADDR: address being added to iface",
-	"RTM_DELADDR: address being removed from iface",
+	"RTM_NEWADDR: address being brought up on iface",
+	"RTM_DELADDR: address being brought down on iface",
 	"RTM_IFINFO: iface status change",
+	"RTM_CHGADDR: address being changed on iface",
+	"RTM_FREEADDR: address being removed from iface",
 	0,
 };
 
@@ -2618,6 +2620,8 @@
 		break;
 	case RTM_NEWADDR:
 	case RTM_DELADDR:
+	case RTM_CHGADDR:
+	case RTM_FREEADDR:
 		ifam = (struct ifa_msghdr *)rtm;
 		(void) printf("metric %d, flags:", ifam->ifam_metric);
 		bprintf(stdout, ifam->ifam_flags, routeflags);
--- a/usr/src/cmd/dladm/dladm.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/dladm/dladm.c	Tue Nov 17 09:17:48 2009 -0800
@@ -60,6 +60,7 @@
 #include <libdlsim.h>
 #include <libdlbridge.h>
 #include <libinetutil.h>
+#include <libvrrpadm.h>
 #include <bsm/adt.h>
 #include <bsm/adt_event.h>
 #include <libdlvnic.h>
@@ -317,9 +318,9 @@
 	{ "show-linkmap",	do_show_linkmap,	NULL		},
 	{ "create-vnic",	do_create_vnic,
 	    "    create-vnic      [-t] -l <link> [-m <value> | auto |\n"
-	    "\t\t     {factory [-n <slot-id>]} | {random [-r <prefix>]}]\n"
-	    "\t\t     [-v <vid> [-f]] [-p <prop>=<value>[,...]] [-H] "
-	    "<vnic-link>"						},
+	    "\t\t     {factory [-n <slot-id>]} | {random [-r <prefix>]} |\n"
+	    "\t\t     {vrrp -V <vrid> -A {inet | inet6}} [-v <vid> [-f]]\n"
+	    "\t\t     [-H] [-p <prop>=<value>[,...]] <vnic-link>"	},
 	{ "delete-vnic",	do_delete_vnic,
 	    "    delete-vnic      [-t] <vnic-link>"			},
 	{ "show-vnic",		do_show_vnic,
@@ -477,6 +478,8 @@
 	{"bw-limit",	required_argument,	0, 'b'	},
 	{"slot",	required_argument,	0, 'n'	},
 	{"mac-prefix",	required_argument,	0, 'r'	},
+	{"vrid",	required_argument,	0, 'V'	},
+	{"address-family",	required_argument,	0, 'A'	},
 	{ 0, 0, 0, 0 }
 };
 
@@ -4431,17 +4434,20 @@
 	int			option;
 	char			*endp = NULL;
 	dladm_status_t		status;
-	vnic_mac_addr_type_t	mac_addr_type = VNIC_MAC_ADDR_TYPE_AUTO;
-	uchar_t			*mac_addr;
-	int			mac_slot = -1, maclen = 0, mac_prefix_len = 0;
+	vnic_mac_addr_type_t	mac_addr_type = VNIC_MAC_ADDR_TYPE_UNKNOWN;
+	uchar_t			*mac_addr = NULL;
+	int			mac_slot = -1;
+	uint_t			maclen = 0, mac_prefix_len = 0;
 	char			propstr[DLADM_STRSIZE];
 	dladm_arg_list_t	*proplist = NULL;
 	int			vid = 0;
+	int			af = AF_UNSPEC;
+	vrid_t			vrid = VRRP_VRID_NONE;
 
 	opterr = 0;
 	bzero(propstr, DLADM_STRSIZE);
 
-	while ((option = getopt_long(argc, argv, ":tfR:l:m:n:p:r:v:H",
+	while ((option = getopt_long(argc, argv, ":tfR:l:m:n:p:r:v:V:A:H",
 	    vnic_lopts, NULL)) != -1) {
 		switch (option) {
 		case 't':
@@ -4457,6 +4463,9 @@
 			l_arg = B_TRUE;
 			break;
 		case 'm':
+			if (mac_addr_type != VNIC_MAC_ADDR_TYPE_UNKNOWN)
+				die("cannot specify -m option twice");
+
 			if (strcmp(optarg, "fixed") == 0) {
 				/*
 				 * A fixed MAC address must be specified
@@ -4468,9 +4477,9 @@
 			    &mac_addr_type) != DLADM_STATUS_OK) {
 				mac_addr_type = VNIC_MAC_ADDR_TYPE_FIXED;
 				/* MAC address specified by value */
-				mac_addr = _link_aton(optarg, &maclen);
+				mac_addr = _link_aton(optarg, (int *)&maclen);
 				if (mac_addr == NULL) {
-					if (maclen == -1)
+					if (maclen == (uint_t)-1)
 						die("invalid MAC address");
 					else
 						die("out of memory");
@@ -4490,14 +4499,29 @@
 				die("property list too long '%s'", propstr);
 			break;
 		case 'r':
-			mac_addr = _link_aton(optarg, &mac_prefix_len);
+			mac_addr = _link_aton(optarg, (int *)&mac_prefix_len);
 			if (mac_addr == NULL) {
-				if (mac_prefix_len == -1)
+				if (mac_prefix_len == (uint_t)-1)
 					die("invalid MAC address");
 				else
 					die("out of memory");
 			}
 			break;
+		case 'V':
+			if (!str2int(optarg, (int *)&vrid) ||
+			    vrid < VRRP_VRID_MIN || vrid > VRRP_VRID_MAX) {
+				die("invalid VRRP identifier '%s'", optarg);
+			}
+
+			break;
+		case 'A':
+			if (strcmp(optarg, "inet") == 0)
+				af = AF_INET;
+			else if (strcmp(optarg, "inet6") == 0)
+				af = AF_INET6;
+			else
+				die("invalid address family '%s'", optarg);
+			break;
 		case 'v':
 			if (vid != 0)
 				die_optdup(option);
@@ -4517,6 +4541,9 @@
 		}
 	}
 
+	if (mac_addr_type == VNIC_MAC_ADDR_TYPE_UNKNOWN)
+		mac_addr_type = VNIC_MAC_ADDR_TYPE_AUTO;
+
 	/*
 	 * 'f' - force, flag can be specified only with 'v' - vlan.
 	 */
@@ -4527,6 +4554,16 @@
 	    mac_addr_type != VNIC_MAC_ADDR_TYPE_FIXED)
 		usage();
 
+	if (mac_addr_type == VNIC_MAC_ADDR_TYPE_VRID) {
+		if (vrid == VRRP_VRID_NONE || af == AF_UNSPEC ||
+		    mac_addr != NULL || maclen != 0 || mac_slot != -1 ||
+		    mac_prefix_len != 0) {
+			usage();
+		}
+	} else if ((af != AF_UNSPEC || vrid != VRRP_VRID_NONE)) {
+		usage();
+	}
+
 	/* check required options */
 	if (!l_arg)
 		usage();
@@ -4556,12 +4593,13 @@
 		die("invalid vnic property");
 
 	status = dladm_vnic_create(handle, name, dev_linkid, mac_addr_type,
-	    mac_addr, maclen, &mac_slot, mac_prefix_len, vid, &linkid, proplist,
-	    flags);
+	    mac_addr, maclen, &mac_slot, mac_prefix_len, vid, vrid, af,
+	    &linkid, proplist, flags);
 	if (status != DLADM_STATUS_OK)
 		die_dlerr(status, "vnic creation over %s failed", devname);
 
 	dladm_free_props(proplist);
+	free(mac_addr);
 }
 
 static void
@@ -4835,6 +4873,13 @@
 				    gettext("factory, slot %d"),
 				    vnic->va_mac_slot);
 				break;
+			case VNIC_MAC_ADDR_TYPE_VRID:
+				(void) snprintf(vbuf.vnic_macaddrtype,
+				    sizeof (vbuf.vnic_macaddrtype),
+				    gettext("vrrp, %d/%s"),
+				    vnic->va_vrid, vnic->va_af == AF_INET ?
+				    "inet" : "inet6");
+				break;
 			}
 
 			if (strlen(vbuf.vnic_macaddrtype) > 0) {
@@ -5048,8 +5093,8 @@
 		altroot_cmd(altroot, argc, argv);
 
 	status = dladm_vnic_create(handle, name, DATALINK_INVALID_LINKID,
-	    VNIC_MAC_ADDR_TYPE_AUTO, mac_addr, ETHERADDRL, NULL, 0, 0, NULL,
-	    NULL, flags);
+	    VNIC_MAC_ADDR_TYPE_AUTO, mac_addr, ETHERADDRL, NULL, 0, 0,
+	    VRRP_VRID_NONE, AF_UNSPEC, NULL, NULL, flags);
 	if (status != DLADM_STATUS_OK)
 		die_dlerr(status, "etherstub creation failed");
 }
--- a/usr/src/cmd/nscd/nscd_frontend.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/nscd/nscd_frontend.c	Tue Nov 17 09:17:48 2009 -0800
@@ -1522,6 +1522,8 @@
 		case RTM_OLDDEL:
 		case RTM_RESOLVE:
 		case RTM_IFINFO:
+		case RTM_CHGADDR:
+		case RTM_FREEADDR:
 			break;
 		default:
 			_NSCD_LOG(NSCD_LOG_FRONT_END, NSCD_LOG_LEVEL_ERROR)
--- a/usr/src/cmd/ptools/pfiles/pfiles.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/ptools/pfiles/pfiles.c	Tue Nov 17 09:17:48 2009 -0800
@@ -592,6 +592,7 @@
 	    { SOL_SOCKET, SO_MAC_EXEMPT,	"SO_MAC_EXEMPT," },
 	    { SOL_SOCKET, SO_MAC_IMPLICIT,	"SO_MAC_IMPLICIT," },
 	    { SOL_SOCKET, SO_EXCLBIND,		"SO_EXCLBIND," },
+	    { SOL_SOCKET, SO_VRRP,		"SO_VRRP," },
 	    { IPPROTO_UDP, UDP_NAT_T_ENDPOINT,	"UDP_NAT_T_ENDPOINT," },
 	};
 	struct linger l;
--- a/usr/src/cmd/svc/shell/net_include.sh	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/svc/shell/net_include.sh	Tue Nov 17 09:17:48 2009 -0800
@@ -283,6 +283,23 @@
 }
 
 #
+# Given the interface name and the address family (inet or inet6), determine
+# whether this is a VRRP VNIC.
+#
+# This is used to determine whether to bring the interface up
+#
+not_vrrp_interface() {
+	macaddrtype=`/sbin/dladm show-vnic $1 -o MACADDRTYPE -p 2>/dev/null`
+
+	case "$macaddrtype" in
+	'vrrp'*''$2'')	vrrp=1
+			;;
+        *)		vrrp=0
+			;;
+	esac
+	return $vrrp
+}
+
 # doDHCPhostname interface
 # Pass to this function the name of an interface.  It will return
 # true if one should enable the use of DHCP client-side host name
@@ -315,6 +332,9 @@
 # the old style address which results in the interface being brought up 
 # and the netmask and broadcast address being set ($inet_oneline_epilogue).
 #
+# Note that if the interface is a VRRP interface, do not bring the address
+# up ($inet_oneline_epilogue_no_up).
+#
 # If there are multiple lines we assume the file contains a list of
 # commands to the processor with neither the implied bringing up of the
 # interface nor the setting of the default netmask and broadcast address.
@@ -322,6 +342,7 @@
 # Return non-zero if any command fails so that the caller may alert
 # users to errors in the configuration.
 #
+inet_oneline_epilogue_no_up="netmask + broadcast +"
 inet_oneline_epilogue="netmask + broadcast + up"
 
 inet_process_hostname()
@@ -365,8 +386,17 @@
 			#
 			[ -z "$ifcmds" ] && return $retval
 			if [ $multiple_lines = false ]; then
+				#
 				# The traditional one-line hostname file.
-				ifcmds="$ifcmds $inet_oneline_epilogue"
+				# Note that we only bring it up if the
+				# interface is not a VRRP VNIC.
+				#
+				if not_vrrp_interface $2 $3; then
+					estr="$inet_oneline_epilogue"
+				else
+					estr="$inet_oneline_epilogue_no_up"
+				fi
+				ifcmds="$ifcmds $estr"
 			fi
 
 			#
@@ -547,7 +577,13 @@
 		if [ $? != 0 ]; then
 			fail="$fail $1"
 		elif [ "$type" = inet6 ]; then
-		    	/sbin/ifconfig $1 inet6 up || fail="$fail $1"
+			#
+			# only bring the interface up if it is not a
+			# VRRP VNIC
+			#
+			if not_vrrp_interface $1 $type; then
+			    	/sbin/ifconfig $1 inet6 up || fail="$fail $1"
+			fi
 		fi
 		echo " $1\c"
 		shift
--- a/usr/src/cmd/truss/print.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/cmd/truss/print.c	Tue Nov 17 09:17:48 2009 -0800
@@ -1765,6 +1765,7 @@
 		case SO_MAC_EXEMPT:	return ("SO_MAC_EXEMPT");
 		case SO_ALLZONES:	return ("SO_ALLZONES");
 		case SO_MAC_IMPLICIT:	return ("SO_MAC_IMPLICIT");
+		case SO_VRRP:		return ("SO_VRRP");
 		case SO_EXCLBIND:	return ("SO_EXCLBIND");
 		case SO_DOMAIN:		return ("SO_DOMAIN");
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/vrrpadm/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=	vrrpadm
+
+include ../Makefile.cmd
+
+XGETFLAGS += -a -x $(PROG).xcl
+LDLIBS += -lvrrpadm -lnsl -linetutil
+
+.KEEP_STATE:
+
+all: $(PROG) 
+
+install: all $(ROOTUSRSBINPROG)
+
+clean:
+	$(RM) $(OBJS)
+
+lint:	lint_PROG
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/vrrpadm/vrrpadm.c	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,800 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/varargs.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <libvrrpadm.h>
+#include <ofmt.h>
+
+static vrrp_handle_t	vrrp_vh = NULL;
+typedef void cmd_func_t(int, char *[], const char *);
+
+static cmd_func_t do_create, do_delete, do_enable, do_disable,
+    do_modify, do_show;
+
+typedef struct {
+	char		*c_name;
+	cmd_func_t	*c_fn;
+	const char	*c_usage;
+} cmd_t;
+
+static cmd_t cmds[] = {
+	{ "create-router",	do_create,
+	    "-V <vrid> -l <link> -A {inet | inet6} [-p <priority>] "
+	    "[-i <adv_interval>] [-o <flags>] <router_name>" },
+	{ "delete-router",	do_delete,	"<router_name>"		},
+	{ "enable-router",	do_enable,	"<router_name>"		},
+	{ "disable-router",	do_disable,	"<router_name>"		},
+	{ "modify-router",	do_modify,
+	    "[-p <priority>] [-i <adv_interval>] [-o <flags>] <router_name>" },
+	{ "show-router",	do_show,
+	    "[-P | -x] [-o field[,...]] [-p] [<router_name>]"	}
+};
+
+static const struct option lopts[] = {
+	{"vrid",		required_argument,	0, 'V'},
+	{"link",		required_argument,	0, 'l'},
+	{"address_family",	required_argument,	0, 'A'},
+	{"priority",		required_argument,	0, 'p'},
+	{"adv_interval",	required_argument,	0, 'i'},
+	{"flags",		required_argument,	0, 'o'},
+	{ 0, 0, 0, 0 }
+};
+
+static const struct option l_show_opts[] = {
+	{"peer",	no_argument,		0, 'P'},
+	{"parsable",	no_argument,		0, 'p'},
+	{"extended",	no_argument,		0, 'x'},
+	{"output",	required_argument,	0, 'o'},
+	{ 0, 0, 0, 0 }
+};
+
+static ofmt_cb_t sfunc_vrrp_conf;
+
+/*
+ * structures for 'dladm show-link -s' (print statistics)
+ */
+enum {
+	ROUTER_NAME,
+	ROUTER_VRID,
+	ROUTER_LINK,
+	ROUTER_VNIC,
+	ROUTER_AF,
+	ROUTER_PRIO,
+	ROUTER_ADV_INTV,
+	ROUTER_MODE,
+	ROUTER_STATE,
+	ROUTER_PRV_STAT,
+	ROUTER_STAT_LAST,
+	ROUTER_PEER,
+	ROUTER_P_PRIO,
+	ROUTER_P_INTV,
+	ROUTER_P_ADV_LAST,
+	ROUTER_M_DOWN_INTV,
+	ROUTER_PRIMARY_IP,
+	ROUTER_VIRTUAL_IPS,
+	ROUTER_VIP_CNT
+};
+
+/*
+ * structures for 'vrrpadm show-router'
+ */
+static const ofmt_field_t show_print_fields[] = {
+/* name,	field width,	index,			callback */
+{ "NAME",		8,	ROUTER_NAME,		sfunc_vrrp_conf	},
+{ "VRID",		5,	ROUTER_VRID,		sfunc_vrrp_conf	},
+{ "LINK",		8,	ROUTER_LINK,		sfunc_vrrp_conf },
+{ "VNIC",		8,	ROUTER_VNIC,		sfunc_vrrp_conf },
+{ "AF",			5,	ROUTER_AF,		sfunc_vrrp_conf },
+{ "PRIO",		5,	ROUTER_PRIO,		sfunc_vrrp_conf },
+{ "ADV_INTV",		9,	ROUTER_ADV_INTV,	sfunc_vrrp_conf },
+{ "MODE",		6,	ROUTER_MODE,		sfunc_vrrp_conf	},
+{ "STATE",		6,	ROUTER_STATE,		sfunc_vrrp_conf },
+{ "PRV_STAT",		9, 	ROUTER_PRV_STAT,	sfunc_vrrp_conf	},
+{ "STAT_LAST",		10,	ROUTER_STAT_LAST,	sfunc_vrrp_conf },
+{ "PEER",		20,	ROUTER_PEER,		sfunc_vrrp_conf	},
+{ "P_PRIO",		7,	ROUTER_P_PRIO,		sfunc_vrrp_conf	},
+{ "P_INTV",		9,	ROUTER_P_INTV,		sfunc_vrrp_conf	},
+{ "P_ADV_LAST",		11,	ROUTER_P_ADV_LAST,	sfunc_vrrp_conf	},
+{ "M_DOWN_INTV",	12,	ROUTER_M_DOWN_INTV,	sfunc_vrrp_conf	},
+{ "PRIMARY_IP",		20,	ROUTER_PRIMARY_IP,	sfunc_vrrp_conf	},
+{ "VIRTUAL_IPS",	40,	ROUTER_VIRTUAL_IPS,	sfunc_vrrp_conf	},
+{ "VIP_CNT",		7,	ROUTER_VIP_CNT,		sfunc_vrrp_conf	},
+{ NULL,			0, 	0,			NULL}}
+;
+
+static vrrp_err_t do_show_router(const char *, ofmt_handle_t);
+static int str2opt(char *opts, uint32_t *, boolean_t *, boolean_t *);
+static char *timeval_since_str(int, char *, size_t);
+
+static void usage();
+static void warn(const char *, ...);
+static void err_exit(const char *, ...);
+static void opterr_exit(int, int, const char *);
+
+int
+main(int argc, char *argv[])
+{
+	vrrp_err_t	err;
+	int		i;
+	cmd_t		*cp;
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	if (argv[1] == NULL)
+		usage();
+
+	if ((err = vrrp_open(&vrrp_vh)) != VRRP_SUCCESS)
+		err_exit("operation failed: %s", vrrp_err2str(err));
+
+	for (i = 0; i < sizeof (cmds) / sizeof (cmd_t); i++) {
+		cp = &cmds[i];
+		if (strcmp(argv[1], cp->c_name) == 0) {
+			cp->c_fn(argc - 1, &argv[1], cp->c_usage);
+			vrrp_close(vrrp_vh);
+			return (EXIT_SUCCESS);
+		}
+	}
+
+	usage();
+	return (EXIT_FAILURE);
+}
+
+static void
+do_create(int argc, char *argv[], const char *usage)
+{
+	vrrp_vr_conf_t		conf;
+	int			c;
+	uint32_t		create_mask = 0, mask;
+	char			*endp;
+	vrrp_err_t		err;
+
+	/*
+	 * default value
+	 */
+	bzero(&conf, sizeof (vrrp_vr_conf_t));
+	conf.vvc_vrid = VRRP_VRID_NONE;
+	conf.vvc_af = AF_UNSPEC;
+	conf.vvc_pri = VRRP_PRI_DEFAULT;
+	conf.vvc_adver_int = VRRP_MAX_ADVER_INT_DFLT;
+	conf.vvc_preempt = B_TRUE;
+	conf.vvc_accept = B_TRUE;
+	conf.vvc_enabled = B_TRUE;
+
+	while ((c = getopt_long(argc, argv, ":V:l:p:i:o:A:f", lopts,
+	    NULL)) != EOF) {
+		switch (c) {
+		case 'l':
+			if (strlcpy(conf.vvc_link, optarg,
+			    sizeof (conf.vvc_link)) >=
+			    sizeof (conf.vvc_link)) {
+				err_exit("invalid data-link name %s", optarg);
+			}
+			break;
+		case 'i':
+			if (create_mask & VRRP_CONF_INTERVAL)
+				err_exit("duplicate '-i' option");
+
+			create_mask |= VRRP_CONF_INTERVAL;
+			conf.vvc_adver_int = (uint32_t)strtol(optarg, &endp, 0);
+			if ((*endp) != '\0' ||
+			    conf.vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
+			    conf.vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
+			    (conf.vvc_adver_int == 0 && errno != 0)) {
+				err_exit("invalid advertisement interval");
+			}
+			break;
+		case 'p':
+			if (create_mask & VRRP_CONF_PRIORITY)
+				err_exit("duplicate '-p' option");
+
+			create_mask |= VRRP_CONF_PRIORITY;
+			conf.vvc_pri = strtol(optarg, &endp, 0);
+			if ((*endp) != '\0' || conf.vvc_pri < VRRP_PRI_MIN ||
+			    conf.vvc_pri > VRRP_PRI_OWNER ||
+			    (conf.vvc_pri == 0 && errno != 0)) {
+				err_exit("invalid priority");
+			}
+			break;
+		case 'o':
+			mask = 0;
+			if (str2opt(optarg, &mask,
+			    &conf.vvc_preempt, &conf.vvc_accept) != 0) {
+				err_exit("invalid options: %s", optarg);
+			}
+			if (mask & create_mask & VRRP_CONF_PREEMPT)
+				err_exit("duplicate '-o preempt' option");
+			else if (mask & create_mask & VRRP_CONF_ACCEPT)
+				err_exit("duplicate '-o accept' option");
+			create_mask |= mask;
+			break;
+		case 'V':
+			if (conf.vvc_vrid != VRRP_VRID_NONE)
+				err_exit("duplicate '-V' option");
+
+			conf.vvc_vrid = strtol(optarg, &endp, 0);
+			if ((*endp) != '\0' || conf.vvc_vrid < VRRP_VRID_MIN ||
+			    conf.vvc_vrid > VRRP_VRID_MAX ||
+			    (conf.vvc_vrid == 0 && errno != 0)) {
+				err_exit("invalid VRID");
+			}
+			break;
+		case 'A':
+			if (conf.vvc_af != AF_UNSPEC)
+				err_exit("duplicate '-A' option");
+
+			if (strcmp(optarg, "inet") == 0)
+				conf.vvc_af = AF_INET;
+			else if (strcmp(optarg, "inet6") == 0)
+				conf.vvc_af = AF_INET6;
+			else
+				err_exit("invalid address family");
+			break;
+		default:
+			opterr_exit(optopt, c, usage);
+		}
+	}
+
+	if (argc - optind > 1)
+		err_exit("usage: %s", gettext(usage));
+
+	if (optind != argc - 1)
+		err_exit("VRRP name not specified");
+
+	if (strlcpy(conf.vvc_name, argv[optind],
+	    sizeof (conf.vvc_name)) >= sizeof (conf.vvc_name)) {
+		err_exit("Invalid router name %s", argv[optind]);
+	}
+
+	if (conf.vvc_vrid == VRRP_VRID_NONE)
+		err_exit("VRID not specified");
+
+	if (conf.vvc_af == AF_UNSPEC)
+		err_exit("address family not specified");
+
+	if (strlen(conf.vvc_link) == 0)
+		err_exit("link name not specified");
+
+	if (!conf.vvc_accept && conf.vvc_pri == VRRP_PRI_OWNER)
+		err_exit("accept_mode must be true for virtual IP owner");
+
+done:
+	if ((err = vrrp_create(vrrp_vh, &conf)) == VRRP_SUCCESS)
+		return;
+
+	err_exit("create-router failed: %s", vrrp_err2str(err));
+}
+
+static void
+do_delete(int argc, char *argv[], const char *use)
+{
+	vrrp_err_t	err;
+
+	if (argc != 2)
+		err_exit("usage: %s", gettext(use));
+
+	if ((err = vrrp_delete(vrrp_vh, argv[1])) != VRRP_SUCCESS)
+		err_exit("delete-router failed: %s", vrrp_err2str(err));
+}
+
+static void
+do_enable(int argc, char *argv[], const char *use)
+{
+	vrrp_err_t	err;
+
+	if (argc != 2)
+		err_exit("usage: %s", gettext(use));
+
+	if ((err = vrrp_enable(vrrp_vh, argv[1])) != VRRP_SUCCESS)
+		err_exit("enable-router failed: %s", vrrp_err2str(err));
+}
+
+static void
+do_disable(int argc, char *argv[], const char *use)
+{
+	vrrp_err_t	err;
+
+	if (argc != 2)
+		err_exit("usage: %s", gettext(use));
+
+	if ((err = vrrp_disable(vrrp_vh, argv[1])) != VRRP_SUCCESS)
+		err_exit("disable-router failed: %s", vrrp_err2str(err));
+}
+
+static void
+do_modify(int argc, char *argv[], const char *use)
+{
+	vrrp_vr_conf_t	conf;
+	vrrp_err_t	err;
+	uint32_t	modify_mask = 0, mask;
+	char		*endp;
+	int		c;
+
+	while ((c = getopt_long(argc, argv, ":i:p:o:", lopts, NULL)) != EOF) {
+		switch (c) {
+		case 'i':
+			if (modify_mask & VRRP_CONF_INTERVAL)
+				err_exit("duplicate '-i' option");
+
+			modify_mask |= VRRP_CONF_INTERVAL;
+			conf.vvc_adver_int = (uint32_t)strtol(optarg, &endp, 0);
+			if ((*endp) != '\0' ||
+			    conf.vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
+			    conf.vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
+			    (conf.vvc_adver_int == 0 && errno != 0)) {
+				err_exit("invalid advertisement interval");
+			}
+			break;
+		case 'o':
+			mask = 0;
+			if (str2opt(optarg, &mask, &conf.vvc_preempt,
+			    &conf.vvc_accept) != 0) {
+				err_exit("Invalid options");
+			}
+			if (mask & modify_mask & VRRP_CONF_PREEMPT)
+				err_exit("duplicate '-o preempt' option");
+			else if (mask & modify_mask & VRRP_CONF_ACCEPT)
+				err_exit("duplicate '-o accept' option");
+			modify_mask |= mask;
+			break;
+		case 'p':
+			if (modify_mask & VRRP_CONF_PRIORITY)
+				err_exit("duplicate '-p' option");
+
+			modify_mask |= VRRP_CONF_PRIORITY;
+			conf.vvc_pri = strtol(optarg, &endp, 0);
+			if ((*endp) != '\0' || conf.vvc_pri < VRRP_PRI_MIN ||
+			    conf.vvc_pri > VRRP_PRI_OWNER ||
+			    (conf.vvc_pri == 0 && errno != 0)) {
+				err_exit("invalid priority");
+			}
+			break;
+		default:
+			opterr_exit(optopt, c, use);
+		}
+	}
+
+	if (argc - optind > 1)
+		err_exit("usage: %s", gettext(use));
+
+	if (optind != argc - 1)
+		err_exit("VRRP name not specified.");
+
+	if (strlcpy(conf.vvc_name, argv[optind], sizeof (conf.vvc_name)) >=
+	    sizeof (conf.vvc_name)) {
+		err_exit("invalid router name %s", argv[optind]);
+	}
+
+	if ((modify_mask & VRRP_CONF_ACCEPT) && !conf.vvc_accept &&
+	    (modify_mask & VRRP_CONF_PRIORITY) &&
+	    conf.vvc_pri == VRRP_PRI_OWNER) {
+		err_exit("accept_mode must be true for virtual IP owner");
+	}
+
+	if (modify_mask == 0)
+		usage();
+
+	err = vrrp_modify(vrrp_vh, &conf, modify_mask);
+	if (err != VRRP_SUCCESS)
+		err_exit("modify-router failed: %s", vrrp_err2str(err));
+}
+
+/*
+ * 'show-router' one VRRP router.
+ */
+static vrrp_err_t
+do_show_router(const char *vn, ofmt_handle_t ofmt)
+{
+	vrrp_queryinfo_t	*vq;
+	vrrp_err_t		err;
+
+	if ((err = vrrp_query(vrrp_vh, vn, &vq)) != VRRP_SUCCESS)
+		return (err);
+
+	ofmt_print(ofmt, vq);
+	free(vq);
+	return (VRRP_SUCCESS);
+}
+
+static void
+do_show(int argc, char *argv[], const char *use)
+{
+	int			c;
+	char			*fields_str = NULL;
+	char			*names = NULL, *router;
+	uint32_t		i, in_cnt = 0, out_cnt;
+	ofmt_status_t		oferr;
+	ofmt_handle_t		ofmt;
+	uint_t			ofmt_flags = 0;
+	vrrp_err_t		err = VRRP_SUCCESS;
+	boolean_t		P_opt, x_opt;
+
+	static char		*dft_fields_str =
+	    "NAME,VRID,LINK,AF,PRIO,ADV_INTV,MODE,STATE,VNIC";
+	static char		*ext_fields_str =
+	    "NAME,STATE,PRV_STAT,STAT_LAST,VNIC,PRIMARY_IP,VIRTUAL_IPS";
+	static char		*peer_fields_str =
+	    "NAME,PEER,P_PRIO,P_INTV,P_ADV_LAST,M_DOWN_INTV";
+	/*
+	 * If parsable output is requested, add VIP_CNT into the output
+	 * for extended output. It is not needed for human-readable
+	 * output as it is obvious from the VIRTUAL_IPS list.
+	 */
+	static char		*ext_parsable_fields_str =
+	    "NAME,STATE,PRV_STAT,STAT_LAST,VNIC,PRIMARY_IP,VIP_CNT,"
+	    "VIRTUAL_IPS";
+
+	P_opt = x_opt = B_FALSE;
+	fields_str = dft_fields_str;
+	while ((c = getopt_long(argc, argv, ":Pxpo:", l_show_opts,
+	    NULL)) != EOF) {
+		switch (c) {
+		case 'o':
+			fields_str = optarg;
+			break;
+		case 'p':
+			ofmt_flags |= OFMT_PARSABLE;
+			break;
+		case 'P':
+			P_opt = B_TRUE;
+			fields_str = peer_fields_str;
+			break;
+		case 'x':
+			x_opt = B_TRUE;
+			fields_str = ext_fields_str;
+			break;
+		default:
+			opterr_exit(optopt, c, use);
+		}
+	}
+
+	if (x_opt && P_opt)
+		err_exit("incompatible -P and -x options");
+
+	/*
+	 * If parsable output is requested, add VIP_CNT into the output
+	 * for extended output.
+	 */
+	if ((ofmt_flags & OFMT_PARSABLE) && (fields_str == ext_fields_str))
+		fields_str = ext_parsable_fields_str;
+
+	if ((oferr = ofmt_open(fields_str, show_print_fields, ofmt_flags,
+	    0, &ofmt)) != OFMT_SUCCESS) {
+		char buf[OFMT_BUFSIZE];
+
+		/*
+		 * If some fields were badly formed in human-friendly mode, we
+		 * emit a warning and continue.  Otherwise exit immediately.
+		 */
+		(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
+		if (oferr != OFMT_EBADFIELDS || (ofmt_flags & OFMT_PARSABLE)) {
+			ofmt_close(ofmt);
+			err_exit(buf);
+		} else {
+			warn(buf);
+		}
+	}
+
+	/* Show one router */
+	if (optind == argc - 1) {
+		err = do_show_router(argv[optind], ofmt);
+		goto done;
+	}
+
+	/*
+	 * Show all routers. First set in_cnt to 0 to find out the number
+	 * of vrrp routers.
+	 */
+again:
+	if ((in_cnt != 0) && (names = malloc(in_cnt * VRRP_NAME_MAX)) == NULL) {
+		err = VRRP_ENOMEM;
+		goto done;
+	}
+
+	out_cnt = in_cnt;
+	if ((err = vrrp_list(vrrp_vh, VRRP_VRID_NONE, NULL, AF_UNSPEC,
+	    &out_cnt, names)) != VRRP_SUCCESS) {
+		free(names);
+		goto done;
+	}
+
+	/*
+	 * The VRRP routers has been changed between two vrrp_list()
+	 * calls, try again.
+	 */
+	if (out_cnt > in_cnt) {
+		in_cnt = out_cnt;
+		free(names);
+		goto again;
+	}
+
+	/*
+	 * Each VRRP router name is separated by '\0`
+	 */
+	router = names;
+	for (i = 0; i < in_cnt; i++) {
+		(void) do_show_router(router, ofmt);
+		router += strlen(router) + 1;
+	}
+
+	free(names);
+
+done:
+	ofmt_close(ofmt);
+
+	if (err != VRRP_SUCCESS)
+		err_exit(vrrp_err2str(err));
+}
+
+/*
+ * Callback function to print fields of the configuration information.
+ */
+static boolean_t
+sfunc_vrrp_conf(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
+{
+	vrrp_queryinfo_t	*qinfo = ofmtarg->ofmt_cbarg;
+	uint_t			ofmtid = ofmtarg->ofmt_id;
+	vrrp_vr_conf_t		*conf = &qinfo->show_vi;
+	vrrp_stateinfo_t	*sinfo = &qinfo->show_vs;
+	vrrp_peer_t		*peer = &qinfo->show_vp;
+	vrrp_timerinfo_t	*tinfo = &qinfo->show_vt;
+	vrrp_addrinfo_t		*ainfo = &qinfo->show_va;
+
+	switch (ofmtid) {
+	case ROUTER_NAME:
+		(void) snprintf(buf, bufsize, "%s", conf->vvc_name);
+		break;
+	case ROUTER_VRID:
+		(void) snprintf(buf, bufsize, "%d", conf->vvc_vrid);
+		break;
+	case ROUTER_LINK:
+		(void) snprintf(buf, bufsize, "%s", conf->vvc_link);
+		break;
+	case ROUTER_AF:
+		(void) snprintf(buf, bufsize, "IPv%d",
+		    conf->vvc_af == AF_INET ? 4 : 6);
+		break;
+	case ROUTER_PRIO:
+		(void) snprintf(buf, bufsize, "%d", conf->vvc_pri);
+		break;
+	case ROUTER_ADV_INTV:
+		(void) snprintf(buf, bufsize, "%d", conf->vvc_adver_int);
+		break;
+	case ROUTER_MODE:
+		(void) strlcpy(buf, "-----", bufsize);
+		if (conf->vvc_enabled)
+			buf[0] = 'e';
+		if (conf->vvc_pri == VRRP_PRI_OWNER)
+			buf[1] = 'o';
+		if (conf->vvc_preempt)
+			buf[2] = 'p';
+		if (conf->vvc_accept)
+			buf[3] = 'a';
+		break;
+	case ROUTER_STATE:
+		(void) snprintf(buf, bufsize, "%s",
+		    vrrp_state2str(sinfo->vs_state));
+		break;
+	case ROUTER_PRV_STAT:
+		(void) snprintf(buf, bufsize, "%s",
+		    vrrp_state2str(sinfo->vs_prev_state));
+		break;
+	case ROUTER_STAT_LAST:
+		(void) timeval_since_str(tinfo->vt_since_last_tran, buf,
+		    bufsize);
+		break;
+	case ROUTER_PEER:
+		/* LINTED E_CONSTANT_CONDITION */
+		VRRPADDR2STR(conf->vvc_af, &peer->vp_addr,
+		    buf, bufsize, B_FALSE);
+		break;
+	case ROUTER_P_PRIO:
+		(void) snprintf(buf, bufsize, "%d", peer->vp_prio);
+		break;
+	case ROUTER_P_INTV:
+		(void) snprintf(buf, bufsize, "%d", peer->vp_adver_int);
+		break;
+	case ROUTER_P_ADV_LAST:
+		(void) timeval_since_str(tinfo->vt_since_last_adv, buf,
+		    bufsize);
+		break;
+	case ROUTER_M_DOWN_INTV:
+		(void) snprintf(buf, bufsize, "%d", tinfo->vt_master_down_intv);
+		break;
+	case ROUTER_VNIC:
+		(void) snprintf(buf, bufsize, "%s",
+		    strlen(ainfo->va_vnic) == 0 ? "--" : ainfo->va_vnic);
+		break;
+	case ROUTER_PRIMARY_IP:
+		/* LINTED E_CONSTANT_CONDITION */
+		VRRPADDR2STR(conf->vvc_af, &ainfo->va_primary,
+		    buf, bufsize, B_FALSE);
+		break;
+	case ROUTER_VIRTUAL_IPS: {
+		uint32_t i;
+
+		for (i = 0; i < ainfo->va_vipcnt; i++) {
+			/* LINTED E_CONSTANT_CONDITION */
+			VRRPADDR2STR(conf->vvc_af, &(ainfo->va_vips[i]),
+			    buf, bufsize, B_TRUE);
+			if (i != ainfo->va_vipcnt - 1)
+				(void) strlcat(buf, ",", bufsize);
+		}
+		break;
+	}
+	case ROUTER_VIP_CNT:
+		(void) snprintf(buf, bufsize, "%d", ainfo->va_vipcnt);
+		break;
+	default:
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+static void
+usage()
+{
+	int	i;
+	cmd_t	*cp;
+
+	(void) fprintf(stderr, "%s",
+	    gettext("usage:  vrrpadm <sub-command> <args> ...\n"));
+
+	for (i = 0; i < sizeof (cmds) / sizeof (cmd_t); i++) {
+		cp = &cmds[i];
+		if (cp->c_usage != NULL)
+			(void) fprintf(stderr, "          %-10s %s\n",
+			    gettext(cp->c_name), gettext(cp->c_usage));
+	}
+
+	vrrp_close(vrrp_vh);
+	exit(EXIT_FAILURE);
+}
+
+static void
+warn(const char *format, ...)
+{
+	va_list alist;
+
+	format = gettext(format);
+	(void) fprintf(stderr, gettext("warning: "));
+
+	va_start(alist, format);
+	(void) vfprintf(stderr, format, alist);
+	va_end(alist);
+	(void) putc('\n', stderr);
+}
+
+static void
+err_exit(const char *format, ...)
+{
+	va_list alist;
+
+	format = gettext(format);
+	va_start(alist, format);
+	(void) vfprintf(stderr, format, alist);
+	va_end(alist);
+	(void) putc('\n', stderr);
+	vrrp_close(vrrp_vh);
+	exit(EXIT_FAILURE);
+}
+
+static void
+opterr_exit(int opt, int opterr, const char *use)
+{
+	switch (opterr) {
+	case ':':
+		err_exit("option '-%c' requires a value\nusage: %s", opt,
+		    gettext(use));
+		break;
+	case '?':
+	default:
+		err_exit("unrecognized option '-%c'\nusage: %s", opt,
+		    gettext(use));
+		break;
+	}
+}
+
+static char *
+timeval_since_str(int mill, char *str, size_t len)
+{
+	int	sec, msec, min;
+
+	msec = mill % 1000;
+	sec = mill / 1000;
+	min = sec > 60 ? sec / 60 : 0;
+	sec %= 60;
+
+	if (min > 0)
+		(void) snprintf(str, len, "%4dm%2ds", min, sec);
+	else
+		(void) snprintf(str, len, "%4d.%03ds", sec, msec);
+
+	return (str);
+}
+
+/*
+ * Parses options string. The values of the two options will be returned
+ * by 'preempt' and 'accept', and the mask 'modify_mask' will be updated
+ * accordingly.
+ *
+ * Returns 0 on success, errno on failures.
+ *
+ * Used by do_create() and do_modify().
+ *
+ * Note that "opts" could be modified internally in this function.
+ */
+static int
+str2opt(char *opts, uint32_t *modify_mask, boolean_t *preempt,
+    boolean_t *accept)
+{
+	char		*value;
+	int		opt;
+	uint32_t	mask = 0;
+	static enum { o_preempt = 0, o_un_preempt, o_accept, o_no_accept };
+	static char	*myopts[] = {
+		"preempt",
+		"un_preempt",
+		"accept",
+		"no_accept",
+		NULL
+	};
+
+	while (*opts != '\0') {
+		switch ((opt = getsubopt(&opts, myopts, &value))) {
+		case o_preempt:
+		case o_un_preempt:
+			if (mask & VRRP_CONF_PREEMPT)
+				return (EINVAL);
+
+			mask |= VRRP_CONF_PREEMPT;
+			*preempt = (opt == o_preempt);
+			break;
+		case o_accept:
+		case o_no_accept:
+			if (mask & VRRP_CONF_ACCEPT)
+				return (EINVAL);
+
+			mask |= VRRP_CONF_ACCEPT;
+			*accept = (opt == o_accept);
+			break;
+		default:
+			return (EINVAL);
+		}
+	}
+
+	*modify_mask |= mask;
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/vrrpadm/vrrpadm.xcl	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,75 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+msgid	"create-router"
+msgid	"delete-router"
+msgid	"enable-router"
+msgid	"disable-router"
+msgid	"modify-router"
+msgid	"VRID"
+msgid	"vrid"
+msgid	"LINK"
+msgid	"link"
+msgid	"inet"
+msgid	"inet6"
+msgid	"router_name"
+msgid	"flags"
+msgid	"address_family"
+msgid	"priority"
+msgid	"adv_interal"
+msgid	"peer"
+msgid	"parsable"
+msgid	"extended"
+msgid	"output"
+msgid	"NAME"
+msgid	"VNIC"
+msgid	"AF"
+msgid	"PRIO"
+msgid	"ADV_INTV"
+msgid	"MODE"
+msgid	"STATE"
+msgid	"PRV_STAT"
+msgid	"STAT_LAST"
+msgid	"PEER"
+msgid	"P_PRIO"
+msgid	"P_INTV"
+msgid	"P_ADV_LAST"
+msgid	"M_DOWN_INTV"
+msgid	"PRIMARY_IP"
+msgid	"VIRTUAL_IPS"
+msgid	"VIP_CNT"
+msgid	":V:l:p:i:o:A:f"
+msgid	"-i"
+msgid	"-p"
+msgid	"-o"
+msgid	"preempt"
+msgid	"accept"
+msgid	"-V"
+msgid	"-A"
+msgid	"VRRP"
+msgid	"accept_mode"
+msgid	":i:p:o:"
+msgid	":Pxpo:"
+msgid	"vrrpadm"
+msgid	"sub-command"
+msgid	"args"
--- a/usr/src/lib/Makefile	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -22,7 +22,6 @@
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#
 
 include ../Makefile.master
 
@@ -252,6 +251,7 @@
 	libadutils	\
 	libipmi		\
 	libexacct/demo	\
+	libvrrpadm	\
 	libvscan	\
 	libgrubmgmt	\
 	smbsrv		\
@@ -352,6 +352,7 @@
 	libsum		\
 	libtsol		\
 	libuutil	\
+	libvrrpadm	\
 	libvscan	\
 	libwanboot	\
 	libwanbootutil	\
@@ -464,6 +465,7 @@
 	libtnfprobe	\
 	libtsnet	\
 	libtsol		\
+	libvrrpadm	\
 	libvolmgt	\
 	libumem		\
 	libunistat	\
@@ -639,6 +641,7 @@
 libtsalarm:	libpcp
 smbsrv:		libsocket libnsl libmd libxnet libpthread librt \
 		libshare libidmap pkcs11 libsqlite
+libvrrpadm:	libsocket libdladm
 libvscan:	libscf
 scsi:		libnvpair
 mpapi:		libpthread libdevinfo libsysevent libnvpair
--- a/usr/src/lib/libdladm/common/libdladm_impl.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libdladm/common/libdladm_impl.h	Tue Nov 17 09:17:48 2009 -0800
@@ -93,6 +93,8 @@
 #define	FMADDRSLOT	"maddrslot"	/* uint64_t */
 #define	FMADDRPREFIXLEN	"maddrpreflen"	/* uint64_t */
 #define	FHWRINGS	"hwrings"	/* boolean_t */
+#define	FVRID		"vrid"		/* uint64_t */
+#define	FVRAF		"vraf"		/* uint64_t */
 
 /*
  * Set for simlinks only
--- a/usr/src/lib/libdladm/common/libdlvlan.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libdladm/common/libdlvlan.c	Tue Nov 17 09:17:48 2009 -0800
@@ -19,12 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #include <libdlvlan.h>
 #include <libdlvnic.h>
+#include <libvrrpadm.h>
 
 /*
  * VLAN Administration Library.
@@ -62,8 +63,8 @@
     datalink_id_t *vlan_id_out)
 {
 	return (dladm_vnic_create(handle, vlan, linkid,
-	    VNIC_MAC_ADDR_TYPE_PRIMARY, NULL, 0, NULL, 0, vid, vlan_id_out,
-	    proplist, flags | DLADM_OPT_VLAN));
+	    VNIC_MAC_ADDR_TYPE_PRIMARY, NULL, 0, NULL, 0, vid, VRRP_VRID_NONE,
+	    AF_UNSPEC, vlan_id_out, proplist, flags | DLADM_OPT_VLAN));
 }
 
 /*
--- a/usr/src/lib/libdladm/common/libdlvnic.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libdladm/common/libdlvnic.c	Tue Nov 17 09:17:48 2009 -0800
@@ -38,6 +38,7 @@
 #include <net/if_dl.h>
 #include <sys/dld.h>
 #include <libdladm_impl.h>
+#include <libvrrpadm.h>
 #include <libdllink.h>
 #include <libdlbridge.h>
 #include <libdlvnic.h>
@@ -64,8 +65,8 @@
 dladm_vnic_diag2status(vnic_ioc_diag_t ioc_diag)
 {
 	switch (ioc_diag) {
-	case VNIC_IOC_DIAG_MACADDR_INVALID:
-		return (DLADM_STATUS_INVALIDMACADDR);
+	case VNIC_IOC_DIAG_NONE:
+		return (DLADM_STATUS_OK);
 	case VNIC_IOC_DIAG_MACADDRLEN_INVALID:
 		return (DLADM_STATUS_INVALIDMACADDRLEN);
 	case VNIC_IOC_DIAG_MACADDR_NIC:
@@ -88,8 +89,11 @@
 		return (DLADM_STATUS_INVALID_MACMARGIN);
 	case VNIC_IOC_DIAG_NO_HWRINGS:
 		return (DLADM_STATUS_NO_HWRINGS);
+	case VNIC_IOC_DIAG_MACADDR_INVALID:
+		return (DLADM_STATUS_INVALIDMACADDR);
+	default:
+		return (DLADM_STATUS_FAILED);
 	}
-	return (DLADM_STATUS_OK);
 }
 
 /*
@@ -110,6 +114,8 @@
 	ioc.vc_mac_slot = attr->va_mac_slot;
 	ioc.vc_mac_prefix_len = attr->va_mac_prefix_len;
 	ioc.vc_vid = attr->va_vid;
+	ioc.vc_vrid = attr->va_vrid;
+	ioc.vc_af = attr->va_af;
 	ioc.vc_flags = attr->va_force ? VNIC_IOC_CREATE_FORCE : 0;
 	ioc.vc_flags |= attr->va_hwrings ? VNIC_IOC_CREATE_REQ_HWRINGS : 0;
 
@@ -174,6 +180,8 @@
 	attrp->va_mac_slot = vnic->vn_mac_slot;
 	attrp->va_mac_prefix_len = vnic->vn_mac_prefix_len;
 	attrp->va_vid = vnic->vn_vid;
+	attrp->va_vrid = vnic->vn_vrid;
+	attrp->va_af = vnic->vn_af;
 	attrp->va_force = vnic->vn_force;
 
 bail:
@@ -237,6 +245,20 @@
 
 		attrp->va_mac_addr_type = (vnic_mac_addr_type_t)u64;
 
+		if ((status = dladm_get_conf_field(handle, conf, FVRID,
+		    &u64, sizeof (u64))) != DLADM_STATUS_OK) {
+			attrp->va_vrid = VRRP_VRID_NONE;
+		} else {
+			attrp->va_vrid = (vrid_t)u64;
+		}
+
+		if ((status = dladm_get_conf_field(handle, conf, FVRAF,
+		    &u64, sizeof (u64))) != DLADM_STATUS_OK) {
+			attrp->va_af = AF_UNSPEC;
+		} else {
+			attrp->va_af = (int)u64;
+		}
+
 		status = dladm_get_conf_field(handle, conf, FMADDRLEN, &u64,
 		    sizeof (u64));
 		attrp->va_mac_len = ((status == DLADM_STATUS_OK) ?
@@ -318,7 +340,8 @@
 	{"random", VNIC_MAC_ADDR_TYPE_RANDOM},
 	{"factory", VNIC_MAC_ADDR_TYPE_FACTORY},
 	{"auto", VNIC_MAC_ADDR_TYPE_AUTO},
-	{"fixed", VNIC_MAC_ADDR_TYPE_PRIMARY}
+	{"fixed", VNIC_MAC_ADDR_TYPE_PRIMARY},
+	{"vrrp", VNIC_MAC_ADDR_TYPE_VRID}
 };
 
 #define	NADDR_TYPES (sizeof (addr_types) / sizeof (dladm_vnic_addr_type_t))
@@ -352,13 +375,40 @@
 }
 
 /*
+ * Based on the VRRP specification, the virtual router MAC address associated
+ * with a virtual router is an IEEE 802 MAC address in the following format:
+ *
+ * IPv4 case: 00-00-5E-00-01-{VRID} (in hex in internet standard bit-order)
+ *
+ * IPv6 case: 00-00-5E-00-02-{VRID} (in hex in internet standard bit-order)
+ */
+static dladm_status_t
+i_dladm_vnic_vrrp_mac(vrid_t vrid, int af, uint8_t *mac, uint_t maclen)
+{
+	if (maclen < ETHERADDRL || vrid < VRRP_VRID_MIN ||
+	    vrid > VRRP_VRID_MAX || (af != AF_INET && af != AF_INET6)) {
+		return (DLADM_STATUS_BADARG);
+	}
+
+	mac[0] = mac[1] = mac[3] = 0x0;
+	mac[2] = 0x5e;
+	mac[4] = (af == AF_INET) ? 0x01 : 0x02;
+	mac[5] = vrid;
+	return (DLADM_STATUS_OK);
+}
+
+/*
  * Create a new VNIC / VLAN. Update the configuration file and bring it up.
+ * The "vrid" and "af" arguments are only required if the mac_addr_type is
+ * VNIC_MAC_ADDR_TYPE_VRID. In that case, the MAC address will be caculated
+ * based on the above algorithm.
  */
 dladm_status_t
 dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid,
-    vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, int mac_len,
-    int *mac_slot, uint_t mac_prefix_len, uint16_t vid,
-    datalink_id_t *vnic_id_out, dladm_arg_list_t *proplist, uint32_t flags)
+    vnic_mac_addr_type_t mac_addr_type, uchar_t *mac_addr, uint_t mac_len,
+    int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
+    int af, datalink_id_t *vnic_id_out, dladm_arg_list_t *proplist,
+    uint32_t flags)
 {
 	dladm_vnic_attr_t attr;
 	datalink_id_t vnic_id;
@@ -385,12 +435,31 @@
 
 	is_etherstub = (linkid == DATALINK_INVALID_LINKID);
 
-	if (mac_len > MAXMACADDRLEN)
-		return (DLADM_STATUS_INVALIDMACADDRLEN);
-
 	if (!dladm_vnic_macaddrtype2str(mac_addr_type))
 		return (DLADM_STATUS_INVALIDMACADDRTYPE);
 
+	if ((flags & DLADM_OPT_ANCHOR) == 0) {
+		if ((status = dladm_datalink_id2info(handle, linkid, NULL,
+		    &class, &media, NULL, 0)) != DLADM_STATUS_OK)
+			return (status);
+
+		if (class == DATALINK_CLASS_VNIC ||
+		    class == DATALINK_CLASS_VLAN)
+			return (DLADM_STATUS_BADARG);
+	} else {
+		/* it's an anchor VNIC */
+		if (linkid != DATALINK_INVALID_LINKID || vid != 0)
+			return (DLADM_STATUS_BADARG);
+	}
+
+	/*
+	 * Only VRRP VNIC need VRID and address family specified.
+	 */
+	if (mac_addr_type != VNIC_MAC_ADDR_TYPE_VRID &&
+	    (af != AF_UNSPEC || vrid != VRRP_VRID_NONE)) {
+		return (DLADM_STATUS_BADARG);
+	}
+
 	/*
 	 * If a random address might be generated, but no prefix
 	 * was specified by the caller, use the default MAC address
@@ -404,19 +473,30 @@
 		bcopy(dladm_vnic_def_prefix, mac_addr, mac_prefix_len);
 	}
 
-	if ((flags & DLADM_OPT_ANCHOR) == 0) {
-		if ((status = dladm_datalink_id2info(handle, linkid, NULL,
-		    &class, &media, NULL, 0)) != DLADM_STATUS_OK)
+	/*
+	 * If this is a VRRP VNIC, generate its MAC address using the given
+	 * VRID and address family.
+	 */
+	if (mac_addr_type == VNIC_MAC_ADDR_TYPE_VRID) {
+		/*
+		 * VRRP VNICs must be created over ethernet data-links.
+		 */
+		if (vrid < VRRP_VRID_MIN || vrid > VRRP_VRID_MAX ||
+		    (af != AF_INET && af != AF_INET6) || mac_addr != NULL ||
+		    mac_len != 0 || mac_prefix_len != 0 ||
+		    (mac_slot != NULL && *mac_slot != -1) || is_etherstub ||
+		    media != DL_ETHER) {
+			return (DLADM_STATUS_BADARG);
+		}
+		mac_len = ETHERADDRL;
+		mac_addr = tmp_addr;
+		status = i_dladm_vnic_vrrp_mac(vrid, af, mac_addr, mac_len);
+		if (status != DLADM_STATUS_OK)
 			return (status);
+	}
 
-		if (class == DATALINK_CLASS_VNIC ||
-		    class == DATALINK_CLASS_VLAN)
-			return (DLADM_STATUS_BADARG);
-	} else {
-		/* it's an anchor VNIC */
-		if (linkid != DATALINK_INVALID_LINKID || vid != 0)
-			return (DLADM_STATUS_BADARG);
-	}
+	if (mac_len > MAXMACADDRLEN)
+		return (DLADM_STATUS_INVALIDMACADDRLEN);
 
 	if (vnic == NULL) {
 		flags |= DLADM_OPT_PREFIX;
@@ -458,6 +538,8 @@
 		bcopy(mac_addr, attr.va_mac_addr, mac_prefix_len);
 	attr.va_mac_prefix_len = mac_prefix_len;
 	attr.va_vid = vid;
+	attr.va_vrid = vrid;
+	attr.va_af = af;
 	attr.va_force = (flags & DLADM_OPT_FORCE) != 0;
 	attr.va_hwrings = (flags & DLADM_OPT_HWRINGS) != 0;
 
@@ -619,6 +701,18 @@
 		if (status != DLADM_STATUS_OK)
 			goto done;
 
+		u64 = attrp->va_vrid;
+		status = dladm_set_conf_field(handle, conf, FVRID,
+		    DLADM_TYPE_UINT64, &u64);
+		if (status != DLADM_STATUS_OK)
+			goto done;
+
+		u64 = attrp->va_af;
+		status = dladm_set_conf_field(handle, conf, FVRAF,
+		    DLADM_TYPE_UINT64, &u64);
+		if (status != DLADM_STATUS_OK)
+			goto done;
+
 		if (attrp->va_mac_len != ETHERADDRL) {
 			u64 = attrp->va_mac_len;
 			status = dladm_set_conf_field(handle, conf, FMADDRLEN,
@@ -626,21 +720,11 @@
 			if (status != DLADM_STATUS_OK)
 				goto done;
 		}
-	}
 
-	if (attrp->va_hwrings) {
-		boolean_t hwrings = attrp->va_hwrings;
-		status = dladm_set_conf_field(handle, conf, FHWRINGS,
-		    DLADM_TYPE_BOOLEAN, &hwrings);
-		if (status != DLADM_STATUS_OK)
-			goto done;
-	}
-
-	if (class != DATALINK_CLASS_VLAN) {
 		if (attrp->va_mac_slot != -1) {
 			u64 = attrp->va_mac_slot;
-			status = dladm_set_conf_field(handle, conf, FMADDRSLOT,
-			    DLADM_TYPE_UINT64, &u64);
+			status = dladm_set_conf_field(handle, conf,
+			    FMADDRSLOT, DLADM_TYPE_UINT64, &u64);
 			if (status != DLADM_STATUS_OK)
 			goto done;
 		}
@@ -661,6 +745,14 @@
 			goto done;
 	}
 
+	if (attrp->va_hwrings) {
+		boolean_t hwrings = attrp->va_hwrings;
+		status = dladm_set_conf_field(handle, conf, FHWRINGS,
+		    DLADM_TYPE_BOOLEAN, &hwrings);
+		if (status != DLADM_STATUS_OK)
+			goto done;
+	}
+
 	if (attrp->va_vid != 0) {
 		u64 = attrp->va_vid;
 		status = dladm_set_conf_field(handle, conf, FVLANID,
--- a/usr/src/lib/libdladm/common/libdlvnic.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libdladm/common/libdlvnic.h	Tue Nov 17 09:17:48 2009 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -48,13 +48,15 @@
 	uint16_t		va_vid;
 	boolean_t		va_force;
 	boolean_t		va_hwrings;
+	vrid_t			va_vrid;
+	int			va_af;
 	mac_resource_props_t	va_resource_props;
 } dladm_vnic_attr_t;
 
 extern dladm_status_t	dladm_vnic_create(dladm_handle_t, const char *,
 			    datalink_id_t, vnic_mac_addr_type_t, uchar_t *,
-			    int, int *, uint_t, uint16_t, datalink_id_t *,
-			    dladm_arg_list_t *, uint32_t);
+			    uint_t, int *, uint_t, uint16_t, vrid_t, int,
+			    datalink_id_t *, dladm_arg_list_t *, uint32_t);
 
 extern dladm_status_t	dladm_vnic_delete(dladm_handle_t, datalink_id_t,
 			    uint32_t);
@@ -64,7 +66,6 @@
 extern dladm_status_t	dladm_vnic_up(dladm_handle_t, datalink_id_t, uint32_t);
 extern dladm_status_t	dladm_vnic_str2macaddrtype(const char *,
 			    vnic_mac_addr_type_t *);
-
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/libsecdb/auth_attr.txt	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libsecdb/auth_attr.txt	Tue Nov 17 09:17:48 2009 -0800
@@ -97,6 +97,7 @@
 solaris.network.link.security:::Link Security::help=LinkSecurity.html
 solaris.network.wifi.config:::Wifi Config::help=WifiConfig.html
 solaris.network.wifi.wep:::Wifi Wep::help=WifiWep.html
+solaris.network.vrrp:::Administer VRRP::help=NetworkVRRP.html
 #
 solaris.print.:::Printer Management::help=PrintHeader.html
 solaris.print.admin:::Administer Printer::help=PrintAdmin.html
--- a/usr/src/lib/libsecdb/help/auths/Makefile	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libsecdb/help/auths/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -124,6 +124,7 @@
 	NetworkILBconf.html \
 	NetworkILBenable.html \
 	NetworkHeader.html \
+	NetworkVRRP.html \
 	WifiConfig.html \
 	WifiWep.html \
 	LinkSecurity.html \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsecdb/help/auths/NetworkVRRP.html	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,41 @@
+<html>
+
+<!--
+    Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+    Use is subject to license terms.
+
+    CDDL HEADER START
+
+    The contents of this file are subject to the terms of the
+    Common Development and Distribution License (the "License").
+    You may not use this file except in compliance with the License.
+
+    You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+    or http://www.opensolaris.org/os/licensing.
+    See the License for the specific language governing permissions
+    and limitations under the License.
+
+    When distributing Covered Code, include this CDDL HEADER in each
+    file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+    If applicable, add the following below this CDDL HEADER, with the
+    fields enclosed by brackets "[]" replaced with your own identifying
+    information: Portions Copyright [yyyy] [name of copyright owner]
+
+    CDDL HEADER END
+-->
+
+<head>
+<!--
+meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+-->
+</head>
+<body>
+When Administer VRRP is in the Authorizations Included column, it grants
+authorization to administrate the VRRP service, which is provided by the
+vrrpd(1M) daemon.  In particularly, the user is allowed to create, delete,
+modify, disable, re-enable VRRP routers using the vrrpadm(1M) command.
+<p>
+Note, querying configuration and state information of a VRRP router using
+vrrpadm(1M) doesn't require the Administer VRRP authorization.
+</body>
+</html>
--- a/usr/src/lib/libsecdb/help/profiles/Makefile	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libsecdb/help/profiles/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -68,6 +68,7 @@
 	RtNetWifiMngmnt.html \
 	RtNetWifiSecure.html \
 	RtNetLinkSecure.html \
+	RtNetVRRP.html \
 	RtObAccessMngmnt.html \
 	RtPrntAdmin.html \
 	RtProcManagement.html \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsecdb/help/profiles/RtNetVRRP.html	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,36 @@
+<HTML>
+<!--
+    CDDL HEADER START
+
+    The contents of this file are subject to the terms of the
+    Common Development and Distribution License (the "License").
+    You may not use this file except in compliance with the License.
+
+    You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+    or http://www.opensolaris.org/os/licensing.
+    See the License for the specific language governing permissions
+    and limitations under the License.
+
+    When distributing Covered Code, include this CDDL HEADER in each
+    file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+    If applicable, add the following below this CDDL HEADER, with the
+    fields enclosed by brackets "[]" replaced with your own identifying
+    information: Portions Copyright [yyyy] [name of copyright owner]
+
+    CDDL HEADER END
+
+-- Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+-- Use is subject to license terms.
+-->
+<head>
+<title></title>
+</head>
+<body>
+When Network VRRP is in the Rights Included column, it grants the
+right to administer the VRRP routers using the vrrpadm(1M) command.
+<p>
+If Network VRRP is grayed, then you are not entitled to Add or
+Remove this right.
+<p>
+</body>
+</html>
--- a/usr/src/lib/libsecdb/prof_attr.txt	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/lib/libsecdb/prof_attr.txt	Tue Nov 17 09:17:48 2009 -0800
@@ -63,7 +63,8 @@
 NDMP Management:::Manage the NDMP service:auths=solaris.smf.manage.ndmp,solaris.smf.value.ndmp,solaris.smf.read.ndmp;help=RtNdmpMngmnt.html
 Network Autoconf:::Manage network auto-magic configuration via nwamd:auths=solaris.network.autoconf;help=RtNetAutoconf.html
 Network ILB:::Manage ILB configuration via ilbadm:auths=solaris.network.ilb.config,solaris.network.ilb.enable;help=RtNetILB.html
-Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns,solaris.smf.manage.ilb;profiles=Network Wifi Management,Inetd Management,Network Autoconf,Network Observability;help=RtNetMngmnt.html
+Network VRRP:::Manage VRRP instances:auths=solaris.network.vrrp;help=RtNetVRRP.html
+Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns,solaris.smf.manage.ilb;profiles=Network Wifi Management,Inetd Management,Network Autoconf,Network VRRP,Network Observability;help=RtNetMngmnt.html
 Network Observability:::Allow access to observability devices:privs=net_observability;help=RtNetObservability.html
 Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh,solaris.smf.value.tnd;profiles=Network Wifi Security,Network Link Security,Network IPsec Management;help=RtNetSecure.html
 Network Wifi Management:::Manage wifi network configuration:auths=solaris.network.wifi.config;help=RtNetWifiMngmnt.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,63 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include $(SRC)/lib/Makefile.lib
+
+HDRS =		netinet/vrrp.h libvrrpadm.h
+HDRDIR =	common
+
+SUBDIRS =	$(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+POFILE =	libvrrpadm.po
+MSGFILES =      common/libvrrpadm.c
+
+XGETFLAGS =	-a -x libvrrpadm.xcl
+
+all :=		TARGET = all
+clean :=	TARGET = clean
+clobber :=	TARGET = clobber
+install :=	TARGET = install
+lint :=		TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h:	$(ROOTHDRS)
+
+$(POFILE):	pofile_MSGFILES
+
+_msg:		$(MSGDOMAINPOFILE)
+
+check:		$(CHECKHDRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include $(SRC)/lib/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/Makefile.com	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY = libvrrpadm.a
+VERS    = .1
+OBJECTS = libvrrpadm.o
+
+include ../../Makefile.lib
+
+# install this library in the root filesystem
+#include ../../Makefile.rootfs
+
+LIBS =		$(DYNLIB) $(LINTLIB)
+LDLIBS +=	-lc -lsocket -ldladm
+
+SRCDIR =	../common
+$(LINTLIB) :=	SRCS = $(SRCDIR)/$(LINTSRC)
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-I$(SRCDIR) -D_REENTRANT
+
+C99MODE =	$(C99_ENABLE)
+
+.KEEP_STATE:
+
+all:		$(LIBS)
+
+lint:		lintcheck
+
+include $(SRC)/lib/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/amd64/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/common/libvrrpadm.c	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,635 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/varargs.h>
+#include <sys/vlan.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>	/* LIFNAMSIZ */
+#include <netinet/vrrp.h>
+#include <libdladm.h>
+#include <libdlvnic.h>
+#include <libdlvlan.h>
+#include <libdllink.h>
+#include <libintl.h>
+#include <libvrrpadm.h>
+
+typedef vrrp_err_t vrrp_cmd_func_t(int, void *);
+
+static vrrp_err_t
+vrrp_cmd_request(void *cmd, size_t csize, vrrp_cmd_func_t func, void *arg)
+{
+	struct sockaddr_un	to;
+	int			sock, flags;
+	size_t			len, cur_size = 0;
+	vrrp_ret_t		ret;
+	vrrp_err_t		err;
+
+	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+		return (VRRP_ECMD);
+
+	/*
+	 * Set it to be non-blocking.
+	 */
+	flags = fcntl(sock, F_GETFL, 0);
+	(void) fcntl(sock, F_SETFL, (flags | O_NONBLOCK));
+
+	(void) memset(&to, 0, sizeof (to));
+	to.sun_family = AF_UNIX;
+	(void) strlcpy(to.sun_path, VRRPD_SOCKET, sizeof (to.sun_path));
+
+	/*
+	 * Connect to vrrpd
+	 */
+	if (connect(sock, (const struct sockaddr *)&to, sizeof (to)) < 0) {
+		(void) close(sock);
+		return (VRRP_ECMD);
+	}
+
+	/*
+	 * Send the request
+	 */
+	while (cur_size < csize) {
+		len = write(sock, (char *)cmd + cur_size, csize - cur_size);
+		if (len == (size_t)-1 && errno == EAGAIN) {
+			continue;
+		} else if (len > 0) {
+			cur_size += len;
+			continue;
+		}
+		(void) close(sock);
+		return (VRRP_ECMD);
+	}
+
+	/*
+	 * Expect the ack, first get the error code.
+	 */
+	cur_size = 0;
+	while (cur_size < sizeof (vrrp_err_t)) {
+		len = read(sock, (char *)&ret + cur_size,
+		    sizeof (vrrp_err_t) - cur_size);
+
+		if (len == (size_t)-1 && errno == EAGAIN) {
+			continue;
+		} else if (len > 0) {
+			cur_size += len;
+			continue;
+		}
+		(void) close(sock);
+		return (VRRP_ECMD);
+	}
+
+	if ((err = ret.vr_err) != VRRP_SUCCESS)
+		goto done;
+
+	/*
+	 * The specific callback gets the rest of the information.
+	 */
+	if (func != NULL)
+		err = func(sock, arg);
+
+done:
+	(void) close(sock);
+	return (err);
+}
+
+/*
+ * public APIs
+ */
+const char *
+vrrp_err2str(vrrp_err_t err)
+{
+	switch (err) {
+	case VRRP_SUCCESS:
+		return (dgettext(TEXT_DOMAIN, "success"));
+	case VRRP_ENOMEM:
+		return (dgettext(TEXT_DOMAIN, "not enough memory"));
+	case VRRP_EINVALVRNAME:
+		return (dgettext(TEXT_DOMAIN, "invalid router name"));
+	case VRRP_ENOPRIM:
+		return (dgettext(TEXT_DOMAIN, "no primary IP"));
+	case VRRP_EEXIST:
+		return (dgettext(TEXT_DOMAIN, "already exists"));
+	case VRRP_ENOVIRT:
+		return (dgettext(TEXT_DOMAIN, "no virtual IPs"));
+	case VRRP_EIPADM:
+		return (dgettext(TEXT_DOMAIN, "ip configuration failure"));
+	case VRRP_EDLADM:
+		return (dgettext(TEXT_DOMAIN, "data-link configuration "
+		    "failure"));
+	case VRRP_EDB:
+		return (dgettext(TEXT_DOMAIN, "configuration update error"));
+	case VRRP_EBADSTATE:
+		return (dgettext(TEXT_DOMAIN, "invalid state"));
+	case VRRP_EVREXIST:
+		return (dgettext(TEXT_DOMAIN, "VRRP router already exists"));
+	case VRRP_ETOOSMALL:
+		return (dgettext(TEXT_DOMAIN, "not enough space"));
+	case VRRP_EINSTEXIST:
+		return (dgettext(TEXT_DOMAIN, "router name already exists"));
+	case VRRP_ENOTFOUND:
+		return (dgettext(TEXT_DOMAIN, "VRRP router not found"));
+	case VRRP_ECMD:
+		return (dgettext(TEXT_DOMAIN, "failed to communicate to "
+		    "vrrpd"));
+	case VRRP_EINVALADDR:
+		return (dgettext(TEXT_DOMAIN, "invalid IP address"));
+	case VRRP_EINVALAF:
+		return (dgettext(TEXT_DOMAIN, "invalid IP address family"));
+	case VRRP_EINVALLINK:
+		return (dgettext(TEXT_DOMAIN, "invalid data-link"));
+	case VRRP_EPERM:
+		return (dgettext(TEXT_DOMAIN, "permission denied"));
+	case VRRP_ESYS:
+		return (dgettext(TEXT_DOMAIN, "system error"));
+	case VRRP_EAGAIN:
+		return (dgettext(TEXT_DOMAIN, "try again"));
+	case VRRP_EALREADY:
+		return (dgettext(TEXT_DOMAIN, "operation already in progress"));
+	case VRRP_ENOVNIC:
+		return (dgettext(TEXT_DOMAIN, "VRRP VNIC has not been "
+		    "created"));
+	case VRRP_ENOLINK:
+		return (dgettext(TEXT_DOMAIN, "the data-link does not exist"));
+	case VRRP_EINVAL:
+	default:
+		return (dgettext(TEXT_DOMAIN, "invalid argument"));
+	}
+}
+
+const char *
+vrrp_state2str(vrrp_state_t state)
+{
+	switch (state) {
+	case VRRP_STATE_NONE:
+		return (dgettext(TEXT_DOMAIN, "NONE"));
+	case VRRP_STATE_INIT:
+		return (dgettext(TEXT_DOMAIN, "INIT"));
+	case VRRP_STATE_MASTER:
+		return (dgettext(TEXT_DOMAIN, "MASTER"));
+	case VRRP_STATE_BACKUP:
+		return (dgettext(TEXT_DOMAIN, "BACKUP"));
+	default:
+		return (dgettext(TEXT_DOMAIN, "INVALID"));
+	}
+}
+
+vrrp_err_t
+vrrp_open(vrrp_handle_t *vh)
+{
+	dladm_handle_t	dh;
+
+	if (dladm_open(&dh) != DLADM_STATUS_OK)
+		return (VRRP_EDLADM);
+
+	if ((*vh = malloc(sizeof (struct vrrp_handle))) == NULL) {
+		dladm_close(dh);
+		return (VRRP_ENOMEM);
+	}
+	(*vh)->vh_dh = dh;
+	return (VRRP_SUCCESS);
+}
+
+void
+vrrp_close(vrrp_handle_t vh)
+{
+	if (vh != NULL) {
+		dladm_close(vh->vh_dh);
+		free(vh);
+	}
+}
+
+boolean_t
+vrrp_valid_name(const char *name)
+{
+	const char	*c;
+
+	/*
+	 * The legal characters in a valid router name are:
+	 * alphanumeric (a-z,  A-Z,  0-9), underscore ('_'), and '.'.
+	 */
+	for (c = name; *c != '\0'; c++) {
+		if ((isalnum(*c) == 0) && (*c != '_'))
+			return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*ARGSUSED*/
+vrrp_err_t
+vrrp_create(vrrp_handle_t vh, vrrp_vr_conf_t *conf)
+{
+	vrrp_cmd_create_t	cmd;
+	vrrp_err_t		err;
+
+	cmd.vcc_cmd = VRRP_CMD_CREATE;
+	(void) memcpy(&cmd.vcc_conf, conf, sizeof (vrrp_vr_conf_t));
+
+	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
+	return (err);
+}
+
+/*ARGSUSED*/
+vrrp_err_t
+vrrp_delete(vrrp_handle_t vh, const char *vn)
+{
+	vrrp_cmd_delete_t	cmd;
+	vrrp_err_t		err;
+
+	cmd.vcd_cmd = VRRP_CMD_DELETE;
+	if (strlcpy(cmd.vcd_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
+		return (VRRP_EINVAL);
+
+	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
+	return (err);
+}
+
+/*ARGSUSED*/
+vrrp_err_t
+vrrp_enable(vrrp_handle_t vh, const char *vn)
+{
+	vrrp_cmd_enable_t	cmd;
+	vrrp_err_t		err;
+
+	cmd.vcs_cmd = VRRP_CMD_ENABLE;
+	if (strlcpy(cmd.vcs_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
+		return (VRRP_EINVAL);
+
+	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
+	return (err);
+}
+
+/*ARGSUSED*/
+vrrp_err_t
+vrrp_disable(vrrp_handle_t vh, const char *vn)
+{
+	vrrp_cmd_disable_t	cmd;
+	vrrp_err_t		err;
+
+	cmd.vcx_cmd = VRRP_CMD_DISABLE;
+	if (strlcpy(cmd.vcx_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
+		return (VRRP_EINVAL);
+
+	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
+	return (err);
+}
+
+/*ARGSUSED*/
+vrrp_err_t
+vrrp_modify(vrrp_handle_t vh, vrrp_vr_conf_t *conf, uint32_t mask)
+{
+	vrrp_cmd_modify_t	cmd;
+	vrrp_err_t		err;
+
+	cmd.vcm_cmd = VRRP_CMD_MODIFY;
+	cmd.vcm_mask = mask;
+	(void) memcpy(&cmd.vcm_conf, conf, sizeof (vrrp_vr_conf_t));
+
+	err = vrrp_cmd_request(&cmd, sizeof (cmd), NULL, NULL);
+	return (err);
+}
+
+typedef struct vrrp_cmd_list_arg {
+	uint32_t	*vfl_cnt;
+	char		*vfl_names;
+} vrrp_cmd_list_arg_t;
+
+static vrrp_err_t
+vrrp_list_func(int sock, void *arg)
+{
+	vrrp_cmd_list_arg_t	*list_arg = arg;
+	uint32_t		in_cnt = *(list_arg->vfl_cnt);
+	uint32_t		out_cnt;
+	vrrp_ret_list_t		ret;
+	size_t			len, cur_size = 0;
+
+	/*
+	 * Get the rest of vrrp_ret_list_t besides the error code.
+	 */
+	cur_size = sizeof (vrrp_err_t);
+	while (cur_size < sizeof (vrrp_ret_list_t)) {
+		len = read(sock, (char *)&ret + cur_size,
+		    sizeof (vrrp_ret_list_t) - cur_size);
+
+		if (len == (size_t)-1 && errno == EAGAIN) {
+			continue;
+		} else if (len > 0) {
+			cur_size += len;
+			continue;
+		}
+		return (VRRP_ECMD);
+	}
+
+	*(list_arg->vfl_cnt) = out_cnt = ret.vrl_cnt;
+	out_cnt = (in_cnt <= out_cnt) ? in_cnt : out_cnt;
+	cur_size = 0;
+
+	while (cur_size < VRRP_NAME_MAX * out_cnt) {
+		len = read(sock, (char *)list_arg->vfl_names + cur_size,
+		    VRRP_NAME_MAX * out_cnt - cur_size);
+
+		if (len == (size_t)-1 && errno == EAGAIN) {
+			continue;
+		} else if (len > 0) {
+			cur_size += len;
+			continue;
+		}
+		return (VRRP_ECMD);
+	}
+	return (VRRP_SUCCESS);
+}
+
+/*
+ * Looks up the vrrp instances that matches the given variable.
+ *
+ * If the given cnt is 0, names should be set to NULL. In this case, only
+ * the count of the matched instances is returned.
+ *
+ * If the given cnt is non-zero, caller must allocate "names" whose size
+ * is (cnt * VRRP_NAME_MAX).
+ *
+ * Return value: the current count of matched instances, and names will be
+ * points to the list of the current vrrp instances names. Note that
+ * only MIN(in_cnt, out_cnt) number of names will be returned.
+ */
+/*ARGSUSED*/
+vrrp_err_t
+vrrp_list(vrrp_handle_t vh, vrid_t vrid, const char *intf, int af,
+    uint32_t *cnt, char *names)
+{
+	vrrp_cmd_list_t		cmd;
+	vrrp_err_t		err;
+	vrrp_cmd_list_arg_t	list_arg;
+
+	if ((cnt == NULL) || (*cnt != 0 && names == NULL))
+		return (VRRP_EINVAL);
+
+	cmd.vcl_ifname[0] = '\0';
+	if (intf != NULL && (strlcpy(cmd.vcl_ifname, intf,
+	    LIFNAMSIZ) >= LIFNAMSIZ)) {
+		return (VRRP_EINVAL);
+	}
+
+	cmd.vcl_cmd = VRRP_CMD_LIST;
+	cmd.vcl_vrid = vrid;
+	cmd.vcl_af = af;
+
+	list_arg.vfl_cnt = cnt;
+	list_arg.vfl_names = names;
+
+	err = vrrp_cmd_request(&cmd, sizeof (cmd), vrrp_list_func, &list_arg);
+	return (err);
+}
+
+static vrrp_err_t
+vrrp_query_func(int sock, void *arg)
+{
+	vrrp_queryinfo_t	*qinfo = arg;
+	size_t			len, cur_size = 0, total;
+	uint32_t		in_cnt = qinfo->show_va.va_vipcnt;
+	uint32_t		out_cnt;
+
+	/*
+	 * Expect the ack, first get the vrrp_ret_t.
+	 */
+	total = sizeof (vrrp_queryinfo_t);
+	while (cur_size < total) {
+		len = read(sock, (char *)qinfo + cur_size, total - cur_size);
+		if (len == (size_t)-1 && errno == EAGAIN) {
+			continue;
+		} else if (len > 0) {
+			cur_size += len;
+			continue;
+		}
+		return (VRRP_ECMD);
+	}
+
+	out_cnt = qinfo->show_va.va_vipcnt;
+
+	/*
+	 * Even if there is no IP virtual IP address, there is always
+	 * space in the vrrp_queryinfo_t structure for one virtual
+	 * IP address.
+	 */
+	out_cnt = (out_cnt == 0) ? 1 : out_cnt;
+	out_cnt = (in_cnt < out_cnt ? in_cnt : out_cnt) - 1;
+	total += out_cnt * sizeof (vrrp_addr_t);
+
+	while (cur_size < total) {
+		len = read(sock, (char *)qinfo + cur_size, total - cur_size);
+		if (len == (size_t)-1 && errno == EAGAIN) {
+			continue;
+		} else if (len > 0) {
+			cur_size += len;
+			continue;
+		}
+		return (VRRP_ECMD);
+	}
+	return (VRRP_SUCCESS);
+}
+
+/*
+ * *vqp is allocated inside this function and must be freed by the caller.
+ */
+/*ARGSUSED*/
+vrrp_err_t
+vrrp_query(vrrp_handle_t vh, const char *vn, vrrp_queryinfo_t **vqp)
+{
+	vrrp_cmd_query_t	cmd;
+	vrrp_queryinfo_t	*qinfo;
+	vrrp_err_t		err;
+	size_t			size;
+	uint32_t		vipcnt = 1;
+
+	if (strlcpy(cmd.vcq_name, vn, VRRP_NAME_MAX) >= VRRP_NAME_MAX)
+		return (VRRP_EINVAL);
+
+	cmd.vcq_cmd = VRRP_CMD_QUERY;
+
+	/*
+	 * Allocate enough room for virtual IPs.
+	 */
+again:
+	size = sizeof (vrrp_queryinfo_t);
+	size += (vipcnt == 0) ? 0 : (vipcnt - 1) * sizeof (vrrp_addr_t);
+	if ((qinfo = malloc(size)) == NULL) {
+		err = VRRP_ENOMEM;
+		goto done;
+	}
+
+	qinfo->show_va.va_vipcnt = vipcnt;
+	err = vrrp_cmd_request(&cmd, sizeof (cmd), vrrp_query_func, qinfo);
+	if (err != VRRP_SUCCESS) {
+		free(qinfo);
+		goto done;
+	}
+
+	/*
+	 * If the returned number of virtual IPs is greater than we expected,
+	 * allocate more room and try again.
+	 */
+	if (qinfo->show_va.va_vipcnt > vipcnt) {
+		vipcnt = qinfo->show_va.va_vipcnt;
+		free(qinfo);
+		goto again;
+	}
+
+	*vqp = qinfo;
+
+done:
+	return (err);
+}
+
+struct lookup_vnic_arg {
+	vrid_t		lva_vrid;
+	datalink_id_t	lva_linkid;
+	int		lva_af;
+	uint16_t	lva_vid;
+	vrrp_handle_t	lva_vh;
+	char		lva_vnic[MAXLINKNAMELEN];
+};
+
+/*
+ * Is this a special VNIC interface created for VRRP? If so, return
+ * the linkid the VNIC was created on, the VRRP ID and address family.
+ */
+boolean_t
+vrrp_is_vrrp_vnic(vrrp_handle_t vh, datalink_id_t vnicid,
+    datalink_id_t *linkidp, uint16_t *vidp, vrid_t *vridp, int *afp)
+{
+	dladm_vnic_attr_t	vattr;
+
+	if (dladm_vnic_info(vh->vh_dh, vnicid, &vattr, DLADM_OPT_ACTIVE) !=
+	    DLADM_STATUS_OK) {
+		return (B_FALSE);
+	}
+
+	*vridp = vattr.va_vrid;
+	*vidp = vattr.va_vid;
+	*afp = vattr.va_af;
+	*linkidp = vattr.va_link_id;
+	return (vattr.va_vrid != VRRP_VRID_NONE);
+}
+
+static int
+lookup_vnic(dladm_handle_t dh, datalink_id_t vnicid, void *arg)
+{
+	vrid_t			vrid;
+	uint16_t		vid;
+	datalink_id_t		linkid;
+	int			af;
+	struct lookup_vnic_arg	*lva = arg;
+
+	if (vrrp_is_vrrp_vnic(lva->lva_vh, vnicid, &linkid, &vid, &vrid,
+	    &af) && lva->lva_vrid == vrid && lva->lva_linkid == linkid &&
+	    lva->lva_vid == vid && lva->lva_af == af) {
+		if (dladm_datalink_id2info(dh, vnicid, NULL, NULL, NULL,
+		    lva->lva_vnic, sizeof (lva->lva_vnic)) == DLADM_STATUS_OK) {
+			return (DLADM_WALK_TERMINATE);
+		}
+	}
+	return (DLADM_WALK_CONTINUE);
+}
+
+/*
+ * Given the primary link name, find the assoicated VRRP vnic name, if
+ * the vnic does not exist yet, return the linkid, vid of the primary link.
+ */
+vrrp_err_t
+vrrp_get_vnicname(vrrp_handle_t vh, vrid_t vrid, int af, char *link,
+    datalink_id_t *linkidp, uint16_t *vidp, char *vnic, size_t len)
+{
+	datalink_id_t		linkid;
+	uint32_t		flags;
+	uint16_t		vid = VLAN_ID_NONE;
+	datalink_class_t	class;
+	dladm_vlan_attr_t	vlan_attr;
+	struct lookup_vnic_arg	lva;
+	uint32_t		media;
+
+	if ((strlen(link) == 0) || dladm_name2info(vh->vh_dh,
+	    link, &linkid, &flags, &class, &media) !=
+	    DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) {
+		return (VRRP_EINVAL);
+	}
+
+	if (class == DATALINK_CLASS_VLAN) {
+		if (dladm_vlan_info(vh->vh_dh, linkid, &vlan_attr,
+		    DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
+			return (VRRP_EINVAL);
+		}
+		linkid = vlan_attr.dv_linkid;
+		vid = vlan_attr.dv_vid;
+		if ((dladm_datalink_id2info(vh->vh_dh, linkid, NULL,
+		    &class, &media, NULL, 0)) != DLADM_STATUS_OK) {
+			return (VRRP_EINVAL);
+		}
+	}
+
+	/*
+	 * For now, Only VRRP over aggr and physical ethernet links is supported
+	 */
+	if ((class != DATALINK_CLASS_PHYS && class != DATALINK_CLASS_AGGR) ||
+	    media != DL_ETHER) {
+		return (VRRP_EINVAL);
+	}
+
+	if (linkidp != NULL)
+		*linkidp = linkid;
+	if (vidp != NULL)
+		*vidp = vid;
+
+	/*
+	 * Find the assoicated vnic with the given vrid/vid/af/linkid
+	 */
+	lva.lva_vrid = vrid;
+	lva.lva_vid = vid;
+	lva.lva_af = af;
+	lva.lva_linkid = linkid;
+	lva.lva_vh = vh;
+	lva.lva_vnic[0] = '\0';
+
+	(void) dladm_walk_datalink_id(lookup_vnic, vh->vh_dh, &lva,
+	    DATALINK_CLASS_VNIC, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+	if (strlen(lva.lva_vnic) != 0) {
+		(void) strlcpy(vnic, lva.lva_vnic, len);
+		return (VRRP_SUCCESS);
+	}
+
+	return (VRRP_ENOVNIC);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/common/libvrrpadm.h	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,355 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_LIBVRRPADM_H
+#define	_LIBVRRPADM_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>		/* in(6)_addr_t */
+#include <arpa/inet.h>
+#include <net/if.h>		/* LIFNAMSIZ */
+#include <limits.h>
+#include <netinet/vrrp.h>
+#include <syslog.h>
+#include <libdladm.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	VRRP_NAME_MAX	32
+#define	VRRPD_SOCKET	"/var/run/vrrpd.socket"
+
+/*
+ * to store the IP addresses
+ */
+typedef struct vrrp_addr {
+	union {
+		struct sockaddr_in	a4;
+		struct sockaddr_in6	a6;
+	} in;
+#define	in4	in.a4
+#define	in6	in.a6
+} vrrp_addr_t;
+
+/*
+ * VRRP instance (configuration information).
+ * Passed to vrrp_create(), returned by vrrp_query().
+ */
+typedef struct vrrp_vr_conf_s {
+	char		vvc_name[VRRP_NAME_MAX];	/* VRRP router name */
+	char		vvc_link[MAXLINKNAMELEN];	/* data-link name */
+	vrid_t		vvc_vrid;			/* VRID */
+	int		vvc_af;				/* IPv4/IPv6 */
+	int		vvc_pri;
+	uint32_t	vvc_adver_int;			/* in ms */
+	boolean_t	vvc_preempt;
+	boolean_t	vvc_accept;
+	boolean_t	vvc_enabled;
+} vrrp_vr_conf_t;
+
+/*
+ * VRRP state machine
+ */
+typedef enum {
+	VRRP_STATE_NONE = -1,
+	VRRP_STATE_INIT,
+	VRRP_STATE_MASTER,
+	VRRP_STATE_BACKUP
+} vrrp_state_t;
+
+/*
+ * VRRP status structure
+ * Returned by vrrp_query() as part of vrrp_queryinfo_t.
+ */
+typedef struct vrrp_statusinfo_s {
+	vrrp_state_t	vs_state;
+	vrrp_state_t	vs_prev_state;
+	struct timeval	vs_st_time;	/* timestamp of last state trans */
+} vrrp_stateinfo_t;
+
+/*
+ * The information obtained from peer's advertisements
+ * Returned by vrrp_query() as part of vrrp_queryinfo_t.
+ */
+typedef struct vrrp_peer_s {
+	vrrp_addr_t	vp_addr;	/* source IP addr of the message */
+	int		vp_prio;	/* priority in adv message */
+	struct timeval	vp_time;	/* timestamp of the adv message */
+	int		vp_adver_int;	/* adv interval in adv message */
+} vrrp_peer_t;
+
+/*
+ * Useful timer information, in ms
+ */
+typedef struct vrrp_timeinfo_s {
+	int	vt_since_last_tran;	/* time since last state transition */
+	int	vt_since_last_adv;	/* time since last advertisement */
+	int	vt_master_down_intv;	/* timer interval for backup to */
+					/* declare master down */
+} vrrp_timerinfo_t;
+
+/*
+ * Address information
+ */
+typedef struct vrrp_addrinfo_s {
+	char		va_vnic[MAXLINKNAMELEN];
+	vrrp_addr_t	va_primary;
+	uint32_t	va_vipcnt;
+	vrrp_addr_t	va_vips[1];
+} vrrp_addrinfo_t;
+
+/*
+ * VRRP instance configuration and run-time states information
+ * Returned by vrrp_query().
+ */
+typedef struct vrrp_queryinfo {
+	vrrp_vr_conf_t		show_vi;
+	vrrp_stateinfo_t	show_vs;
+	vrrp_peer_t		show_vp;
+	vrrp_timerinfo_t	show_vt;
+	vrrp_addrinfo_t		show_va;
+} vrrp_queryinfo_t;
+
+/*
+ * flags sent with the VRRP_CMD_MODIFY command. Used in vrrp_setprop().
+ */
+#define	VRRP_CONF_PRIORITY	0x01
+#define	VRRP_CONF_INTERVAL	0x02
+#define	VRRP_CONF_PREEMPT	0x04
+#define	VRRP_CONF_ACCEPT	0x08
+
+/*
+ * Errors
+ */
+typedef enum {
+	VRRP_SUCCESS = 0,
+	VRRP_EINVAL,		/* invalid parameter */
+	VRRP_EINVALVRNAME,	/* invalid router name */
+	VRRP_ENOMEM,		/* no memory */
+	VRRP_ENOVIRT,		/* no virtual IP addresses */
+	VRRP_ENOPRIM,		/* no primary IP address */
+	VRRP_ENOVNIC,		/* no vnic created */
+	VRRP_ENOLINK,		/* the link does not exist */
+	VRRP_EINVALLINK,	/* invalid link */
+	VRRP_EINVALADDR,	/* invalid IP address */
+	VRRP_EINVALAF,		/* invalid IP address familty */
+	VRRP_EDB,		/* configuration error */
+	VRRP_EPERM,		/* permission denied */
+	VRRP_EBADSTATE,		/* VRRP router in bad state */
+	VRRP_EVREXIST,		/* <vrid, intf, af> three-tuple exists */
+	VRRP_EINSTEXIST,	/* router name already exists */
+	VRRP_EEXIST,		/* already exists */
+	VRRP_ENOTFOUND,		/* vrrp router not found */
+	VRRP_ETOOSMALL,		/* too small space */
+	VRRP_EAGAIN,		/* Try again */
+	VRRP_EALREADY,		/* already */
+	VRRP_EDLADM,		/* dladm failure */
+	VRRP_EIPADM,		/* ipadm failure */
+	VRRP_ESYS,		/* system error */
+	VRRP_ECMD		/* command request error */
+} vrrp_err_t;
+
+/*
+ * Internal commands used between vrrpadm and vrrpd.
+ */
+typedef enum {
+	VRRP_CMD_RETURN = 0,
+	VRRP_CMD_CREATE,
+	VRRP_CMD_DELETE,
+	VRRP_CMD_ENABLE,
+	VRRP_CMD_DISABLE,
+	VRRP_CMD_MODIFY,
+	VRRP_CMD_LIST,
+	VRRP_CMD_QUERY
+} vrrp_cmd_type_t;
+
+#define	addr_len(af) ((af) == AF_INET ? sizeof (in_addr_t): sizeof (in6_addr_t))
+
+#define	VRRPADDR_UNSPECIFIED(af, addr) 					\
+	(((af) == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(			\
+	    &(addr)->in6.sin6_addr)) || ((af) == AF_INET &&		\
+	    ((addr)->in4.sin_addr.s_addr == INADDR_ANY)))
+
+#define	VRRPADDR2STR(af, addr, abuf, size, append) {			\
+	char ap[INET6_ADDRSTRLEN];					\
+									\
+	if (VRRPADDR_UNSPECIFIED(af, addr)) {				\
+		(void) strlcpy(ap, "--", INET6_ADDRSTRLEN);		\
+	} else if ((af) == AF_INET) {					\
+		(void) inet_ntop((af), &(addr)->in4.sin_addr, ap,	\
+		    INET6_ADDRSTRLEN);					\
+	} else {							\
+		(void) inet_ntop((af), &(addr)->in6.sin6_addr, ap,	\
+		    INET6_ADDRSTRLEN);					\
+	}								\
+	if (append)							\
+		(void) strlcat(abuf, ap, size);				\
+	else								\
+		(void) strlcpy(abuf, ap, size);				\
+}
+
+typedef struct vrrp_cmd_create_s {
+	uint32_t	vcc_cmd;
+	vrrp_vr_conf_t	vcc_conf;
+} vrrp_cmd_create_t;
+
+typedef struct vrrp_ret_create_s {
+	vrrp_err_t	vrc_err;
+} vrrp_ret_create_t;
+
+typedef struct vrrp_cmd_delete_s {
+	uint32_t	vcd_cmd;
+	char		vcd_name[VRRP_NAME_MAX];
+} vrrp_cmd_delete_t;
+
+typedef struct vrrp_ret_delete_s {
+	vrrp_err_t	vrd_err;
+} vrrp_ret_delete_t;
+
+typedef struct vrrp_cmd_enable_s {
+	uint32_t	vcs_cmd;
+	char		vcs_name[VRRP_NAME_MAX];
+} vrrp_cmd_enable_t;
+
+typedef struct vrrp_ret_enable_s {
+	vrrp_err_t	vrs_err;
+} vrrp_ret_enable_t;
+
+typedef struct vrrp_cmd_disable_s {
+	uint32_t	vcx_cmd;
+	char		vcx_name[VRRP_NAME_MAX];
+} vrrp_cmd_disable_t;
+
+typedef struct vrrp_ret_disable_s {
+	vrrp_err_t	vrx_err;
+} vrrp_ret_disable_t;
+
+typedef struct vrrp_cmd_modify_s {
+	uint32_t	vcm_cmd;
+	uint32_t	vcm_mask;
+	vrrp_vr_conf_t	vcm_conf;
+} vrrp_cmd_modify_t;
+
+typedef struct vrrp_ret_modify_s {
+	vrrp_err_t	vrm_err;
+} vrrp_ret_modify_t;
+
+typedef struct vrrp_cmd_list_s {
+	uint32_t	vcl_cmd;
+	vrid_t		vcl_vrid;
+	char		vcl_ifname[LIFNAMSIZ];
+	int		vcl_af;
+} vrrp_cmd_list_t;
+
+typedef struct vrrp_ret_list_s {
+	vrrp_err_t	vrl_err;
+	uint32_t	vrl_cnt;
+	/*
+	 * When vrl_cnt is non-zero, the return structure will be followed
+	 * by the list of router names, separated by '\0'. Its size will
+	 * be vrl_cnt * VRRP_NAME_MAX.
+	 */
+} vrrp_ret_list_t;
+
+typedef struct vrrp_cmd_query_s {
+	uint32_t	vcq_cmd;
+	char		vcq_name[VRRP_NAME_MAX];
+} vrrp_cmd_query_t;
+
+typedef struct vrrp_ret_query_s {
+	vrrp_err_t		vrq_err;
+	vrrp_queryinfo_t	vrq_qinfo;
+} vrrp_ret_query_t;
+
+/*
+ * Union of all VRRP commands
+ */
+typedef union vrrp_cmd_s {
+	uint32_t		vc_cmd;
+	vrrp_cmd_create_t	vc_cmd_create;
+	vrrp_cmd_delete_t	vc_cmd_delete;
+	vrrp_cmd_enable_t	vc_cmd_enable;
+	vrrp_cmd_disable_t	vc_cmd_disable;
+	vrrp_cmd_modify_t	vc_cmd_modify;
+	vrrp_cmd_list_t		vc_cmd_list;
+} vrrp_cmd_t;
+
+/*
+ * Union of all VRRP replies of the VRRP commands
+ */
+typedef union vrrp_ret_s {
+	vrrp_err_t		vr_err;
+	vrrp_ret_create_t	vr_ret_create;
+	vrrp_ret_delete_t	vr_ret_delete;
+	vrrp_ret_enable_t	vr_ret_enable;
+	vrrp_ret_disable_t	vr_ret_disable;
+	vrrp_ret_modify_t	vr_ret_modify;
+	vrrp_ret_list_t		vr_ret_list;
+	vrrp_ret_query_t	vr_ret_query;
+} vrrp_ret_t;
+
+/*
+ * Public APIs
+ */
+struct vrrp_handle {
+	dladm_handle_t	vh_dh;
+};
+typedef struct vrrp_handle *vrrp_handle_t;
+
+const char	*vrrp_err2str(vrrp_err_t);
+const char	*vrrp_state2str(vrrp_state_t);
+
+vrrp_err_t	vrrp_open(vrrp_handle_t *);
+void		vrrp_close(vrrp_handle_t);
+
+boolean_t	vrrp_valid_name(const char *);
+
+vrrp_err_t	vrrp_create(vrrp_handle_t, vrrp_vr_conf_t *);
+vrrp_err_t	vrrp_delete(vrrp_handle_t, const char *);
+
+vrrp_err_t	vrrp_enable(vrrp_handle_t, const char *);
+vrrp_err_t	vrrp_disable(vrrp_handle_t, const char *);
+
+vrrp_err_t	vrrp_modify(vrrp_handle_t, vrrp_vr_conf_t *, uint32_t);
+
+vrrp_err_t	vrrp_query(vrrp_handle_t, const char *, vrrp_queryinfo_t **);
+
+vrrp_err_t	vrrp_list(vrrp_handle_t, vrid_t, const char *, int,
+		    uint32_t *, char *);
+
+boolean_t	vrrp_is_vrrp_vnic(vrrp_handle_t, datalink_id_t,
+		    datalink_id_t *, uint16_t *, vrid_t *, int *);
+
+vrrp_err_t	vrrp_get_vnicname(vrrp_handle_t, vrid_t, int, char *,
+		    datalink_id_t *, uint16_t *, char *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _LIBVRRPADM_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/common/llib-lvrrpadm	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,30 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libvrrpadm.h>
+#include <netinet/vrrp.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/common/mapfile-vers	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,58 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate_1.1 {
+    global:
+	vrrp_close;
+	vrrp_create;
+	vrrp_delete;
+	vrrp_disable;
+	vrrp_enable;
+	vrrp_err2str;
+	vrrp_get_vnicname;
+	vrrp_is_vrrp_vnic;
+	vrrp_list;
+	vrrp_modify;
+	vrrp_open;
+	vrrp_query;
+	vrrp_state2str;
+	vrrp_valid_name;
+	
+    local:
+	*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/common/netinet/vrrp.h	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,77 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_VRRP_H
+#define	_VRRP_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct vrrp_pkt_s {
+	uint8_t		vp_vers_type;
+	uint8_t		vp_vrid;
+	uint8_t		vp_prio;
+	uint8_t		vp_ipnum;
+	uint16_t	vp_rsvd_adver_int;
+	uint16_t	vp_chksum;
+	/* then follows <vp_ipnum> IPvX addresses */
+	/* then follows NO authentification data */
+} vrrp_pkt_t;
+
+#define	IPPROTO_VRRP		112	/* IP protocol number */
+#define	VRRP_AUTH_LEN		0	/* XXX length of a chunk of Auth Data */
+
+#define	VRRP_IP_TTL		255	/* IPv4 TTL, IPv6 hop limit */
+#define	VRRP_VERSION		3	/* current version */
+#define	VRRP_PKT_ADVERT		1	/* packet type */
+#define	VRRP_VER_MASK		0xf0	/* version mask */
+#define	VRRP_TYPE_MASK		0x0f	/* packet type mask */
+
+#define	VRRP_PRI_OWNER		255	/* priority of IP address owner */
+#define	VRRP_PRI_MIN		1	/* minimum priority */
+#define	VRRP_PRIO_ZERO		0	/* stop participating VRRP */
+#define	VRRP_PRI_DEFAULT	VRRP_PRI_OWNER	/* default priority */
+
+#define	VRRP_VRID_NONE		0
+#define	VRRP_VRID_MIN		1
+#define	VRRP_VRID_MAX		255
+
+#define	CENTISEC2MSEC(centisec)	((centisec) * 10)
+#define	MSEC2CENTISEC(msec)	((msec) / 10)
+
+/* Max advertisement interval, in msec */
+#define	VRRP_MAX_ADVER_INT_MIN	CENTISEC2MSEC(1)
+#define	VRRP_MAX_ADVER_INT_MAX	CENTISEC2MSEC(4095)	/* (2^12 -1) */
+#define	VRRP_MAX_ADVER_INT_DFLT	CENTISEC2MSEC(100)	/* 1 sec */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _VRRP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/i386/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/libvrrpadm.xcl	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+msgid "VRRP"
+msgid "vrrpd"
+msgid "VNIC"
+msgid "NONE"
+msgid "INIT"
+msgid "MASTER"
+msgid "BACKUP"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/sparc/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libvrrpadm/sparcv9/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
--- a/usr/src/pkgdefs/Makefile	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/pkgdefs/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -523,6 +523,8 @@
 	SUNWusbu \
 	SUNWuwb \
 	SUNWvr \
+	SUNWvrrpr \
+	SUNWvrrpu \
 	SUNWvscankr \
 	SUNWvscanr \
 	SUNWvscanu \
--- a/usr/src/pkgdefs/SUNW0on/prototype_com	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com	Tue Nov 17 09:17:48 2009 -0800
@@ -297,6 +297,7 @@
 f none usr/lib/help/auths/locale/NetworkHeader.html 444 root bin
 f none usr/lib/help/auths/locale/NetworkILBconf.html 444 root bin
 f none usr/lib/help/auths/locale/NetworkILBenable.html 444 root bin
+f none usr/lib/help/auths/locale/NetworkVRRP.html 444 root bin
 f none usr/lib/help/auths/locale/WifiConfig.html 444 root bin
 f none usr/lib/help/auths/locale/WifiWep.html 444 root bin
 f none usr/lib/help/auths/locale/LinkSecurity.html 444 root bin
@@ -387,6 +388,7 @@
 f none usr/lib/help/profiles/locale/RtNetWifiMngmnt.html 444 root bin
 f none usr/lib/help/profiles/locale/RtNetWifiSecure.html 444 root bin
 f none usr/lib/help/profiles/locale/RtNetLinkSecure.html 444 root bin
+f none usr/lib/help/profiles/locale/RtNetVRRP.html 444 root bin
 f none usr/lib/help/profiles/locale/RtObAccessMngmnt.html 444 root bin
 f none usr/lib/help/profiles/locale/RtProcManagement.html 444 root bin
 f none usr/lib/help/profiles/locale/RtReparseMngmnt.html 444 root bin
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com	Tue Nov 17 09:17:48 2009 -0800
@@ -486,6 +486,7 @@
 f none usr/lib/help/auths/locale/C/NetworkHeader.html 444 root bin
 f none usr/lib/help/auths/locale/C/NetworkILBconf.html 444 root bin
 f none usr/lib/help/auths/locale/C/NetworkILBenable.html 444 root bin
+f none usr/lib/help/auths/locale/C/NetworkVRRP.html 444 root bin
 f none usr/lib/help/auths/locale/C/PriAdmin.html 444 root bin
 f none usr/lib/help/auths/locale/C/ProfmgrHeader.html 444 root bin
 f none usr/lib/help/auths/locale/C/RoleHeader.html 444 root bin
@@ -615,6 +616,7 @@
 f none usr/lib/help/profiles/locale/C/RtNetWifiMngmnt.html 444 root bin
 f none usr/lib/help/profiles/locale/C/RtNetWifiSecure.html 444 root bin
 f none usr/lib/help/profiles/locale/C/RtNetLinkSecure.html 444 root bin
+f none usr/lib/help/profiles/locale/C/RtNetVRRP.html 444 root bin
 f none usr/lib/help/profiles/locale/C/RtObAccessMngmnt.html 444 root bin
 f none usr/lib/help/profiles/locale/C/RtPrntAdmin.html 444 root bin
 f none usr/lib/help/profiles/locale/C/RtProcManagement.html 444 root bin
--- a/usr/src/pkgdefs/SUNWhea/prototype_com	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com	Tue Nov 17 09:17:48 2009 -0800
@@ -406,6 +406,7 @@
 f none usr/include/netinet/tcpip.h 644 root bin
 f none usr/include/netinet/udp.h 644 root bin
 f none usr/include/netinet/udp_var.h 644 root bin
+f none usr/include/netinet/vrrp.h 644 root bin
 d none usr/include/nfs 755 root bin
 f none usr/include/nfs/auth.h 644 root bin
 f none usr/include/nfs/export.h 644 root bin
@@ -1308,6 +1309,7 @@
 f none usr/include/sys/sysevent/dev.h 644 root bin
 f none usr/include/sys/sysevent/pwrctl.h 644 root bin
 f none usr/include/sys/sysevent/svm.h 644 root bin
+f none usr/include/sys/sysevent/vrrp.h 644 root bin
 f none usr/include/sys/sysevent.h 644 root bin
 f none usr/include/sys/sysevent_impl.h 644 root bin
 f none usr/include/sys/sysinfo.h 644 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpr/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+DATAFILES += i.manifest r.manifest
+
+all: $(FILES)
+
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpr/depend	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This package information file defines software dependencies associated
+# with the pkg.  You can define three types of pkg dependencies with this file:
+#	 P indicates a prerequisite for installation
+#	 I indicates an incompatible package
+#	 R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# 	(<arch>)<version>
+# 	(<arch>)<version>
+# 	...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar	Core Architecture, (Root)
+P SUNWcakr	Core Solaris Kernel Architecture (Root)
+P SUNWkvm	Core Architecture, (Kvm)
+P SUNWcsr	Core Solaris, (Root)
+P SUNWckr	Core Solaris Kernel (Root)
+P SUNWcnetr	Core Solaris Network Infrastructure (Root)
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcsd	Core Solaris Devices
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpr/pkginfo.tmpl	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWvrrpr"
+NAME="Solaris VRRP protocol (Root)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY="system"
+DESC="Solaris VRRP protocol service"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none preserve manifest"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
+#VSTOCK="<reserved by Release Engineering for package part #>"
+#ISTATES="<developer defined>"
+#RSTATES='<developer defined>'
+#ULIMIT="<developer defined>"
+#ORDER="<developer defined>"
+#PSTAMP="<developer defined>"
+#INTONLY="<developer defined>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpr/prototype_com	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,58 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+i i.manifest
+i r.manifest
+#
+# source locations relative to the prototype file
+#
+# SUNWvrrpr
+#
+d none etc 755 root sys
+d none etc/inet 755 root sys
+f none etc/inet/vrrp.conf 644 root sys
+d none lib 755 root bin
+d none lib/svc 755 root bin
+d none lib/svc/method 755 root bin
+f none lib/svc/method/svc-vrrp 555 root bin
+d none var 755 root sys
+d none var/svc 755 root sys
+d none var/svc/manifest 755 root sys
+d none var/svc/manifest/network 755 root sys
+f manifest var/svc/manifest/network/vrrp.xml 0444 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpr/prototype_i386	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWvrrpr
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpr/prototype_sparc	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWvrrpr
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpu/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(FILES)
+
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpu/depend	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,53 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This package information file defines software dependencies associated
+# with the pkg.  You can define three types of pkg dependencies with this file:
+#	 P indicates a prerequisite for installation
+#	 I indicates an incompatible package
+#	 R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+#	(<arch>)<version>
+#	(<arch>)<version>
+#	...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar	Core Architecture, (Root)
+P SUNWcakr	Core Solaris Kernel Architecture (Root)
+P SUNWkvm	Core Architecture, (Kvm)
+P SUNWcsr	Core Solaris, (Root)
+P SUNWckr	Core Solaris Kernel (Root)
+P SUNWcnetr	Core Solaris Network Infrastructure (Root)
+P SUNWcsu	Core Solaris, (Usr)
+P SUNWcsd	Core Solaris Devices
+P SUNWcsl	Core Solaris Libraries
+P SUNWcslr	Core Solaris Libraries
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpu/pkginfo.tmpl	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,58 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWvrrpu"
+NAME="Solaris VRRP protocol (Usr)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="usr"
+MAXINST="1000"
+CATEGORY="system"
+DESC="Solaris VRRP Administration"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
+#VSTOCK="<reserved by Release Engineering for package part #>"
+#ISTATES="<developer defined>"
+#RSTATES='<developer defined>'
+#ULIMIT="<developer defined>"
+#ORDER="<developer defined>"
+#PSTAMP="<developer defined>"
+#INTONLY="<developer defined>"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpu/prototype_com	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWvrrpu
+#
+d none usr 755 root sys
+d none usr/sbin 755 root bin
+f none usr/sbin/vrrpadm 555 root bin
+d none usr/lib 755 root bin
+f none usr/lib/libvrrpadm.so.1 755 root bin
+d none usr/lib/inet 755 root bin
+f none usr/lib/inet/vrrpd 555 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpu/prototype_i386	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWvrrpu
+#
+d none usr/lib/amd64 755 root bin
+f none usr/lib/amd64/libvrrpadm.so.1 755 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWvrrpu/prototype_sparc	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...>	# where to find pkg objects
+#!include <filename>			# include another 'prototype' file
+#!default <mode> <owner> <group>	# default used if not specified on entry
+#!<param>=<value>			# puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWvrrpu
+#
+d none usr/lib/sparcv9 755 root bin
+f none usr/lib/sparcv9/libvrrpadm.so.1 755 root bin
--- a/usr/src/pkgdefs/etc/exception_list_i386	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/pkgdefs/etc/exception_list_i386	Tue Nov 17 09:17:48 2009 -0800
@@ -1261,3 +1261,11 @@
 
 # Private nvfru API
 usr/include/nvfru.h			i386
+
+# vrrp
+usr/include/libvrrpadm.h		i386
+usr/lib/libvrrpadm.so			i386
+usr/lib/llib-lvrrpadm.ln		i386
+usr/lib/amd64/libvrrpadm.so		i386
+usr/lib/amd64/llib-lvrrpadm.ln		i386
+usr/lib/llib-lvrrpadm			i386
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Tue Nov 17 09:17:48 2009 -0800
@@ -1351,3 +1351,11 @@
 
 # Private nvfru API
 usr/include/nvfru.h			sparc
+
+# vrrp
+usr/include/libvrrpadm.h		sparc
+usr/lib/libvrrpadm.so			sparc
+usr/lib/llib-lvrrpadm.ln		sparc
+usr/lib/sparcv9/libvrrpadm.so		sparc
+usr/lib/sparcv9/llib-lvrrpadm.ln	sparc
+usr/lib/llib-lvrrpadm			sparc
--- a/usr/src/uts/common/inet/ip/conn_opt.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ip/conn_opt.c	Tue Nov 17 09:17:48 2009 -0800
@@ -658,11 +658,9 @@
 		case SO_TIMESTAMP:
 			*i1 = connp->conn_recv_ancillary.crb_timestamp;
 			break;	/* goto sizeof (int) option return */
-#ifdef SO_VRRP
 		case SO_VRRP:
 			*i1 = connp->conn_isvrrp;
 			break;	/* goto sizeof (int) option return */
-#endif
 		case SO_ANON_MLP:
 			*i1 = connp->conn_anon_mlp;
 			break;	/* goto sizeof (int) option return */
@@ -1058,12 +1056,10 @@
 		if (IPCL_IS_BOUND(connp))
 			return (EINVAL);
 		break;
-#ifdef SO_VRRP
 	case SO_VRRP:
 		if (secpolicy_ip_config(cr, checkonly) != 0)
 			return (EACCES);
 		break;
-#endif
 	case SO_MAC_EXEMPT:
 		if (secpolicy_net_mac_aware(cr) != 0)
 			return (EACCES);
@@ -1153,11 +1149,9 @@
 	case SO_TIMESTAMP:
 		connp->conn_recv_ancillary.crb_timestamp = onoff;
 		break;
-#ifdef SO_VRRP
 	case SO_VRRP:
 		connp->conn_isvrrp = onoff;
 		break;
-#endif
 	case SO_ANON_MLP:
 		connp->conn_anon_mlp = onoff;
 		break;
--- a/usr/src/uts/common/inet/ip/ip6_if.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ip/ip6_if.c	Tue Nov 17 09:17:48 2009 -0800
@@ -1152,6 +1152,7 @@
 		    ipif->ipif_v6subnet);
 	}
 
+	ip_rts_newaddrmsg(RTM_CHGADDR, 0, ipif, RTSQ_DEFAULT);
 }
 
 /*
@@ -1405,7 +1406,8 @@
 
 #define	IPIF_VALID_IPV6_SOURCE(ipif) \
 	(((ipif)->ipif_flags & IPIF_UP) && \
-	!((ipif)->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST)))
+	!((ipif)->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST)) && \
+	!((ipif)->ipif_ill->ill_flags & ILLF_NOACCEPT))
 
 /* source address candidate */
 typedef struct candidate {
@@ -2224,6 +2226,7 @@
 	mblk_t	*bind_mp = NULL;
 	mblk_t	*unbind_mp = NULL;
 	mblk_t	*notify_mp = NULL;
+	mblk_t  *capab_mp = NULL;
 
 	ip1dbg(("ill_dl_phys(%s:%u)\n", ill->ill_name, ipif->ipif_id));
 	ASSERT(ill->ill_dlpi_style_set);
@@ -2280,6 +2283,12 @@
 	if (info_mp == NULL)
 		goto bad;
 
+	ASSERT(ill->ill_dlpi_capab_state == IDCS_UNKNOWN);
+	capab_mp = ip_dlpi_alloc(sizeof (dl_capability_req_t),
+	    DL_CAPABILITY_REQ);
+	if (capab_mp == NULL)
+		goto bad;
+
 	bind_mp = ip_dlpi_alloc(sizeof (dl_bind_req_t) + sizeof (long),
 	    DL_BIND_REQ);
 	if (bind_mp == NULL)
@@ -2322,6 +2331,12 @@
 	}
 	ill_dlpi_send(ill, bind_mp);
 	ill_dlpi_send(ill, info_mp);
+
+	/*
+	 * Send the capability request to get the VRRP capability information.
+	 */
+	ill_capability_send(ill, capab_mp);
+
 	if (v6token_mp != NULL)
 		ill_dlpi_send(ill, v6token_mp);
 	if (v6lla_mp != NULL)
@@ -2345,6 +2360,7 @@
 	freemsg(info_mp);
 	freemsg(attach_mp);
 	freemsg(bind_mp);
+	freemsg(capab_mp);
 	freemsg(unbind_mp);
 	freemsg(notify_mp);
 	return (ENOMEM);
--- a/usr/src/uts/common/inet/ip/ip6_ire.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ip/ip6_ire.c	Tue Nov 17 09:17:48 2009 -0800
@@ -140,13 +140,9 @@
 	case IRE_LOCAL:
 		ire->ire_sendfn = ire_send_local_v6;
 		ire->ire_recvfn = ire_recv_local_v6;
-#ifdef SO_VRRP
 		ASSERT(ire->ire_ill != NULL);
-		if (ire->ire_ill->ill_flags & ILLF_NOACCEPT) {
-			ire->ire_noaccept = B_TRUE;
+		if (ire->ire_ill->ill_flags & ILLF_NOACCEPT)
 			ire->ire_recvfn = ire_recv_noaccept_v6;
-		}
-#endif
 		break;
 	case IRE_LOOPBACK:
 		ire->ire_sendfn = ire_send_local_v6;
--- a/usr/src/uts/common/inet/ip/ip_if.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ip/ip_if.c	Tue Nov 17 09:17:48 2009 -0800
@@ -167,6 +167,7 @@
 static int	ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q);
 static void	ill_dl_down(ill_t *ill);
 static void	ill_down(ill_t *ill);
+static void	ill_down_ipifs(ill_t *, boolean_t);
 static void	ill_free_mib(ill_t *ill);
 static void	ill_glist_delete(ill_t *);
 static void	ill_phyint_reinit(ill_t *ill);
@@ -189,6 +190,7 @@
 
 static void ill_capability_dispatch(ill_t *, mblk_t *, dl_capability_sub_t *);
 static void ill_capability_id_ack(ill_t *, mblk_t *, dl_capability_sub_t *);
+static void ill_capability_vrrp_ack(ill_t *, mblk_t *, dl_capability_sub_t *);
 static void ill_capability_hcksum_ack(ill_t *, mblk_t *, dl_capability_sub_t *);
 static void ill_capability_hcksum_reset_fill(ill_t *, mblk_t *);
 static void ill_capability_zerocopy_ack(ill_t *, mblk_t *,
@@ -200,7 +202,6 @@
 static void	ill_capability_dld_enable(ill_t *);
 static void	ill_capability_ack_thr(void *);
 static void	ill_capability_lso_enable(ill_t *);
-static void	ill_capability_send(ill_t *, mblk_t *);
 
 static ill_t	*ill_prev_usesrc(ill_t *);
 static int	ill_relink_usesrc_ills(ill_t *, ill_t *, uint_t);
@@ -1446,6 +1447,16 @@
 static void
 ill_capability_dispatch(ill_t *ill, mblk_t *mp, dl_capability_sub_t *subp)
 {
+	/*
+	 * If no ipif was brought up over this ill, this DL_CAPABILITY_REQ/ACK
+	 * is only to get the VRRP capability.
+	 */
+	if (ill->ill_ipif_up_count == 0) {
+		if (subp->dl_cap == DL_CAPAB_VRRP)
+			ill_capability_vrrp_ack(ill, mp, subp);
+		return;
+	}
+
 	switch (subp->dl_cap) {
 	case DL_CAPAB_HCKSUM:
 		ill_capability_hcksum_ack(ill, mp, subp);
@@ -1456,6 +1467,8 @@
 	case DL_CAPAB_DLD:
 		ill_capability_dld_ack(ill, mp, subp);
 		break;
+	case DL_CAPAB_VRRP:
+		break;
 	default:
 		ip1dbg(("ill_capability_dispatch: unknown capab type %d\n",
 		    subp->dl_cap));
@@ -1463,6 +1476,44 @@
 }
 
 /*
+ * Process the vrrp capability received from a DLS Provider. isub must point
+ * to the sub-capability (DL_CAPAB_VRRP) of a DL_CAPABILITY_ACK message.
+ */
+static void
+ill_capability_vrrp_ack(ill_t *ill, mblk_t *mp, dl_capability_sub_t *isub)
+{
+	dl_capab_vrrp_t	*vrrp;
+	uint_t		sub_dl_cap = isub->dl_cap;
+	uint8_t		*capend;
+
+	ASSERT(IAM_WRITER_ILL(ill));
+	ASSERT(sub_dl_cap == DL_CAPAB_VRRP);
+
+	/*
+	 * Note: range checks here are not absolutely sufficient to
+	 * make us robust against malformed messages sent by drivers;
+	 * this is in keeping with the rest of IP's dlpi handling.
+	 * (Remember, it's coming from something else in the kernel
+	 * address space)
+	 */
+	capend = (uint8_t *)(isub + 1) + isub->dl_length;
+	if (capend > mp->b_wptr) {
+		cmn_err(CE_WARN, "ill_capability_vrrp_ack: "
+		    "malformed sub-capability too long for mblk");
+		return;
+	}
+	vrrp = (dl_capab_vrrp_t *)(isub + 1);
+
+	/*
+	 * Compare the IP address family and set ILLF_VRRP for the right ill.
+	 */
+	if ((vrrp->vrrp_af == AF_INET6 && ill->ill_isv6) ||
+	    (vrrp->vrrp_af == AF_INET && !ill->ill_isv6)) {
+		ill->ill_flags |= ILLF_VRRP;
+	}
+}
+
+/*
  * Process a hardware checksum offload capability negotiation ack received
  * from a DLS Provider.isub must point to the sub-capability (DL_CAPAB_HCKSUM)
  * of a DL_CAPABILITY_ACK message.
@@ -2138,7 +2189,7 @@
 
 /*
  * Consume a new-style hardware capabilities negotiation ack.
- * Called via taskq on receipt of DL_CAPABBILITY_ACK.
+ * Called via taskq on receipt of DL_CAPABILITY_ACK.
  */
 static void
 ill_capability_ack_thr(void *arg)
@@ -3753,6 +3804,8 @@
 	 */
 	sctp_update_ipif_addr(ipif, ov6addr);
 
+	ip_rts_newaddrmsg(RTM_CHGADDR, 0, ipif, RTSQ_DEFAULT);
+
 	/*
 	 * ill_glist_insert() -> ill_phyint_reinit() may have merged IPSQs.
 	 * If so, free our original one.
@@ -9480,6 +9533,8 @@
 	sctp_update_ipif_addr(ipif, ov6addr);
 	ipif->ipif_addr_ready = 0;
 
+	ip_rts_newaddrmsg(RTM_CHGADDR, 0, ipif, RTSQ_DEFAULT);
+
 	/*
 	 * If the interface was previously marked as a duplicate, then since
 	 * we've now got a "new" address, it should no longer be considered a
@@ -9800,6 +9855,33 @@
 }
 
 /*
+ * Check which flags will change by the given flags being set
+ * silently ignore flags which userland is not allowed to control.
+ * (Because these flags may change between SIOCGLIFFLAGS and
+ * SIOCSLIFFLAGS, and that's outside of userland's control,
+ * we need to silently ignore them rather than fail.)
+ */
+static void
+ip_sioctl_flags_onoff(ipif_t *ipif, uint64_t flags, uint64_t *onp,
+    uint64_t *offp)
+{
+	ill_t		*ill = ipif->ipif_ill;
+	phyint_t 	*phyi = ill->ill_phyint;
+	uint64_t	cantchange_flags, intf_flags;
+	uint64_t	turn_on, turn_off;
+
+	intf_flags = ipif->ipif_flags | ill->ill_flags | phyi->phyint_flags;
+	cantchange_flags = IFF_CANTCHANGE;
+	if (IS_IPMP(ill))
+		cantchange_flags |= IFF_IPMP_CANTCHANGE;
+	turn_on = (flags ^ intf_flags) & ~cantchange_flags;
+	turn_off = intf_flags & turn_on;
+	turn_on ^= turn_off;
+	*onp = turn_on;
+	*offp = turn_off;
+}
+
+/*
  * Set interface flags.  Many flags require special handling (e.g.,
  * bringing the interface down); see below for details.
  *
@@ -9822,7 +9904,8 @@
 	int	err = 0;
 	phyint_t *phyi;
 	ill_t *ill;
-	uint64_t intf_flags, cantchange_flags;
+	conn_t *connp;
+	uint64_t intf_flags;
 	boolean_t phyint_flags_modified = B_FALSE;
 	uint64_t flags;
 	struct ifreq *ifr;
@@ -9868,23 +9951,10 @@
 	if (IS_IPMP(ill) && ((flags ^ intf_flags) & IFF_IPMP_INVALID))
 		return (EINVAL);
 
-	/*
-	 * Check which flags will change; silently ignore flags which userland
-	 * is not allowed to control.  (Because these flags may change between
-	 * SIOCGLIFFLAGS and SIOCSLIFFLAGS, and that's outside of userland's
-	 * control, we need to silently ignore them rather than fail.)
-	 */
-	cantchange_flags = IFF_CANTCHANGE;
-	if (IS_IPMP(ill))
-		cantchange_flags |= IFF_IPMP_CANTCHANGE;
-
-	turn_on = (flags ^ intf_flags) & ~cantchange_flags;
-	if (turn_on == 0)
+	ip_sioctl_flags_onoff(ipif, flags, &turn_on, &turn_off);
+	if ((turn_on|turn_off) == 0)
 		return (0);	/* No change */
 
-	turn_off = intf_flags & turn_on;
-	turn_on ^= turn_off;
-
 	/*
 	 * All test addresses must be IFF_DEPRECATED (to ensure source address
 	 * selection avoids them) -- so force IFF_DEPRECATED on, and do not
@@ -9894,6 +9964,27 @@
 	    (turn_on|intf_flags) & IFF_NOFAILOVER)
 		return (EINVAL);
 
+	if ((connp = Q_TO_CONN(q)) == NULL)
+		return (EINVAL);
+
+	/*
+	 * Only vrrp control socket is allowed to change IFF_UP and
+	 * IFF_NOACCEPT flags when IFF_VRRP is set.
+	 */
+	if ((intf_flags & IFF_VRRP) && ((turn_off | turn_on) & IFF_UP)) {
+		if (!connp->conn_isvrrp)
+			return (EINVAL);
+	}
+
+	/*
+	 * The IFF_NOACCEPT flag can only be set on an IFF_VRRP IP address by
+	 * VRRP control socket.
+	 */
+	if ((turn_off | turn_on) & IFF_NOACCEPT) {
+		if (!connp->conn_isvrrp || !(intf_flags & IFF_VRRP))
+			return (EINVAL);
+	}
+
 	if (turn_on & IFF_NOFAILOVER) {
 		turn_on |= IFF_DEPRECATED;
 		flags |= IFF_DEPRECATED;
@@ -10091,12 +10182,21 @@
 	}
 
 	/*
-	 * The only flag changes that we currently take specific action on are
+	 * If we are going to change one or more of the flags that are
 	 * IPIF_UP, IPIF_DEPRECATED, IPIF_NOXMIT, IPIF_NOLOCAL, ILLF_NOARP,
 	 * ILLF_NONUD, IPIF_PRIVATE, IPIF_ANYCAST, IPIF_PREFERRED, and
-	 * IPIF_NOFAILOVER.  This is done by bring the ipif down, changing the
-	 * flags and bringing it back up again.  For IPIF_NOFAILOVER, the act
-	 * of bringing it back up will trigger the address to be moved.
+	 * IPIF_NOFAILOVER, we will take special action.  This is
+	 * done by bring the ipif down, changing the flags and bringing
+	 * it back up again.  For IPIF_NOFAILOVER, the act of bringing it
+	 * back up will trigger the address to be moved.
+	 *
+	 * If we are going to change IFF_NOACCEPT, we need to bring
+	 * all the ipifs down then bring them up again.	 The act of
+	 * bringing all the ipifs back up will trigger the local
+	 * ires being recreated with "no_accept" set/cleared.
+	 *
+	 * Note that ILLF_NOACCEPT is always set separately from the
+	 * other flags.
 	 */
 	if ((turn_on|turn_off) &
 	    (IPIF_UP|IPIF_DEPRECATED|IPIF_NOXMIT|IPIF_NOLOCAL|ILLF_NOARP|
@@ -10118,6 +10218,27 @@
 		if (err == EINPROGRESS)
 			return (err);
 		(void) ipif_down_tail(ipif);
+	} else if ((turn_on|turn_off) & ILLF_NOACCEPT) {
+		/*
+		 * If we can quiesce the ill, then continue.  If not, then
+		 * ip_sioctl_flags_tail() will be called from
+		 * ipif_ill_refrele_tail().
+		 */
+		ill_down_ipifs(ill, B_TRUE);
+
+		mutex_enter(&connp->conn_lock);
+		mutex_enter(&ill->ill_lock);
+		if (!ill_is_quiescent(ill)) {
+			boolean_t success;
+
+			success = ipsq_pending_mp_add(connp, ill->ill_ipif,
+			    q, mp, ILL_DOWN);
+			mutex_exit(&ill->ill_lock);
+			mutex_exit(&connp->conn_lock);
+			return (success ? EINPROGRESS : EINTR);
+		}
+		mutex_exit(&ill->ill_lock);
+		mutex_exit(&connp->conn_lock);
 	}
 	return (ip_sioctl_flags_tail(ipif, flags, q, mp));
 }
@@ -10128,7 +10249,6 @@
 	ill_t	*ill;
 	phyint_t *phyi;
 	uint64_t turn_on, turn_off;
-	uint64_t intf_flags, cantchange_flags;
 	boolean_t phyint_flags_modified = B_FALSE;
 	int	err = 0;
 	boolean_t set_linklocal = B_FALSE;
@@ -10141,14 +10261,13 @@
 	ill = ipif->ipif_ill;
 	phyi = ill->ill_phyint;
 
-	intf_flags = ipif->ipif_flags | ill->ill_flags | phyi->phyint_flags;
-	cantchange_flags = IFF_CANTCHANGE | IFF_UP;
-	if (IS_IPMP(ill))
-		cantchange_flags |= IFF_IPMP_CANTCHANGE;
-
-	turn_on = (flags ^ intf_flags) & ~cantchange_flags;
-	turn_off = intf_flags & turn_on;
-	turn_on ^= turn_off;
+	ip_sioctl_flags_onoff(ipif, flags, &turn_on, &turn_off);
+
+	/*
+	 * IFF_UP is handled separately.
+	 */
+	turn_on &= ~IFF_UP;
+	turn_off &= ~IFF_UP;
 
 	if ((turn_on|turn_off) & IFF_PHYINT_FLAGS)
 		phyint_flags_modified = B_TRUE;
@@ -10193,7 +10312,17 @@
 			ipmp_phyint_refresh_active(phyi);
 	}
 
-	if ((flags & IFF_UP) && !(ipif->ipif_flags & IPIF_UP)) {
+	if ((turn_on|turn_off) & ILLF_NOACCEPT) {
+		/*
+		 * If the ILLF_NOACCEPT flag is changed, bring up all the
+		 * ipifs that were brought down.
+		 *
+		 * The routing sockets messages are sent as the result
+		 * of ill_up_ipifs(), further, SCTP's IPIF list was updated
+		 * as well.
+		 */
+		err = ill_up_ipifs(ill, q, mp);
+	} else if ((flags & IFF_UP) && !(ipif->ipif_flags & IPIF_UP)) {
 		/*
 		 * XXX ipif_up really does not know whether a phyint flags
 		 * was modified or not. So, it sends up information on
@@ -10242,17 +10371,26 @@
 	uint64_t flags;
 	struct ifreq *ifr = if_req;
 	struct lifreq *lifr = if_req;
+	uint64_t turn_on, turn_off;
 
 	ip1dbg(("ip_sioctl_flags_restart(%s:%u %p)\n",
 	    ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
 
-	(void) ipif_down_tail(ipif);
 	if (ipip->ipi_cmd_type == IF_CMD) {
 		/* cast to uint16_t prevents unwanted sign extension */
 		flags = (uint16_t)ifr->ifr_flags;
 	} else {
 		flags = lifr->lifr_flags;
 	}
+
+	/*
+	 * If this function call is a result of the ILLF_NOACCEPT flag
+	 * change, do not call ipif_down_tail(). See ip_sioctl_flags().
+	 */
+	ip_sioctl_flags_onoff(ipif, flags, &turn_on, &turn_off);
+	if (!((turn_on|turn_off) & ILLF_NOACCEPT))
+		(void) ipif_down_tail(ipif);
+
 	return (ip_sioctl_flags_tail(ipif, flags, q, mp));
 }
 
@@ -12339,7 +12477,7 @@
 	ill_dlpi_dispatch(ill, mp);
 }
 
-static void
+void
 ill_capability_send(ill_t *ill, mblk_t *mp)
 {
 	ill->ill_capab_pending_cnt++;
@@ -13047,6 +13185,7 @@
 
 	/* Ask SCTP to take it out of it list */
 	sctp_update_ipif(ipif, SCTP_IPIF_REMOVE);
+	ip_rts_newaddrmsg(RTM_FREEADDR, 0, ipif, RTSQ_DEFAULT);
 
 	/* Get it out of the ILL interface list. */
 	ipif_remove(ipif);
@@ -14686,6 +14825,9 @@
 		/* Always skip NOLOCAL and ANYCAST interfaces */
 		if (ipif->ipif_flags & (IPIF_NOLOCAL|IPIF_ANYCAST))
 			continue;
+		/* Always skip NOACCEPT interfaces */
+		if (ipif->ipif_ill->ill_flags & ILLF_NOACCEPT)
+			continue;
 		if (!(ipif->ipif_flags & IPIF_UP))
 			continue;
 
--- a/usr/src/uts/common/inet/ip/ip_ire.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ip/ip_ire.c	Tue Nov 17 09:17:48 2009 -0800
@@ -554,13 +554,9 @@
 	case IRE_LOCAL:
 		ire->ire_sendfn = ire_send_local_v4;
 		ire->ire_recvfn = ire_recv_local_v4;
-#ifdef SO_VRRP
 		ASSERT(ire->ire_ill != NULL);
-		if (ire->ire_ill->ill_flags & ILLF_NOACCEPT) {
-			ire->ire_noaccept = B_TRUE;
+		if (ire->ire_ill->ill_flags & ILLF_NOACCEPT)
 			ire->ire_recvfn = ire_recv_noaccept_v6;
-		}
-#endif
 		break;
 	case IRE_LOOPBACK:
 		ire->ire_sendfn = ire_send_local_v4;
--- a/usr/src/uts/common/inet/ip/ip_rts.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ip/ip_rts.c	Tue Nov 17 09:17:48 2009 -0800
@@ -1791,6 +1791,8 @@
 	switch (type) {
 	case RTM_DELADDR:
 	case RTM_NEWADDR:
+	case RTM_CHGADDR:
+	case RTM_FREEADDR:
 		return (sizeof (ifa_msghdr_t));
 	case RTM_IFINFO:
 		return (sizeof (if_msghdr_t));
@@ -1935,15 +1937,13 @@
 }
 
 /*
- * This is called to generate messages to the routing socket
- * indicating a network interface has had addresses associated with it.
- * The structure of the code is based on the 4.4BSD-Lite2 <net/rtsock.c>.
+ * If cmd is RTM_ADD or RTM_DELETE, generate the rt_msghdr_t message;
+ * otherwise (RTM_NEWADDR, RTM_DELADDR, RTM_CHGADDR and RTM_FREEADDR)
+ * generate the ifa_msghdr_t message.
  */
-void
-ip_rts_newaddrmsg(int cmd, int error, const ipif_t *ipif, uint_t flags)
+static void
+rts_new_rtsmsg(int cmd, int error, const ipif_t *ipif, uint_t flags)
 {
-	int		pass;
-	int		ncmd;
 	int		rtm_addrs;
 	mblk_t		*mp;
 	ifa_msghdr_t	*ifam;
@@ -1952,16 +1952,94 @@
 	ip_stack_t	*ipst = ipif->ipif_ill->ill_ipst;
 
 	/*
-	 * Let conn_ixa caching know that source address selection
-	 * changed
+	 * Do not report unspecified address if this is the RTM_CHGADDR or
+	 * RTM_FREEADDR message.
 	 */
-	ip_update_source_selection(ipst);
+	if (cmd == RTM_CHGADDR || cmd == RTM_FREEADDR) {
+		if (!ipif->ipif_isv6) {
+			if (ipif->ipif_lcl_addr == INADDR_ANY)
+				return;
+		} else if (IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr)) {
+			return;
+		}
+	}
 
 	if (ipif->ipif_isv6)
 		af = AF_INET6;
 	else
 		af = AF_INET;
 
+	if (cmd == RTM_ADD || cmd == RTM_DELETE)
+		rtm_addrs = (RTA_DST | RTA_NETMASK);
+	else
+		rtm_addrs = (RTA_IFA | RTA_NETMASK | RTA_BRD | RTA_IFP);
+
+	mp = rts_alloc_msg(cmd, rtm_addrs, af, 0);
+	if (mp == NULL)
+		return;
+
+	if (cmd != RTM_ADD && cmd != RTM_DELETE) {
+		switch (af) {
+		case AF_INET:
+			rts_fill_msg(cmd, rtm_addrs, 0,
+			    ipif->ipif_net_mask, 0, ipif->ipif_lcl_addr,
+			    ipif->ipif_pp_dst_addr, 0,
+			    ipif->ipif_lcl_addr, ipif->ipif_ill,
+			    mp, NULL);
+			break;
+		case AF_INET6:
+			rts_fill_msg_v6(cmd, rtm_addrs,
+			    &ipv6_all_zeros, &ipif->ipif_v6net_mask,
+			    &ipv6_all_zeros, &ipif->ipif_v6lcl_addr,
+			    &ipif->ipif_v6pp_dst_addr, &ipv6_all_zeros,
+			    &ipif->ipif_v6lcl_addr, ipif->ipif_ill,
+			    mp, NULL);
+			break;
+		}
+		ifam = (ifa_msghdr_t *)mp->b_rptr;
+		ifam->ifam_index =
+		    ipif->ipif_ill->ill_phyint->phyint_ifindex;
+		ifam->ifam_metric = ipif->ipif_metric;
+		ifam->ifam_flags = ((cmd == RTM_NEWADDR) ? RTF_UP : 0);
+		ifam->ifam_addrs = rtm_addrs;
+	} else {
+		switch (af) {
+		case AF_INET:
+			rts_fill_msg(cmd, rtm_addrs,
+			    ipif->ipif_lcl_addr, ipif->ipif_net_mask, 0,
+			    0, 0, 0, 0, NULL, mp, NULL);
+			break;
+		case AF_INET6:
+			rts_fill_msg_v6(cmd, rtm_addrs,
+			    &ipif->ipif_v6lcl_addr,
+			    &ipif->ipif_v6net_mask, &ipv6_all_zeros,
+			    &ipv6_all_zeros, &ipv6_all_zeros,
+			    &ipv6_all_zeros, &ipv6_all_zeros,
+			    NULL, mp, NULL);
+			break;
+		}
+		rtm = (rt_msghdr_t *)mp->b_rptr;
+		rtm->rtm_index =
+		    ipif->ipif_ill->ill_phyint->phyint_ifindex;
+		rtm->rtm_flags = ((cmd == RTM_ADD) ? RTF_UP : 0);
+		rtm->rtm_errno = error;
+		if (error == 0)
+			rtm->rtm_flags |= RTF_DONE;
+		rtm->rtm_addrs = rtm_addrs;
+	}
+	rts_queue_input(mp, NULL, af, flags, ipst);
+}
+
+/*
+ * This is called to generate messages to the routing socket
+ * indicating a network interface has had addresses associated with it.
+ * The structure of the code is based on the 4.4BSD-Lite2 <net/rtsock.c>.
+ */
+void
+ip_rts_newaddrmsg(int cmd, int error, const ipif_t *ipif, uint_t flags)
+{
+	ip_stack_t	*ipst = ipif->ipif_ill->ill_ipst;
+
 	if (flags & RTSQ_DEFAULT) {
 		flags = RTSQ_ALL;
 		/*
@@ -1973,74 +2051,29 @@
 	}
 
 	/*
+	 * Let conn_ixa caching know that source address selection
+	 * changed
+	 */
+	if (cmd == RTM_ADD || cmd == RTM_DELETE)
+		ip_update_source_selection(ipst);
+
+	/*
 	 * If the request is DELETE, send RTM_DELETE and RTM_DELADDR.
 	 * if the request is ADD, send RTM_NEWADDR and RTM_ADD.
+	 * otherwise simply send the request.
 	 */
-	for (pass = 1; pass < 3; pass++) {
-		if ((cmd == RTM_ADD && pass == 1) ||
-		    (cmd == RTM_DELETE && pass == 2)) {
-			ncmd = ((cmd == RTM_ADD) ? RTM_NEWADDR : RTM_DELADDR);
-
-			rtm_addrs = (RTA_IFA | RTA_NETMASK | RTA_BRD | RTA_IFP);
-			mp = rts_alloc_msg(ncmd, rtm_addrs, af, 0);
-			if (mp == NULL)
-				continue;
-			switch (af) {
-			case AF_INET:
-				rts_fill_msg(ncmd, rtm_addrs, 0,
-				    ipif->ipif_net_mask, 0, ipif->ipif_lcl_addr,
-				    ipif->ipif_pp_dst_addr, 0,
-				    ipif->ipif_lcl_addr, ipif->ipif_ill,
-				    mp, NULL);
-				break;
-			case AF_INET6:
-				rts_fill_msg_v6(ncmd, rtm_addrs,
-				    &ipv6_all_zeros, &ipif->ipif_v6net_mask,
-				    &ipv6_all_zeros, &ipif->ipif_v6lcl_addr,
-				    &ipif->ipif_v6pp_dst_addr, &ipv6_all_zeros,
-				    &ipif->ipif_v6lcl_addr, ipif->ipif_ill,
-				    mp, NULL);
-				break;
-			}
-			ifam = (ifa_msghdr_t *)mp->b_rptr;
-			ifam->ifam_index =
-			    ipif->ipif_ill->ill_phyint->phyint_ifindex;
-			ifam->ifam_metric = ipif->ipif_metric;
-			ifam->ifam_flags = ((cmd == RTM_ADD) ? RTF_UP : 0);
-			ifam->ifam_addrs = rtm_addrs;
-			rts_queue_input(mp, NULL, af, flags, ipst);
-		}
-		if ((cmd == RTM_ADD && pass == 2) ||
-		    (cmd == RTM_DELETE && pass == 1)) {
-			rtm_addrs = (RTA_DST | RTA_NETMASK);
-			mp = rts_alloc_msg(cmd, rtm_addrs, af, 0);
-			if (mp == NULL)
-				continue;
-			switch (af) {
-			case AF_INET:
-				rts_fill_msg(cmd, rtm_addrs,
-				    ipif->ipif_lcl_addr, ipif->ipif_net_mask, 0,
-				    0, 0, 0, 0, NULL, mp, NULL);
-				break;
-			case AF_INET6:
-				rts_fill_msg_v6(cmd, rtm_addrs,
-				    &ipif->ipif_v6lcl_addr,
-				    &ipif->ipif_v6net_mask, &ipv6_all_zeros,
-				    &ipv6_all_zeros, &ipv6_all_zeros,
-				    &ipv6_all_zeros, &ipv6_all_zeros,
-				    NULL, mp, NULL);
-				break;
-			}
-			rtm = (rt_msghdr_t *)mp->b_rptr;
-			rtm->rtm_index =
-			    ipif->ipif_ill->ill_phyint->phyint_ifindex;
-			rtm->rtm_flags = ((cmd == RTM_ADD) ? RTF_UP : 0);
-			rtm->rtm_errno = error;
-			if (error == 0)
-				rtm->rtm_flags |= RTF_DONE;
-			rtm->rtm_addrs = rtm_addrs;
-			rts_queue_input(mp, NULL, af, flags, ipst);
-		}
+	switch (cmd) {
+	case RTM_ADD:
+		rts_new_rtsmsg(RTM_NEWADDR, error, ipif, flags);
+		rts_new_rtsmsg(RTM_ADD, error, ipif, flags);
+		break;
+	case RTM_DELETE:
+		rts_new_rtsmsg(RTM_DELETE, error, ipif, flags);
+		rts_new_rtsmsg(RTM_DELADDR, error, ipif, flags);
+		break;
+	default:
+		rts_new_rtsmsg(cmd, error, ipif, flags);
+		break;
 	}
 }
 
--- a/usr/src/uts/common/inet/ip_if.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ip_if.h	Tue Nov 17 09:17:48 2009 -0800
@@ -80,7 +80,7 @@
 
 #define	IFF_PHYINTINST_FLAGS	(IFF_DEBUG|IFF_NOTRAILERS|IFF_NOARP| \
     IFF_MULTICAST|IFF_ROUTER|IFF_NONUD|IFF_NORTEXCH|IFF_IPV4|IFF_IPV6| \
-    IFF_COS_ENABLED|IFF_FIXEDMTU)
+    IFF_COS_ENABLED|IFF_FIXEDMTU|IFF_VRRP|IFF_NOACCEPT)
 
 #define	IFF_LOGINT_FLAGS	(IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| \
     IFF_UNNUMBERED|IFF_DHCPRUNNING|IFF_PRIVATE|IFF_NOXMIT|IFF_NOLOCAL| \
@@ -111,6 +111,8 @@
 #define	ILLF_IPV6		IFF_IPV6	/* IPv6 interface */
 #define	ILLF_COS_ENABLED	IFF_COS_ENABLED	/* Is CoS marking supported */
 #define	ILLF_FIXEDMTU		IFF_FIXEDMTU	/* set with SIOCSLIFMTU */
+#define	ILLF_VRRP		IFF_VRRP	/* managed by VRRP */
+#define	ILLF_NOACCEPT		IFF_NOACCEPT	/* accept only ND messagees */
 
 #define	IPIF_UP			IFF_UP		/* interface is up */
 #define	IPIF_BROADCAST		IFF_BROADCAST	/* broadcast address valid */
@@ -311,6 +313,7 @@
 
 extern	int	ip_addr_availability_check(ipif_t *);
 extern	void	ip_ll_subnet_defaults(ill_t *, mblk_t *);
+extern	void	ill_capability_send(ill_t *, mblk_t *);
 
 extern	int	ip_rt_add(ipaddr_t, ipaddr_t, ipaddr_t, ipaddr_t, int,
     ill_t *, ire_t **, boolean_t, struct rtsa_s *, ip_stack_t *, zoneid_t);
--- a/usr/src/uts/common/inet/ipclassifier.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/ipclassifier.h	Tue Nov 17 09:17:48 2009 -0800
@@ -375,7 +375,8 @@
 		conn_mac_mode : 2,		/* normal/loose/implicit MAC */
 		conn_anon_priv_bind : 1,	/* *_ANON_PRIV_BIND state */
 		conn_zone_is_global : 1,	/* GLOBAL_ZONEID */
-		conn_spare : 24;
+		conn_isvrrp : 1,		/* VRRP control socket */
+		conn_spare : 23;
 
 	boolean_t	conn_flow_cntrld;
 	netstack_t	*conn_netstack;	/* Corresponds to a netstack_hold */
--- a/usr/src/uts/common/inet/udp/udp_opt_data.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/inet/udp/udp_opt_data.c	Tue Nov 17 09:17:48 2009 -0800
@@ -75,6 +75,7 @@
 	},
 { SO_ALLZONES, SOL_SOCKET, OA_R, OA_RW, OP_CONFIG, 0, sizeof (int),
 	0 },
+{ SO_VRRP, SOL_SOCKET, OA_RW, OA_RW, OP_CONFIG, 0, sizeof (int), 0 },
 { SO_TIMESTAMP, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0
 	},
 { SO_ANON_MLP, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int),
--- a/usr/src/uts/common/io/dld/dld_proto.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/io/dld/dld_proto.c	Tue Nov 17 09:17:48 2009 -0800
@@ -1552,6 +1552,8 @@
 	dl_capab_dld_t		dld;
 	dl_capab_hcksum_t	hcksum;
 	dl_capab_zerocopy_t	zcopy;
+	dl_capab_vrrp_t		vrrp;
+	mac_capab_vrrp_t	vrrp_capab;
 	uint8_t			*ptr;
 	queue_t			*q = dsp->ds_wq;
 	mblk_t			*mp1;
@@ -1559,6 +1561,7 @@
 	boolean_t		hcksum_capable = B_FALSE;
 	boolean_t		zcopy_capable = B_FALSE;
 	boolean_t		dld_capable = B_FALSE;
+	boolean_t		vrrp_capable = B_FALSE;
 
 	/*
 	 * Initially assume no capabilities.
@@ -1605,6 +1608,16 @@
 	}
 
 	/*
+	 * Check if vrrp is supported on this interface. If so, reserve
+	 * space for that capability.
+	 */
+	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_VRRP, &vrrp_capab)) {
+		vrrp_capable = B_TRUE;
+		subsize += sizeof (dl_capability_sub_t) +
+		    sizeof (dl_capab_vrrp_t);
+	}
+
+	/*
 	 * If there are no capabilities to advertise or if we
 	 * can't allocate a response, send a DL_ERROR_ACK.
 	 */
@@ -1660,6 +1673,21 @@
 	}
 
 	/*
+	 * VRRP capability negotiation
+	 */
+	if (vrrp_capable) {
+		dlsp = (dl_capability_sub_t *)ptr;
+		dlsp->dl_cap = DL_CAPAB_VRRP;
+		dlsp->dl_length = sizeof (dl_capab_vrrp_t);
+		ptr += sizeof (dl_capability_sub_t);
+
+		bzero(&vrrp, sizeof (dl_capab_vrrp_t));
+		vrrp.vrrp_af = vrrp_capab.mcv_af;
+		bcopy(&vrrp, ptr, sizeof (dl_capab_vrrp_t));
+		ptr += sizeof (dl_capab_vrrp_t);
+	}
+
+	/*
 	 * Direct capability negotiation interface between IP and DLD.
 	 * Refer to dld.h for details.
 	 */
--- a/usr/src/uts/common/io/vnic/vnic_ctl.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/io/vnic/vnic_ctl.c	Tue Nov 17 09:17:48 2009 -0800
@@ -210,6 +210,7 @@
 
 	switch (mac_addr_type) {
 	case VNIC_MAC_ADDR_TYPE_FIXED:
+	case VNIC_MAC_ADDR_TYPE_VRID:
 		mac_len = create_arg->vc_mac_len;
 		/*
 		 * Sanity check the MAC address length. vnic_dev_create()
@@ -265,8 +266,11 @@
 create:
 	err = vnic_dev_create(create_arg->vc_vnic_id, create_arg->vc_link_id,
 	    &mac_addr_type, &mac_len, mac_addr, &mac_slot, mac_prefix_len,
-	    create_arg->vc_vid, &create_arg->vc_resource_props,
-	    create_arg->vc_flags, &diag, cred);
+	    create_arg->vc_vid, create_arg->vc_vrid, create_arg->vc_af,
+	    &create_arg->vc_resource_props, create_arg->vc_flags, &diag,
+	    cred);
+
+
 	if (err != 0)
 		goto bail;
 
--- a/usr/src/uts/common/io/vnic/vnic_dev.c	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/io/vnic/vnic_dev.c	Tue Nov 17 09:17:48 2009 -0800
@@ -197,6 +197,7 @@
 
 	switch (vnic_addr_type) {
 	case VNIC_MAC_ADDR_TYPE_FIXED:
+	case VNIC_MAC_ADDR_TYPE_VRID:
 		/*
 		 * The MAC address value to assign to the VNIC
 		 * is already provided in mac_addr_arg. addr_len_ptr_arg
@@ -319,8 +320,8 @@
 int
 vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
     vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
-    int *mac_slot, uint_t mac_prefix_len, uint16_t vid,
-    mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
+    int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
+    int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
     cred_t *credp)
 {
 	vnic_t *vnic;
@@ -354,6 +355,8 @@
 
 	vnic->vn_id = vnic_id;
 	vnic->vn_link_id = linkid;
+	vnic->vn_vrid = vrid;
+	vnic->vn_af = af;
 
 	if (!is_anchor) {
 		if (linkid == DATALINK_INVALID_LINKID) {
@@ -759,6 +762,16 @@
 	case MAC_CAPAB_NO_NATIVEVLAN:
 	case MAC_CAPAB_NO_ZCOPY:
 		return (B_TRUE);
+	case MAC_CAPAB_VRRP: {
+		mac_capab_vrrp_t *vrrp_capab = cap_data;
+
+		if (vnic->vn_vrid != 0) {
+			if (vrrp_capab != NULL)
+				vrrp_capab->mcv_af = vnic->vn_af;
+			return (B_TRUE);
+		}
+		return (B_FALSE);
+	}
 	default:
 		return (B_FALSE);
 	}
@@ -896,6 +909,8 @@
 	info->vn_mac_prefix_len = 0;
 	info->vn_vid = vnic->vn_vid;
 	info->vn_force = vnic->vn_force;
+	info->vn_vrid = vnic->vn_vrid;
+	info->vn_af = vnic->vn_af;
 
 	bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
 	if (vnic->vn_mch != NULL)
--- a/usr/src/uts/common/net/if.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/net/if.h	Tue Nov 17 09:17:48 2009 -0800
@@ -147,7 +147,7 @@
 
 #define	IFF_IPV4	0x0001000000	/* IPv4 interface */
 #define	IFF_IPV6	0x0002000000	/* IPv6 interface */
-					/* 0x0004000000 was IFF_MIPRUNNING */
+#define	IFF_NOACCEPT	0x0004000000	/* no-accept mode VRRP ill */
 #define	IFF_NOFAILOVER	0x0008000000	/* in.mpathd(1M) test address */
 
 #define	IFF_FAILED	0x0010000000	/* Interface has failed */
@@ -168,20 +168,22 @@
 #define	IFF_VIRTUAL	0x2000000000ll	/* Does not send or receive packets */
 #define	IFF_DUPLICATE	0x4000000000ll	/* Local address already in use */
 #define	IFF_IPMP	0x8000000000ll	/* IPMP IP interface */
+#define	IFF_VRRP	0x10000000000ll	/* Managed by VRRP */
 
 /* flags that cannot be changed by userland on any interface */
 #define	IFF_CANTCHANGE \
 	(IFF_BROADCAST | IFF_POINTOPOINT | IFF_RUNNING | IFF_PROMISC | \
 	IFF_MULTICAST | IFF_MULTI_BCAST | IFF_UNNUMBERED | IFF_IPV4 | \
 	IFF_IPV6 | IFF_IPMP | IFF_FIXEDMTU | IFF_VIRTUAL | \
-	IFF_LOOPBACK | IFF_ALLMULTI | IFF_DUPLICATE | IFF_COS_ENABLED)
+	IFF_LOOPBACK | IFF_ALLMULTI | IFF_DUPLICATE | IFF_COS_ENABLED | \
+	IFF_VRRP)
 
 /* flags that cannot be changed by userland on an IPMP interface */
 #define	IFF_IPMP_CANTCHANGE 	IFF_FAILED
 
 /* flags that can never be set on an IPMP interface */
 #define	IFF_IPMP_INVALID	(IFF_STANDBY | IFF_INACTIVE | IFF_OFFLINE | \
-	IFF_NOFAILOVER | IFF_NOARP | IFF_NONUD | IFF_XRESOLV)
+	IFF_NOFAILOVER | IFF_NOARP | IFF_NONUD | IFF_XRESOLV | IFF_NOACCEPT)
 
 /*
  * Output queues (ifp->if_snd) and internetwork datagram level (pup level 1)
--- a/usr/src/uts/common/net/route.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/net/route.h	Tue Nov 17 09:17:48 2009 -0800
@@ -180,6 +180,8 @@
 #define	RTM_NEWADDR	0xc	/* address being added to iface */
 #define	RTM_DELADDR	0xd	/* address being removed from iface */
 #define	RTM_IFINFO	0xe	/* iface going up/down etc. */
+#define	RTM_CHGADDR	0xf	/* address added/changed (even while down) */
+#define	RTM_FREEADDR	0x10	/* address removed (even while down) */
 
 #define	RTV_MTU		0x1	/* init or lock _mtu */
 #define	RTV_HOPCOUNT	0x2	/* init or lock _hopcount */
--- a/usr/src/uts/common/sys/Makefile	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/Makefile	Tue Nov 17 09:17:48 2009 -0800
@@ -962,7 +962,8 @@
 	eventdefs.h	\
 	ipmp.h		\
 	pwrctl.h	\
-	svm.h
+	svm.h		\
+	vrrp.h
 
 CONTRACTHDRS=		\
 	process.h	\
--- a/usr/src/uts/common/sys/dlpi.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/dlpi.h	Tue Nov 17 09:17:48 2009 -0800
@@ -599,6 +599,8 @@
 					/* dl_data is dl_capab_zerocopy_t */
 #define	DL_CAPAB_DLD		0x06	/* dld capability */
 					/* dl_data is dl_capab_dld_t */
+#define	DL_CAPAB_VRRP		0x07	/* vrrp capability */
+					/* dl_data is dl_capab_vrrp_t */
 
 typedef struct {
 	t_uscalar_t	dl_cap;		/* capability type */
@@ -680,6 +682,13 @@
 #ifdef _KERNEL
 
 /*
+ * VRRP sub-capability (follows dl_capability_sub_t)
+ */
+typedef struct {
+	int	vrrp_af;	/* IPv4 or IPv6 */
+} dl_capab_vrrp_t;
+
+/*
  * The DL_CAPAB_DLD capability enables the capabilities of gldv3-based drivers
  * to be negotiated using a function call (dld_capab) instead of using streams.
  */
--- a/usr/src/uts/common/sys/mac_provider.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/mac_provider.h	Tue Nov 17 09:17:48 2009 -0800
@@ -90,6 +90,7 @@
 	MAC_CAPAB_NO_NATIVEVLAN	= 0x0008, /* boolean only, no data */
 	MAC_CAPAB_NO_ZCOPY	= 0x0010, /* boolean only, no data */
 	MAC_CAPAB_LEGACY	= 0x0020, /* data is mac_capab_legacy_t */
+	MAC_CAPAB_VRRP		= 0x0040, /* data is mac_capab_vrrp_t */
 
 	/*
 	 * Public Capabilities
@@ -393,6 +394,11 @@
 	mac_unbind_share_t	ms_sunbind;	/* Unbind a share */
 } mac_capab_share_t;
 
+typedef struct mac_capab_vrrp_s {
+	/* IPv6 or IPv4? */
+	int		mcv_af;
+} mac_capab_vrrp_t;
+
 /*
  * MAC registration interface
  */
--- a/usr/src/uts/common/sys/socket.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/socket.h	Tue Nov 17 09:17:48 2009 -0800
@@ -177,6 +177,7 @@
 #define	SO_ALLZONES	0x1014		/* bind in all zones */
 #define	SO_EXCLBIND	0x1015		/* exclusive binding */
 #define	SO_MAC_IMPLICIT	0x1016		/* hide mac labels on wire */
+#define	SO_VRRP		0x1017		/* VRRP control socket */
 
 #ifdef	_KERNEL
 #define	SO_SRCADDR	0x2001		/* Internal: AF_UNIX source address */
--- a/usr/src/uts/common/sys/sysevent/eventdefs.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/sysevent/eventdefs.h	Tue Nov 17 09:17:48 2009 -0800
@@ -52,6 +52,7 @@
 #define	EC_FM		"EC_fm"		/* FMA error report event */
 #define	EC_ZFS		"EC_zfs"	/* ZFS event */
 #define	EC_DATALINK	"EC_datalink"	/* datalink event */
+#define	EC_VRRP		"EC_vrrp"	/* VRRP event */
 
 /*
  * The following event class is reserved for exclusive use
@@ -261,6 +262,12 @@
  */
 #define	ESC_DATALINK_PHYS_ADD	"ESC_datalink_phys_add"	/* new physical link */
 
+/*
+ * VRRP subclass definitions. Supporting attributes (name/value paris) are
+ * found in sys/sysevent/vrrp.h
+ */
+#define	ESC_VRRP_STATE_CHANGE	"ESC_vrrp_state_change"
+
 #ifdef	__cplusplus
 }
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/sysevent/vrrp.h	Tue Nov 17 09:17:48 2009 -0800
@@ -0,0 +1,78 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_SYSEVENT_VRRP_H
+#define	_SYS_SYSEVENT_VRRP_H
+
+/*
+ * VRRP sysevent definitions.  Note that all of these definitions are
+ * Sun-private and are subject to change at any time.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Event type EC_VRRP/ESC_VRRP_GROUP_STATE event schema
+ *
+ *	Event Class     - EC_VRRP
+ *	Event Sub-Class - ESC_VRRP_STATE_CHANGE
+ *	Event Vendor	- SUNW_VENDOR		(defined in sys/sysevent.h)
+ *	Event Publisher - VRRP_EVENT_PUBLISHER	(defined in this file)
+ *
+ * 	Attribute Name  - VRRP_EVENT_VERSION
+ *	Attribute Type  - SE_DATA_TYPE_UINT8
+ *	Attribute Value - <version>
+ *
+ *	Attribute Name  - VRRP_EVENT_ROUTER_NAME
+ *	Attribute Type  - SE_DATA_TYPE_STRING
+ *	Attribute Value - <router-name>
+ *
+ *	Attribute Name  - VRRP_EVENT_STATE
+ *	Attribute Type  - SE_DATA_TYPE_UINT8
+ * 	Attribute Value - <state>
+ *
+ *	Attribute Name  - VRRP_EVENT_PREV_STATE
+ *	Attribute Type  - SE_DATA_TYPE_UINT8
+ * 	Attribute Value - <previous-state>
+ */
+
+#define	VRRP_EVENT_PUBLISHER	"vrrpd"
+
+#define	VRRP_EVENT_VERSION	"vrrp_event_version"
+#define	VRRP_EVENT_ROUTER_NAME	"vrrp_router_name"
+#define	VRRP_EVENT_STATE	"vrrp_state"
+#define	VRRP_EVENT_PREV_STATE	"vrrp_prev_state"
+
+#define	VRRP_EVENT_CUR_VERSION	1
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_SYSEVENT_VRRP_H */
--- a/usr/src/uts/common/sys/types.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/types.h	Tue Nov 17 09:17:48 2009 -0800
@@ -379,6 +379,7 @@
 typedef	uid_t	gid_t;			/* GID type		*/
 
 typedef uint32_t	datalink_id_t;
+typedef	uint32_t	vrid_t;
 
 typedef id_t    taskid_t;
 typedef id_t    projid_t;
--- a/usr/src/uts/common/sys/vnic.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/vnic.h	Tue Nov 17 09:17:48 2009 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -87,11 +87,13 @@
  */
 
 typedef enum {
+	VNIC_MAC_ADDR_TYPE_UNKNOWN = -1,
 	VNIC_MAC_ADDR_TYPE_FIXED,
 	VNIC_MAC_ADDR_TYPE_RANDOM,
 	VNIC_MAC_ADDR_TYPE_FACTORY,
 	VNIC_MAC_ADDR_TYPE_AUTO,
-	VNIC_MAC_ADDR_TYPE_PRIMARY
+	VNIC_MAC_ADDR_TYPE_PRIMARY,
+	VNIC_MAC_ADDR_TYPE_VRID
 } vnic_mac_addr_type_t;
 
 #if _LONG_LONG_ALIGNMENT == 8 && _LONG_LONG_ALIGNMENT_32 == 4
@@ -121,6 +123,8 @@
 	uint_t		vc_mac_prefix_len;
 	int		vc_mac_slot;
 	uint16_t	vc_vid;
+	vrid_t		vc_vrid;
+	int		vc_af;
 	uint_t		vc_status;
 	uint_t		vc_flags;
 	vnic_ioc_diag_t	vc_diag;
@@ -144,6 +148,8 @@
 	uint_t		vn_mac_slot;
 	uint32_t	vn_mac_prefix_len;
 	uint16_t	vn_vid;
+	vrid_t		vn_vrid;
+	int		vn_af;
 	boolean_t	vn_force;
 	mac_resource_props_t vn_resource_props;
 } vnic_info_t;
--- a/usr/src/uts/common/sys/vnic_impl.h	Tue Nov 17 08:00:02 2009 -0800
+++ b/usr/src/uts/common/sys/vnic_impl.h	Tue Nov 17 09:17:48 2009 -0800
@@ -54,6 +54,8 @@
 	uint8_t			vn_addr[MAXMACADDRLEN];
 	size_t			vn_addr_len;
 	uint16_t		vn_vid;
+	vrid_t			vn_vrid;
+	int			vn_af;
 	boolean_t		vn_force;
 	datalink_id_t		vn_link_id;
 	mac_notify_handle_t	vn_mnh;
@@ -72,8 +74,8 @@
 #define	vn_maddr_get		vn_mma_capab.maddr_get
 
 extern int vnic_dev_create(datalink_id_t, datalink_id_t, vnic_mac_addr_type_t *,
-    int *, uchar_t *, int *, uint_t, uint16_t, mac_resource_props_t *,
-    uint32_t, vnic_ioc_diag_t *, cred_t *);
+    int *, uchar_t *, int *, uint_t, uint16_t, vrid_t, int,
+    mac_resource_props_t *, uint32_t, vnic_ioc_diag_t *, cred_t *);
 extern int vnic_dev_modify(datalink_id_t, uint_t, vnic_mac_addr_type_t,
     uint_t, uchar_t *, uint_t, mac_resource_props_t *);
 extern int vnic_dev_delete(datalink_id_t, uint32_t, cred_t *);