Mercurial > illumos > illumos-gate
changeset 11076:445f05f9f7b4
PSARC/2008/693 VRRP
PSARC/2009/388 VRRP Update
6288572 RFE: VRRP implementation desired for Solaris
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 *);