xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd.c (revision bbf215553c7233fbab8a0afdf1fac74c44781867)
11cb875aeSCathy Zhou /*
21cb875aeSCathy Zhou  * CDDL HEADER START
31cb875aeSCathy Zhou  *
41cb875aeSCathy Zhou  * The contents of this file are subject to the terms of the
51cb875aeSCathy Zhou  * Common Development and Distribution License (the "License").
61cb875aeSCathy Zhou  * You may not use this file except in compliance with the License.
71cb875aeSCathy Zhou  *
81cb875aeSCathy Zhou  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91cb875aeSCathy Zhou  * or http://www.opensolaris.org/os/licensing.
101cb875aeSCathy Zhou  * See the License for the specific language governing permissions
111cb875aeSCathy Zhou  * and limitations under the License.
121cb875aeSCathy Zhou  *
131cb875aeSCathy Zhou  * When distributing Covered Code, include this CDDL HEADER in each
141cb875aeSCathy Zhou  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151cb875aeSCathy Zhou  * If applicable, add the following below this CDDL HEADER, with the
161cb875aeSCathy Zhou  * fields enclosed by brackets "[]" replaced with your own identifying
171cb875aeSCathy Zhou  * information: Portions Copyright [yyyy] [name of copyright owner]
181cb875aeSCathy Zhou  *
191cb875aeSCathy Zhou  * CDDL HEADER END
201cb875aeSCathy Zhou  */
211cb875aeSCathy Zhou 
221cb875aeSCathy Zhou /*
23f6da83d4SAnurag S. Maskey  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
241cb875aeSCathy Zhou  */
251cb875aeSCathy Zhou 
262954adb0SRob Gulewich /*
272954adb0SRob Gulewich  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
282954adb0SRob Gulewich  */
292954adb0SRob Gulewich 
301cb875aeSCathy Zhou #include <sys/types.h>
311cb875aeSCathy Zhou #include <sys/socket.h>
321cb875aeSCathy Zhou #include <sys/sockio.h>
331cb875aeSCathy Zhou #include <sys/sysevent/vrrp.h>
341cb875aeSCathy Zhou #include <sys/sysevent/eventdefs.h>
351cb875aeSCathy Zhou #include <sys/varargs.h>
361cb875aeSCathy Zhou #include <auth_attr.h>
371cb875aeSCathy Zhou #include <ctype.h>
381cb875aeSCathy Zhou #include <fcntl.h>
391cb875aeSCathy Zhou #include <stdlib.h>
401cb875aeSCathy Zhou #include <strings.h>
411cb875aeSCathy Zhou #include <errno.h>
421cb875aeSCathy Zhou #include <unistd.h>
431cb875aeSCathy Zhou #include <zone.h>
441cb875aeSCathy Zhou #include <libsysevent.h>
451cb875aeSCathy Zhou #include <limits.h>
461cb875aeSCathy Zhou #include <locale.h>
471cb875aeSCathy Zhou #include <arpa/inet.h>
481cb875aeSCathy Zhou #include <signal.h>
491cb875aeSCathy Zhou #include <assert.h>
501cb875aeSCathy Zhou #include <ucred.h>
511cb875aeSCathy Zhou #include <bsm/adt.h>
521cb875aeSCathy Zhou #include <bsm/adt_event.h>
531cb875aeSCathy Zhou #include <priv_utils.h>
541cb875aeSCathy Zhou #include <libdllink.h>
551cb875aeSCathy Zhou #include <libdlvnic.h>
56f6da83d4SAnurag S. Maskey #include <libipadm.h>
571cb875aeSCathy Zhou #include <pwd.h>
581cb875aeSCathy Zhou #include <libvrrpadm.h>
591cb875aeSCathy Zhou #include <net/route.h>
601cb875aeSCathy Zhou #include "vrrpd_impl.h"
611cb875aeSCathy Zhou 
621cb875aeSCathy Zhou /*
631cb875aeSCathy Zhou  * A VRRP router can be only start participating the VRRP protocol of a virtual
641cb875aeSCathy Zhou  * router when all the following conditions are met:
651cb875aeSCathy Zhou  *
661cb875aeSCathy Zhou  * - The VRRP router is enabled (vr->vvr_conf.vvc_enabled is _B_TRUE)
671cb875aeSCathy Zhou  * - The RX socket is successfully created over the physical interface to
681cb875aeSCathy Zhou  *   receive the VRRP multicast advertisement. Note that one RX socket can
691cb875aeSCathy Zhou  *   be shared by several VRRP routers configured over the same physical
701cb875aeSCathy Zhou  *   interface. (See vrrpd_init_rxsock())
711cb875aeSCathy Zhou  * - The TX socket is successfully created over the VNIC interface to send
721cb875aeSCathy Zhou  *   the VRRP advertisment. (See vrrpd_init_txsock())
731cb875aeSCathy Zhou  * - The primary IP address has been successfully selected over the physical
741cb875aeSCathy Zhou  *   interface. (See vrrpd_select_primary())
751cb875aeSCathy Zhou  *
761cb875aeSCathy Zhou  * If a VRRP router is enabled but the other conditions haven't be satisfied,
771cb875aeSCathy Zhou  * the router will be stay at the VRRP_STATE_INIT state. If all the above
781cb875aeSCathy Zhou  * conditions are met, the VRRP router will be transit to either
791cb875aeSCathy Zhou  * the VRRP_STATE_MASTER or the VRRP_STATE_BACKUP state, depends on the VRRP
801cb875aeSCathy Zhou  * protocol.
811cb875aeSCathy Zhou  */
821cb875aeSCathy Zhou 
831cb875aeSCathy Zhou #define	skip_whitespace(p)	while (isspace(*(p))) ++(p)
841cb875aeSCathy Zhou 
851cb875aeSCathy Zhou #define	BUFFSIZE	65536
861cb875aeSCathy Zhou 
871cb875aeSCathy Zhou #define	VRRPCONF	"/etc/inet/vrrp.conf"
881cb875aeSCathy Zhou 
891cb875aeSCathy Zhou typedef struct vrrpd_rtsock_s {
901cb875aeSCathy Zhou 	int		vrt_af;		/* address family */
911cb875aeSCathy Zhou 	int		vrt_fd;		/* socket for the PF_ROUTE msg */
921cb875aeSCathy Zhou 	iu_event_id_t	vrt_eid;	/* event ID */
931cb875aeSCathy Zhou } vrrpd_rtsock_t;
941cb875aeSCathy Zhou 
95f6da83d4SAnurag S. Maskey static ipadm_handle_t	vrrp_ipadm_handle = NULL;	/* libipadm handle */
961cb875aeSCathy Zhou static int		vrrp_logflag = 0;
971cb875aeSCathy Zhou boolean_t		vrrp_debug_level = 0;
981cb875aeSCathy Zhou iu_eh_t			*vrrpd_eh = NULL;
991cb875aeSCathy Zhou iu_tq_t			*vrrpd_timerq = NULL;
1001cb875aeSCathy Zhou static vrrp_handle_t	vrrpd_vh = NULL;
1011cb875aeSCathy Zhou static int		vrrpd_cmdsock_fd = -1;	/* socket to communicate */
1021cb875aeSCathy Zhou 						/* between vrrpd/libvrrpadm */
1031cb875aeSCathy Zhou static iu_event_id_t	vrrpd_cmdsock_eid = -1;
1041cb875aeSCathy Zhou static int		vrrpd_ctlsock_fd = -1;	/* socket to bring up/down */
1051cb875aeSCathy Zhou 						/* the virtual IP addresses */
1061cb875aeSCathy Zhou static int		vrrpd_ctlsock6_fd = -1;
1071cb875aeSCathy Zhou static vrrpd_rtsock_t	vrrpd_rtsocks[2] = {
1081cb875aeSCathy Zhou 	{AF_INET, -1, -1},
1091cb875aeSCathy Zhou 	{AF_INET6, -1, -1}
1101cb875aeSCathy Zhou };
1111cb875aeSCathy Zhou static iu_timer_id_t	vrrp_scan_timer_id = -1;
1121cb875aeSCathy Zhou 
1131cb875aeSCathy Zhou TAILQ_HEAD(vrrp_vr_list_s, vrrp_vr_s);
1141cb875aeSCathy Zhou TAILQ_HEAD(vrrp_intf_list_s, vrrp_intf_s);
1151cb875aeSCathy Zhou static struct vrrp_vr_list_s	vrrp_vr_list;
1161cb875aeSCathy Zhou static struct vrrp_intf_list_s	vrrp_intf_list;
1171cb875aeSCathy Zhou static char		vrrpd_conffile[MAXPATHLEN];
1181cb875aeSCathy Zhou 
1191cb875aeSCathy Zhou /*
1201cb875aeSCathy Zhou  * Multicast address of VRRP advertisement in network byte order
1211cb875aeSCathy Zhou  */
1221cb875aeSCathy Zhou static vrrp_addr_t	vrrp_muladdr4;
1231cb875aeSCathy Zhou static vrrp_addr_t	vrrp_muladdr6;
1241cb875aeSCathy Zhou 
1251cb875aeSCathy Zhou static int		vrrpd_scan_interval = 20000;	/* ms */
126c5e0ece0SCathy Zhou static int		pfds[2];
1271cb875aeSCathy Zhou 
1281cb875aeSCathy Zhou /*
1291cb875aeSCathy Zhou  * macros to calculate skew_time and master_down_timer
1301cb875aeSCathy Zhou  *
1311cb875aeSCathy Zhou  * Note that the input is in centisecs and output are in msecs
1321cb875aeSCathy Zhou  */
1331cb875aeSCathy Zhou #define	SKEW_TIME(pri, intv)	((intv) * (256 - (pri)) / 256)
1341cb875aeSCathy Zhou #define	MASTER_DOWN_INTERVAL(pri, intv)	(3 * (intv) + SKEW_TIME((pri), (intv)))
1351cb875aeSCathy Zhou 
1361cb875aeSCathy Zhou #define	SKEW_TIME_VR(vr)	\
1371cb875aeSCathy Zhou 	SKEW_TIME((vr)->vvr_conf.vvc_pri, (vr)->vvr_master_adver_int)
1381cb875aeSCathy Zhou #define	MASTER_DOWN_INTERVAL_VR(vr)	\
1391cb875aeSCathy Zhou 	MASTER_DOWN_INTERVAL((vr)->vvr_conf.vvc_pri, (vr)->vvr_master_adver_int)
1401cb875aeSCathy Zhou 
1411cb875aeSCathy Zhou #define	VRRP_CONF_UPDATE	0x01
1421cb875aeSCathy Zhou #define	VRRP_CONF_DELETE	0x02
1431cb875aeSCathy Zhou 
1441cb875aeSCathy Zhou static char *af_str(int);
1451cb875aeSCathy Zhou 
1461cb875aeSCathy Zhou static iu_tq_callback_t vrrp_adv_timeout;
1471cb875aeSCathy Zhou static iu_tq_callback_t vrrp_b2m_timeout;
1481cb875aeSCathy Zhou static iu_eh_callback_t vrrpd_sock_handler;
1491cb875aeSCathy Zhou static iu_eh_callback_t vrrpd_rtsock_handler;
1501cb875aeSCathy Zhou static iu_eh_callback_t vrrpd_cmdsock_handler;
1511cb875aeSCathy Zhou 
1521cb875aeSCathy Zhou static int daemon_init();
1531cb875aeSCathy Zhou 
1541cb875aeSCathy Zhou static vrrp_err_t vrrpd_init();
1551cb875aeSCathy Zhou static void vrrpd_fini();
1561cb875aeSCathy Zhou static vrrp_err_t vrrpd_cmdsock_create();
1571cb875aeSCathy Zhou static void vrrpd_cmdsock_destroy();
1581cb875aeSCathy Zhou static vrrp_err_t vrrpd_rtsock_create();
1591cb875aeSCathy Zhou static void vrrpd_rtsock_destroy();
1601cb875aeSCathy Zhou static vrrp_err_t vrrpd_ctlsock_create();
1611cb875aeSCathy Zhou static void vrrpd_ctlsock_destroy();
1621cb875aeSCathy Zhou 
1631cb875aeSCathy Zhou static void vrrpd_scan_timer(iu_tq_t *, void *);
1641cb875aeSCathy Zhou static void vrrpd_scan(int);
1651cb875aeSCathy Zhou static vrrp_err_t vrrpd_init_rxsock(vrrp_vr_t *);
1661cb875aeSCathy Zhou static void vrrpd_fini_rxsock(vrrp_vr_t *);
1671cb875aeSCathy Zhou static vrrp_err_t vrrpd_init_txsock(vrrp_vr_t *);
1681cb875aeSCathy Zhou static vrrp_err_t vrrpd_init_txsock_v4(vrrp_vr_t *);
1691cb875aeSCathy Zhou static vrrp_err_t vrrpd_init_txsock_v6(vrrp_vr_t *);
1701cb875aeSCathy Zhou static void vrrpd_fini_txsock(vrrp_vr_t *);
1711cb875aeSCathy Zhou 
1721cb875aeSCathy Zhou static vrrp_err_t vrrpd_create_vr(vrrp_vr_conf_t *);
1731cb875aeSCathy Zhou static vrrp_err_t vrrpd_enable_vr(vrrp_vr_t *);
1741cb875aeSCathy Zhou static void vrrpd_disable_vr(vrrp_vr_t *, vrrp_intf_t *, boolean_t);
1751cb875aeSCathy Zhou static void vrrpd_delete_vr(vrrp_vr_t *);
1761cb875aeSCathy Zhou 
1771cb875aeSCathy Zhou static vrrp_err_t vrrpd_create(vrrp_vr_conf_t *, boolean_t);
1781cb875aeSCathy Zhou static vrrp_err_t vrrpd_delete(const char *);
1791cb875aeSCathy Zhou static vrrp_err_t vrrpd_enable(const char *, boolean_t);
1801cb875aeSCathy Zhou static vrrp_err_t vrrpd_disable(const char *);
1811cb875aeSCathy Zhou static vrrp_err_t vrrpd_modify(vrrp_vr_conf_t *, uint32_t);
1821cb875aeSCathy Zhou static void vrrpd_list(vrid_t, char *, int, vrrp_ret_list_t *, size_t *);
1831cb875aeSCathy Zhou static void vrrpd_query(const char *, vrrp_ret_query_t *, size_t *);
1841cb875aeSCathy Zhou 
1851cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_name(vrrp_vr_conf_t *, const char *);
1861cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_vrid(vrrp_vr_conf_t *, const char *);
1871cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_af(vrrp_vr_conf_t *, const char *);
1881cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_pri(vrrp_vr_conf_t *, const char *);
1891cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_adver_int(vrrp_vr_conf_t *, const char *);
1901cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_preempt(vrrp_vr_conf_t *, const char *);
1911cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_accept(vrrp_vr_conf_t *, const char *);
1921cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_ifname(vrrp_vr_conf_t *, const char *);
1931cb875aeSCathy Zhou static boolean_t vrrp_rd_prop_enabled(vrrp_vr_conf_t *, const char *);
1941cb875aeSCathy Zhou static int vrrp_wt_prop_name(vrrp_vr_conf_t *, char *, size_t);
1951cb875aeSCathy Zhou static int vrrp_wt_prop_vrid(vrrp_vr_conf_t *, char *, size_t);
1961cb875aeSCathy Zhou static int vrrp_wt_prop_af(vrrp_vr_conf_t *, char *, size_t);
1971cb875aeSCathy Zhou static int vrrp_wt_prop_pri(vrrp_vr_conf_t *, char *, size_t);
1981cb875aeSCathy Zhou static int vrrp_wt_prop_adver_int(vrrp_vr_conf_t *, char *, size_t);
1991cb875aeSCathy Zhou static int vrrp_wt_prop_preempt(vrrp_vr_conf_t *, char *, size_t);
2001cb875aeSCathy Zhou static int vrrp_wt_prop_accept(vrrp_vr_conf_t *, char *, size_t);
2011cb875aeSCathy Zhou static int vrrp_wt_prop_ifname(vrrp_vr_conf_t *, char *, size_t);
2021cb875aeSCathy Zhou static int vrrp_wt_prop_enabled(vrrp_vr_conf_t *, char *, size_t);
2031cb875aeSCathy Zhou 
2041cb875aeSCathy Zhou static void vrrpd_cmd_create(void *, void *, size_t *);
2051cb875aeSCathy Zhou static void vrrpd_cmd_delete(void *, void *, size_t *);
2061cb875aeSCathy Zhou static void vrrpd_cmd_enable(void *, void *, size_t *);
2071cb875aeSCathy Zhou static void vrrpd_cmd_disable(void *, void *, size_t *);
2081cb875aeSCathy Zhou static void vrrpd_cmd_modify(void *, void *, size_t *);
2091cb875aeSCathy Zhou static void vrrpd_cmd_list(void *, void *, size_t *);
2101cb875aeSCathy Zhou static void vrrpd_cmd_query(void *, void *, size_t *);
2111cb875aeSCathy Zhou 
2121cb875aeSCathy Zhou static vrrp_vr_t *vrrpd_lookup_vr_by_vrid(char *, vrid_t vrid_t, int);
2131cb875aeSCathy Zhou static vrrp_vr_t *vrrpd_lookup_vr_by_name(const char *);
2141cb875aeSCathy Zhou static vrrp_intf_t *vrrpd_lookup_if(const char *, int);
2151cb875aeSCathy Zhou static vrrp_err_t vrrpd_create_if(const char *, int, uint32_t, vrrp_intf_t **);
2161cb875aeSCathy Zhou static void vrrpd_delete_if(vrrp_intf_t *, boolean_t);
2171cb875aeSCathy Zhou static vrrp_err_t vrrpd_create_ip(vrrp_intf_t *, const char *, vrrp_addr_t *,
2181cb875aeSCathy Zhou     uint64_t flags);
2191cb875aeSCathy Zhou static void vrrpd_delete_ip(vrrp_intf_t *, vrrp_ip_t *);
2201cb875aeSCathy Zhou 
2211cb875aeSCathy Zhou static void vrrpd_init_ipcache(int);
2221cb875aeSCathy Zhou static void vrrpd_update_ipcache(int);
223f6da83d4SAnurag S. Maskey static ipadm_status_t vrrpd_walk_addr_info(int);
2241cb875aeSCathy Zhou static vrrp_err_t vrrpd_add_ipaddr(char *, int, vrrp_addr_t *,
2251cb875aeSCathy Zhou     int, uint64_t);
2261cb875aeSCathy Zhou static vrrp_ip_t *vrrpd_select_primary(vrrp_intf_t *);
2271cb875aeSCathy Zhou static void vrrpd_reselect_primary(vrrp_intf_t *);
2281cb875aeSCathy Zhou static void vrrpd_reenable_all_vr();
2291cb875aeSCathy Zhou static void vrrpd_remove_if(vrrp_intf_t *, boolean_t);
2301cb875aeSCathy Zhou 
2311cb875aeSCathy Zhou static uint16_t in_cksum(int, uint16_t, void *);
2321cb875aeSCathy Zhou static uint16_t vrrp_cksum4(struct in_addr *, struct in_addr *,
2331cb875aeSCathy Zhou     uint16_t, vrrp_pkt_t *);
2341cb875aeSCathy Zhou static uint16_t vrrp_cksum6(struct in6_addr *, struct in6_addr *,
2351cb875aeSCathy Zhou     uint16_t, vrrp_pkt_t *);
2361cb875aeSCathy Zhou static size_t vrrpd_build_vrrp(vrrp_vr_t *, uchar_t *, int, boolean_t);
2371cb875aeSCathy Zhou 
2381cb875aeSCathy Zhou static void vrrpd_process_adv(vrrp_vr_t *, vrrp_addr_t *, vrrp_pkt_t *);
2391cb875aeSCathy Zhou static vrrp_err_t vrrpd_send_adv(vrrp_vr_t *, boolean_t);
2401cb875aeSCathy Zhou 
2411cb875aeSCathy Zhou /* state transition functions */
2421cb875aeSCathy Zhou static vrrp_err_t vrrpd_state_i2m(vrrp_vr_t *);
2431cb875aeSCathy Zhou static vrrp_err_t vrrpd_state_i2b(vrrp_vr_t *);
2441cb875aeSCathy Zhou static void vrrpd_state_m2i(vrrp_vr_t *);
2451cb875aeSCathy Zhou static void vrrpd_state_b2i(vrrp_vr_t *);
2461cb875aeSCathy Zhou static vrrp_err_t vrrpd_state_b2m(vrrp_vr_t *);
2471cb875aeSCathy Zhou static vrrp_err_t vrrpd_state_m2b(vrrp_vr_t *);
2481cb875aeSCathy Zhou static void vrrpd_state_trans(vrrp_state_t, vrrp_state_t, vrrp_vr_t *);
2491cb875aeSCathy Zhou 
2501cb875aeSCathy Zhou static vrrp_err_t vrrpd_set_noaccept(vrrp_vr_t *, boolean_t);
2511cb875aeSCathy Zhou static vrrp_err_t vrrpd_virtualip_update(vrrp_vr_t *, boolean_t);
2521cb875aeSCathy Zhou static vrrp_err_t vrrpd_virtualip_updateone(vrrp_intf_t *, vrrp_ip_t *,
2531cb875aeSCathy Zhou     boolean_t);
2541cb875aeSCathy Zhou static int vrrpd_post_event(const char *, vrrp_state_t, vrrp_state_t);
2551cb875aeSCathy Zhou 
2561cb875aeSCathy Zhou static void vrrpd_initconf();
2571cb875aeSCathy Zhou static vrrp_err_t vrrpd_updateconf(vrrp_vr_conf_t *, uint_t);
2581cb875aeSCathy Zhou static vrrp_err_t vrrpd_write_vrconf(char *, size_t, vrrp_vr_conf_t *);
2591cb875aeSCathy Zhou static vrrp_err_t vrrpd_read_vrconf(char *, vrrp_vr_conf_t *);
2601cb875aeSCathy Zhou static vrrp_err_t vrrpd_readprop(const char *, vrrp_vr_conf_t *);
2611cb875aeSCathy Zhou static void vrrpd_cleanup();
2621cb875aeSCathy Zhou 
2631cb875aeSCathy Zhou static void vrrp_log(int, char *, ...);
2641cb875aeSCathy Zhou static int timeval_to_milli(struct timeval);
2651cb875aeSCathy Zhou static struct timeval timeval_delta(struct timeval, struct timeval);
2661cb875aeSCathy Zhou 
2671cb875aeSCathy Zhou typedef struct vrrpd_prop_s {
2681cb875aeSCathy Zhou 	char		*vs_propname;
2691cb875aeSCathy Zhou 	boolean_t	(*vs_propread)(vrrp_vr_conf_t *, const char *);
2701cb875aeSCathy Zhou 	int		(*vs_propwrite)(vrrp_vr_conf_t *, char *, size_t);
2711cb875aeSCathy Zhou } vrrp_prop_t;
2721cb875aeSCathy Zhou 
2731cb875aeSCathy Zhou /*
2741cb875aeSCathy Zhou  * persistent VRRP properties array
2751cb875aeSCathy Zhou  */
2761cb875aeSCathy Zhou static vrrp_prop_t vrrp_prop_info_tbl[] = {
2771cb875aeSCathy Zhou 	{"name", vrrp_rd_prop_name, vrrp_wt_prop_name},
2781cb875aeSCathy Zhou 	{"vrid", vrrp_rd_prop_vrid, vrrp_wt_prop_vrid},
2791cb875aeSCathy Zhou 	{"priority", vrrp_rd_prop_pri, vrrp_wt_prop_pri},
2801cb875aeSCathy Zhou 	{"adv_intval", vrrp_rd_prop_adver_int, vrrp_wt_prop_adver_int},
2811cb875aeSCathy Zhou 	{"preempt_mode", vrrp_rd_prop_preempt, vrrp_wt_prop_preempt},
2821cb875aeSCathy Zhou 	{"accept_mode", vrrp_rd_prop_accept, vrrp_wt_prop_accept},
2831cb875aeSCathy Zhou 	{"interface", vrrp_rd_prop_ifname, vrrp_wt_prop_ifname},
2841cb875aeSCathy Zhou 	{"af", vrrp_rd_prop_af, vrrp_wt_prop_af},
2851cb875aeSCathy Zhou 	{"enabled", vrrp_rd_prop_enabled, vrrp_wt_prop_enabled}
2861cb875aeSCathy Zhou };
2871cb875aeSCathy Zhou 
2881cb875aeSCathy Zhou #define	VRRP_PROP_INFO_TABSIZE	\
2891cb875aeSCathy Zhou 	(sizeof (vrrp_prop_info_tbl) / sizeof (vrrp_prop_t))
2901cb875aeSCathy Zhou 
2911cb875aeSCathy Zhou typedef void vrrp_cmd_func_t(void *, void *, size_t *);
2921cb875aeSCathy Zhou 
2931cb875aeSCathy Zhou typedef struct vrrp_cmd_info_s {
2941cb875aeSCathy Zhou 	vrrp_cmd_type_t	vi_cmd;
2951cb875aeSCathy Zhou 	size_t		vi_reqsize;
2961cb875aeSCathy Zhou 	size_t		vi_acksize;	/* 0 if the size is variable */
2971cb875aeSCathy Zhou 	boolean_t	vi_setop;	/* Set operation? Check credentials */
2981cb875aeSCathy Zhou 	vrrp_cmd_func_t	*vi_cmdfunc;
2991cb875aeSCathy Zhou } vrrp_cmd_info_t;
3001cb875aeSCathy Zhou 
3011cb875aeSCathy Zhou static vrrp_cmd_info_t vrrp_cmd_info_tbl[] = {
3021cb875aeSCathy Zhou 	{VRRP_CMD_CREATE, sizeof (vrrp_cmd_create_t),
3031cb875aeSCathy Zhou 	    sizeof (vrrp_ret_create_t), _B_TRUE, vrrpd_cmd_create},
3041cb875aeSCathy Zhou 	{VRRP_CMD_DELETE, sizeof (vrrp_cmd_delete_t),
3051cb875aeSCathy Zhou 	    sizeof (vrrp_ret_delete_t), _B_TRUE, vrrpd_cmd_delete},
3061cb875aeSCathy Zhou 	{VRRP_CMD_ENABLE, sizeof (vrrp_cmd_enable_t),
3071cb875aeSCathy Zhou 	    sizeof (vrrp_ret_enable_t), _B_TRUE, vrrpd_cmd_enable},
3081cb875aeSCathy Zhou 	{VRRP_CMD_DISABLE, sizeof (vrrp_cmd_disable_t),
3091cb875aeSCathy Zhou 	    sizeof (vrrp_ret_disable_t), _B_TRUE, vrrpd_cmd_disable},
3101cb875aeSCathy Zhou 	{VRRP_CMD_MODIFY, sizeof (vrrp_cmd_modify_t),
3111cb875aeSCathy Zhou 	    sizeof (vrrp_ret_modify_t), _B_TRUE, vrrpd_cmd_modify},
3121cb875aeSCathy Zhou 	{VRRP_CMD_QUERY, sizeof (vrrp_cmd_query_t), 0,
3131cb875aeSCathy Zhou 	    _B_FALSE, vrrpd_cmd_query},
3141cb875aeSCathy Zhou 	{VRRP_CMD_LIST, sizeof (vrrp_cmd_list_t), 0,
3151cb875aeSCathy Zhou 	    _B_FALSE, vrrpd_cmd_list}
3161cb875aeSCathy Zhou };
3171cb875aeSCathy Zhou 
3181cb875aeSCathy Zhou #define	VRRP_DOOR_INFO_TABLE_SIZE	\
3191cb875aeSCathy Zhou 	(sizeof (vrrp_cmd_info_tbl) / sizeof (vrrp_cmd_info_t))
3201cb875aeSCathy Zhou 
3211cb875aeSCathy Zhou static int
ipaddr_cmp(int af,vrrp_addr_t * addr1,vrrp_addr_t * addr2)3221cb875aeSCathy Zhou ipaddr_cmp(int af, vrrp_addr_t *addr1, vrrp_addr_t *addr2)
3231cb875aeSCathy Zhou {
3241cb875aeSCathy Zhou 	if (af == AF_INET) {
3251cb875aeSCathy Zhou 		return (memcmp(&addr1->in4.sin_addr,
3261cb875aeSCathy Zhou 		    &addr2->in4.sin_addr, sizeof (struct in_addr)));
3271cb875aeSCathy Zhou 	} else {
3281cb875aeSCathy Zhou 		return (memcmp(&addr1->in6.sin6_addr,
3291cb875aeSCathy Zhou 		    &addr2->in6.sin6_addr, sizeof (struct in6_addr)));
3301cb875aeSCathy Zhou 	}
3311cb875aeSCathy Zhou }
3321cb875aeSCathy Zhou 
3331cb875aeSCathy Zhou static vrrp_vr_t *
vrrpd_lookup_vr_by_vrid(char * ifname,vrid_t vrid,int af)3341cb875aeSCathy Zhou vrrpd_lookup_vr_by_vrid(char *ifname, vrid_t vrid, int af)
3351cb875aeSCathy Zhou {
3361cb875aeSCathy Zhou 	vrrp_vr_t *vr;
3371cb875aeSCathy Zhou 
3381cb875aeSCathy Zhou 	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
3391cb875aeSCathy Zhou 		if (strcmp(vr->vvr_conf.vvc_link, ifname) == 0 &&
3401cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_vrid == vrid &&
3411cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_af == af) {
3421cb875aeSCathy Zhou 			break;
3431cb875aeSCathy Zhou 		}
3441cb875aeSCathy Zhou 	}
3451cb875aeSCathy Zhou 	return (vr);
3461cb875aeSCathy Zhou }
3471cb875aeSCathy Zhou 
3481cb875aeSCathy Zhou static vrrp_vr_t *
vrrpd_lookup_vr_by_name(const char * name)3491cb875aeSCathy Zhou vrrpd_lookup_vr_by_name(const char *name)
3501cb875aeSCathy Zhou {
3511cb875aeSCathy Zhou 	vrrp_vr_t *vr;
3521cb875aeSCathy Zhou 
3531cb875aeSCathy Zhou 	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
3541cb875aeSCathy Zhou 		if (strcmp(vr->vvr_conf.vvc_name, name) == 0)
3551cb875aeSCathy Zhou 			break;
3561cb875aeSCathy Zhou 	}
3571cb875aeSCathy Zhou 	return (vr);
3581cb875aeSCathy Zhou }
3591cb875aeSCathy Zhou 
3601cb875aeSCathy Zhou static vrrp_intf_t *
vrrpd_lookup_if(const char * ifname,int af)3611cb875aeSCathy Zhou vrrpd_lookup_if(const char *ifname, int af)
3621cb875aeSCathy Zhou {
3631cb875aeSCathy Zhou 	vrrp_intf_t	*intf;
3641cb875aeSCathy Zhou 
3651cb875aeSCathy Zhou 	TAILQ_FOREACH(intf, &vrrp_intf_list, vvi_next) {
3661cb875aeSCathy Zhou 		if (strcmp(ifname, intf->vvi_ifname) == 0 &&
3671cb875aeSCathy Zhou 		    af == intf->vvi_af) {
3681cb875aeSCathy Zhou 			break;
3691cb875aeSCathy Zhou 		}
3701cb875aeSCathy Zhou 	}
3711cb875aeSCathy Zhou 	return (intf);
3721cb875aeSCathy Zhou }
3731cb875aeSCathy Zhou 
3741cb875aeSCathy Zhou static vrrp_err_t
vrrpd_create_if(const char * ifname,int af,uint32_t ifindex,vrrp_intf_t ** intfp)3751cb875aeSCathy Zhou vrrpd_create_if(const char *ifname, int af, uint32_t ifindex,
3761cb875aeSCathy Zhou     vrrp_intf_t **intfp)
3771cb875aeSCathy Zhou {
3781cb875aeSCathy Zhou 	vrrp_intf_t	*intf;
3791cb875aeSCathy Zhou 
3801cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_create_if(%s, %s, %d)",
3811cb875aeSCathy Zhou 	    ifname, af_str(af), ifindex);
3821cb875aeSCathy Zhou 
3831cb875aeSCathy Zhou 	if (((*intfp) = malloc(sizeof (vrrp_intf_t))) == NULL) {
3841cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_create_if(): failed to "
3851cb875aeSCathy Zhou 		    "allocate %s/%s interface", ifname, af_str(af));
3861cb875aeSCathy Zhou 		return (VRRP_ENOMEM);
3871cb875aeSCathy Zhou 	}
3881cb875aeSCathy Zhou 
3891cb875aeSCathy Zhou 	intf = *intfp;
3901cb875aeSCathy Zhou 	TAILQ_INIT(&intf->vvi_iplist);
3911cb875aeSCathy Zhou 	(void) strlcpy(intf->vvi_ifname, ifname, sizeof (intf->vvi_ifname));
3921cb875aeSCathy Zhou 	intf->vvi_af = af;
3931cb875aeSCathy Zhou 	intf->vvi_sockfd = -1;
3941cb875aeSCathy Zhou 	intf->vvi_nvr = 0;
3951cb875aeSCathy Zhou 	intf->vvi_eid = -1;
3961cb875aeSCathy Zhou 	intf->vvi_pip = NULL;
3971cb875aeSCathy Zhou 	intf->vvi_ifindex = ifindex;
3981cb875aeSCathy Zhou 	intf->vvi_state = NODE_STATE_NEW;
3991cb875aeSCathy Zhou 	intf->vvi_vr_state = VRRP_STATE_INIT;
4001cb875aeSCathy Zhou 	TAILQ_INSERT_TAIL(&vrrp_intf_list, intf, vvi_next);
4011cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
4021cb875aeSCathy Zhou }
4031cb875aeSCathy Zhou 
4041cb875aeSCathy Zhou /*
4051cb875aeSCathy Zhou  * An interface is deleted. If update_vr is true, the deletion of the interface
4061cb875aeSCathy Zhou  * may cause the state transition of assoicated VRRP router (if this interface
4071cb875aeSCathy Zhou  * is either the primary or the VNIC interface of the VRRP router); otherwise,
4081cb875aeSCathy Zhou  * simply delete the interface without updating the VRRP router.
4091cb875aeSCathy Zhou  */
4101cb875aeSCathy Zhou static void
vrrpd_delete_if(vrrp_intf_t * intf,boolean_t update_vr)4111cb875aeSCathy Zhou vrrpd_delete_if(vrrp_intf_t *intf, boolean_t update_vr)
4121cb875aeSCathy Zhou {
4131cb875aeSCathy Zhou 	vrrp_ip_t	*ip;
4141cb875aeSCathy Zhou 
4151cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_delete_if(%s, %s, %supdate_vr)",
4161cb875aeSCathy Zhou 	    intf->vvi_ifname, af_str(intf->vvi_af), update_vr ? "" : "no_");
4171cb875aeSCathy Zhou 
4181cb875aeSCathy Zhou 	if (update_vr) {
4191cb875aeSCathy Zhou 		/*
4201cb875aeSCathy Zhou 		 * If a this interface is the physical interface or the VNIC
4211cb875aeSCathy Zhou 		 * of a VRRP router, the deletion of the interface (no IP
4221cb875aeSCathy Zhou 		 * address exists on this interface) may cause the state
4231cb875aeSCathy Zhou 		 * transition of the VRRP router. call vrrpd_remove_if()
4241cb875aeSCathy Zhou 		 * to find all corresponding VRRP router and update their
4251cb875aeSCathy Zhou 		 * states.
4261cb875aeSCathy Zhou 		 */
4271cb875aeSCathy Zhou 		vrrpd_remove_if(intf, _B_FALSE);
4281cb875aeSCathy Zhou 	}
4291cb875aeSCathy Zhou 
4301cb875aeSCathy Zhou 	/*
4311cb875aeSCathy Zhou 	 * First remove and delete all the IP addresses on the interface
4321cb875aeSCathy Zhou 	 */
4331cb875aeSCathy Zhou 	while (!TAILQ_EMPTY(&intf->vvi_iplist)) {
4341cb875aeSCathy Zhou 		ip = TAILQ_FIRST(&intf->vvi_iplist);
4351cb875aeSCathy Zhou 		vrrpd_delete_ip(intf, ip);
4361cb875aeSCathy Zhou 	}
4371cb875aeSCathy Zhou 
4381cb875aeSCathy Zhou 	/*
4391cb875aeSCathy Zhou 	 * Then remove and delete the interface
4401cb875aeSCathy Zhou 	 */
4411cb875aeSCathy Zhou 	TAILQ_REMOVE(&vrrp_intf_list, intf, vvi_next);
4421cb875aeSCathy Zhou 	(void) free(intf);
4431cb875aeSCathy Zhou }
4441cb875aeSCathy Zhou 
4451cb875aeSCathy Zhou static vrrp_err_t
vrrpd_create_ip(vrrp_intf_t * intf,const char * lifname,vrrp_addr_t * addr,uint64_t flags)4461cb875aeSCathy Zhou vrrpd_create_ip(vrrp_intf_t *intf, const char *lifname, vrrp_addr_t *addr,
4471cb875aeSCathy Zhou     uint64_t flags)
4481cb875aeSCathy Zhou {
4491cb875aeSCathy Zhou 	vrrp_ip_t	*ip;
4501cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
4511cb875aeSCathy Zhou 
4521cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
4531cb875aeSCathy Zhou 	VRRPADDR2STR(intf->vvi_af, addr, abuf, INET6_ADDRSTRLEN, _B_FALSE);
4541cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_create_ip(%s, %s, %s, 0x%x)",
4551cb875aeSCathy Zhou 	    intf->vvi_ifname, lifname, abuf, flags);
4561cb875aeSCathy Zhou 
4571cb875aeSCathy Zhou 	if ((ip = malloc(sizeof (vrrp_ip_t))) == NULL) {
4581cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_create_ip(%s, %s):"
4591cb875aeSCathy Zhou 		    "failed to allocate IP", lifname, abuf);
4601cb875aeSCathy Zhou 		return (VRRP_ENOMEM);
4611cb875aeSCathy Zhou 	}
4621cb875aeSCathy Zhou 
4631cb875aeSCathy Zhou 	(void) strncpy(ip->vip_lifname, lifname, sizeof (ip->vip_lifname));
4641cb875aeSCathy Zhou 	ip->vip_state = NODE_STATE_NEW;
4651cb875aeSCathy Zhou 	ip->vip_flags = flags;
4661cb875aeSCathy Zhou 	(void) memcpy(&ip->vip_addr, addr, sizeof (ip->vip_addr));
4671cb875aeSCathy Zhou 
4681cb875aeSCathy Zhou 	/*
4691cb875aeSCathy Zhou 	 * Make sure link-local IPv6 IP addresses are at the head of the list
4701cb875aeSCathy Zhou 	 */
4711cb875aeSCathy Zhou 	if (intf->vvi_af == AF_INET6 &&
4721cb875aeSCathy Zhou 	    IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr)) {
4731cb875aeSCathy Zhou 		TAILQ_INSERT_HEAD(&intf->vvi_iplist, ip, vip_next);
4741cb875aeSCathy Zhou 	} else {
4751cb875aeSCathy Zhou 		TAILQ_INSERT_TAIL(&intf->vvi_iplist, ip, vip_next);
4761cb875aeSCathy Zhou 	}
4771cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
4781cb875aeSCathy Zhou }
4791cb875aeSCathy Zhou 
4801cb875aeSCathy Zhou static void
vrrpd_delete_ip(vrrp_intf_t * intf,vrrp_ip_t * ip)4811cb875aeSCathy Zhou vrrpd_delete_ip(vrrp_intf_t *intf, vrrp_ip_t *ip)
4821cb875aeSCathy Zhou {
4831cb875aeSCathy Zhou 	char	abuf[INET6_ADDRSTRLEN];
4841cb875aeSCathy Zhou 	int	af = intf->vvi_af;
4851cb875aeSCathy Zhou 
4861cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
4871cb875aeSCathy Zhou 	VRRPADDR2STR(af, &ip->vip_addr, abuf, sizeof (abuf), _B_FALSE);
4881cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_delete_ip(%s, %s, %s) is %sprimary",
4891cb875aeSCathy Zhou 	    intf->vvi_ifname, ip->vip_lifname, abuf,
4901cb875aeSCathy Zhou 	    intf->vvi_pip == ip ? "" : "not ");
4911cb875aeSCathy Zhou 
4921cb875aeSCathy Zhou 	if (intf->vvi_pip == ip)
4931cb875aeSCathy Zhou 		intf->vvi_pip = NULL;
4941cb875aeSCathy Zhou 
4951cb875aeSCathy Zhou 	TAILQ_REMOVE(&intf->vvi_iplist, ip, vip_next);
4961cb875aeSCathy Zhou 	(void) free(ip);
4971cb875aeSCathy Zhou }
4981cb875aeSCathy Zhou 
4991cb875aeSCathy Zhou static char *
rtm_event2str(uchar_t event)5001cb875aeSCathy Zhou rtm_event2str(uchar_t event)
5011cb875aeSCathy Zhou {
5021cb875aeSCathy Zhou 	switch (event) {
5031cb875aeSCathy Zhou 	case RTM_NEWADDR:
5041cb875aeSCathy Zhou 		return ("RTM_NEWADDR");
5051cb875aeSCathy Zhou 	case RTM_DELADDR:
5061cb875aeSCathy Zhou 		return ("RTM_DELADDR");
5071cb875aeSCathy Zhou 	case RTM_IFINFO:
5081cb875aeSCathy Zhou 		return ("RTM_IFINFO");
5091cb875aeSCathy Zhou 	case RTM_ADD:
5101cb875aeSCathy Zhou 		return ("RTM_ADD");
5111cb875aeSCathy Zhou 	case RTM_DELETE:
5121cb875aeSCathy Zhou 		return ("RTM_DELETE");
5131cb875aeSCathy Zhou 	case RTM_CHANGE:
5141cb875aeSCathy Zhou 		return ("RTM_CHANGE");
5151cb875aeSCathy Zhou 	case RTM_OLDADD:
5161cb875aeSCathy Zhou 		return ("RTM_OLDADD");
5171cb875aeSCathy Zhou 	case RTM_OLDDEL:
5181cb875aeSCathy Zhou 		return ("RTM_OLDDEL");
5191cb875aeSCathy Zhou 	case RTM_CHGADDR:
5201cb875aeSCathy Zhou 		return ("RTM_CHGADDR");
5211cb875aeSCathy Zhou 	case RTM_FREEADDR:
5221cb875aeSCathy Zhou 		return ("RTM_FREEADDR");
5231cb875aeSCathy Zhou 	default:
5241cb875aeSCathy Zhou 		return ("RTM_OTHER");
5251cb875aeSCathy Zhou 	}
5261cb875aeSCathy Zhou }
5271cb875aeSCathy Zhou 
528c5e0ece0SCathy Zhou /*
529c5e0ece0SCathy Zhou  * This is called by the child process to inform the parent process to
530c5e0ece0SCathy Zhou  * exit with the given return value. Note that the child process
531c5e0ece0SCathy Zhou  * (the daemon process) informs the parent process to exit when anything
532c5e0ece0SCathy Zhou  * goes wrong or when all the intialization is done.
533c5e0ece0SCathy Zhou  */
534c5e0ece0SCathy Zhou static int
vrrpd_inform_parent_exit(int rv)535c5e0ece0SCathy Zhou vrrpd_inform_parent_exit(int rv)
536c5e0ece0SCathy Zhou {
537c5e0ece0SCathy Zhou 	int err = 0;
538c5e0ece0SCathy Zhou 
539c5e0ece0SCathy Zhou 	/*
540c5e0ece0SCathy Zhou 	 * If vrrp_debug_level is none-zero, vrrpd is not running as
541c5e0ece0SCathy Zhou 	 * a daemon. Return directly.
542c5e0ece0SCathy Zhou 	 */
543c5e0ece0SCathy Zhou 	if (vrrp_debug_level != 0)
544c5e0ece0SCathy Zhou 		return (0);
545c5e0ece0SCathy Zhou 
546c5e0ece0SCathy Zhou 	if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) {
547c5e0ece0SCathy Zhou 		err = errno;
548c5e0ece0SCathy Zhou 		(void) close(pfds[1]);
549c5e0ece0SCathy Zhou 		return (err);
550c5e0ece0SCathy Zhou 	}
551c5e0ece0SCathy Zhou 	(void) close(pfds[1]);
552c5e0ece0SCathy Zhou 	return (0);
553c5e0ece0SCathy Zhou }
554c5e0ece0SCathy Zhou 
5551cb875aeSCathy Zhou int
main(int argc,char * argv[])5561cb875aeSCathy Zhou main(int argc, char *argv[])
5571cb875aeSCathy Zhou {
5581cb875aeSCathy Zhou 	int c, err;
5591cb875aeSCathy Zhou 	struct sigaction sa;
5601cb875aeSCathy Zhou 	sigset_t mask;
5611cb875aeSCathy Zhou 	struct rlimit rl;
5621cb875aeSCathy Zhou 
5631cb875aeSCathy Zhou 	(void) setlocale(LC_ALL, "");
5641cb875aeSCathy Zhou 	(void) textdomain(TEXT_DOMAIN);
5651cb875aeSCathy Zhou 
5661cb875aeSCathy Zhou 	/*
5671cb875aeSCathy Zhou 	 * We need PRIV_SYS_CONFIG to post VRRP sysevent, PRIV_NET_RAWACESS
5681cb875aeSCathy Zhou 	 * and PRIV_NET_ICMPACCESS to open  the raw socket, PRIV_SYS_IP_CONFIG
5691cb875aeSCathy Zhou 	 * to bring up/down the virtual IP addresses, and PRIV_SYS_RESOURCE to
5701cb875aeSCathy Zhou 	 * setrlimit().
5711cb875aeSCathy Zhou 	 *
5721cb875aeSCathy Zhou 	 * Note that sysevent is not supported in non-global zones.
5731cb875aeSCathy Zhou 	 */
5741cb875aeSCathy Zhou 	if (getzoneid() == GLOBAL_ZONEID) {
5751cb875aeSCathy Zhou 		err = __init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
5761cb875aeSCathy Zhou 		    PRIV_SYS_CONFIG, PRIV_NET_RAWACCESS, PRIV_NET_ICMPACCESS,
5771cb875aeSCathy Zhou 		    PRIV_SYS_IP_CONFIG, PRIV_SYS_RESOURCE, NULL);
5781cb875aeSCathy Zhou 	} else {
5791cb875aeSCathy Zhou 		err = __init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
5801cb875aeSCathy Zhou 		    PRIV_NET_RAWACCESS, PRIV_NET_ICMPACCESS,
5811cb875aeSCathy Zhou 		    PRIV_SYS_IP_CONFIG, PRIV_SYS_RESOURCE, NULL);
5821cb875aeSCathy Zhou 	}
5831cb875aeSCathy Zhou 
5841cb875aeSCathy Zhou 	if (err == -1) {
5851cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "main(): init_daemon_priv() failed");
5861cb875aeSCathy Zhou 		return (EXIT_FAILURE);
5871cb875aeSCathy Zhou 	}
5881cb875aeSCathy Zhou 
5891cb875aeSCathy Zhou 	/*
5901cb875aeSCathy Zhou 	 * If vrrpd is started by other process, it will inherit the
5911cb875aeSCathy Zhou 	 * signal block mask. We unblock all signals to make sure the
5921cb875aeSCathy Zhou 	 * signal handling will work normally.
5931cb875aeSCathy Zhou 	 */
5941cb875aeSCathy Zhou 	(void) sigfillset(&mask);
5951cb875aeSCathy Zhou 	(void) thr_sigsetmask(SIG_UNBLOCK, &mask, NULL);
5961cb875aeSCathy Zhou 	sa.sa_handler = vrrpd_cleanup;
5971cb875aeSCathy Zhou 	sa.sa_flags = 0;
5981cb875aeSCathy Zhou 	(void) sigemptyset(&sa.sa_mask);
5991cb875aeSCathy Zhou 	(void) sigaction(SIGINT, &sa, NULL);
6001cb875aeSCathy Zhou 	(void) sigaction(SIGQUIT, &sa, NULL);
6011cb875aeSCathy Zhou 	(void) sigaction(SIGTERM, &sa, NULL);
6021cb875aeSCathy Zhou 
6031cb875aeSCathy Zhou 	vrrp_debug_level = 0;
6041cb875aeSCathy Zhou 	(void) strlcpy(vrrpd_conffile, VRRPCONF, sizeof (vrrpd_conffile));
6051cb875aeSCathy Zhou 	while ((c = getopt(argc, argv, "d:f:")) != EOF) {
6061cb875aeSCathy Zhou 		switch (c) {
6071cb875aeSCathy Zhou 		case 'd':
6081cb875aeSCathy Zhou 			vrrp_debug_level = atoi(optarg);
6091cb875aeSCathy Zhou 			break;
6101cb875aeSCathy Zhou 		case 'f':
6111cb875aeSCathy Zhou 			(void) strlcpy(vrrpd_conffile, optarg,
6121cb875aeSCathy Zhou 			    sizeof (vrrpd_conffile));
6131cb875aeSCathy Zhou 			break;
6141cb875aeSCathy Zhou 		default:
6151cb875aeSCathy Zhou 			break;
6161cb875aeSCathy Zhou 		}
6171cb875aeSCathy Zhou 	}
6181cb875aeSCathy Zhou 
6191cb875aeSCathy Zhou 	closefrom(3);
6201cb875aeSCathy Zhou 	if (vrrp_debug_level == 0 && (daemon_init() != 0)) {
6211cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "main(): daemon_init() failed");
6221cb875aeSCathy Zhou 		return (EXIT_FAILURE);
6231cb875aeSCathy Zhou 	}
6241cb875aeSCathy Zhou 
6251cb875aeSCathy Zhou 	rl.rlim_cur = RLIM_INFINITY;
6261cb875aeSCathy Zhou 	rl.rlim_max = RLIM_INFINITY;
6271cb875aeSCathy Zhou 	if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
6281cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "main(): setrlimit() failed");
629c5e0ece0SCathy Zhou 		goto child_out;
6301cb875aeSCathy Zhou 	}
6311cb875aeSCathy Zhou 
6321cb875aeSCathy Zhou 	if (vrrpd_init() != VRRP_SUCCESS) {
6331cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "main(): vrrpd_init() failed");
634c5e0ece0SCathy Zhou 		goto child_out;
6351cb875aeSCathy Zhou 	}
6361cb875aeSCathy Zhou 
6371cb875aeSCathy Zhou 	/*
6381cb875aeSCathy Zhou 	 * Get rid of unneeded privileges.
6391cb875aeSCathy Zhou 	 */
6401cb875aeSCathy Zhou 	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
6411cb875aeSCathy Zhou 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, PRIV_SYS_RESOURCE, NULL);
6421cb875aeSCathy Zhou 
6431cb875aeSCathy Zhou 	/*
6441cb875aeSCathy Zhou 	 * Read the configuration and initialize the existing VRRP
6451cb875aeSCathy Zhou 	 * configuration
6461cb875aeSCathy Zhou 	 */
6471cb875aeSCathy Zhou 	vrrpd_initconf();
6481cb875aeSCathy Zhou 
6491cb875aeSCathy Zhou 	/*
650c5e0ece0SCathy Zhou 	 * Inform the parent process that it can successfully exit.
651c5e0ece0SCathy Zhou 	 */
652c5e0ece0SCathy Zhou 	if ((err = vrrpd_inform_parent_exit(EXIT_SUCCESS)) != 0) {
653c5e0ece0SCathy Zhou 		vrrpd_cleanup();
654c5e0ece0SCathy Zhou 		vrrp_log(VRRP_WARNING, "vrrpd_inform_parent_exit() failed: %s",
655c5e0ece0SCathy Zhou 		    strerror(err));
656c5e0ece0SCathy Zhou 		return (EXIT_FAILURE);
657c5e0ece0SCathy Zhou 	}
658c5e0ece0SCathy Zhou 
659c5e0ece0SCathy Zhou 	/*
6601cb875aeSCathy Zhou 	 * Start the loop to handle the timer and the IO events.
6611cb875aeSCathy Zhou 	 */
6621cb875aeSCathy Zhou 	switch (iu_handle_events(vrrpd_eh, vrrpd_timerq)) {
6631cb875aeSCathy Zhou 	case -1:
6641cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "main(): iu_handle_events() failed "
6651cb875aeSCathy Zhou 		    "abnormally");
6661cb875aeSCathy Zhou 		break;
6671cb875aeSCathy Zhou 	default:
6681cb875aeSCathy Zhou 		break;
6691cb875aeSCathy Zhou 	}
6701cb875aeSCathy Zhou 
6711cb875aeSCathy Zhou 	vrrpd_cleanup();
6721cb875aeSCathy Zhou 	return (EXIT_SUCCESS);
673c5e0ece0SCathy Zhou 
674c5e0ece0SCathy Zhou child_out:
675c5e0ece0SCathy Zhou 	(void) vrrpd_inform_parent_exit(EXIT_FAILURE);
676c5e0ece0SCathy Zhou 	return (EXIT_FAILURE);
6771cb875aeSCathy Zhou }
6781cb875aeSCathy Zhou 
6791cb875aeSCathy Zhou static int
daemon_init()6801cb875aeSCathy Zhou daemon_init()
6811cb875aeSCathy Zhou {
6821cb875aeSCathy Zhou 	pid_t	pid;
683c5e0ece0SCathy Zhou 	int	rv;
6841cb875aeSCathy Zhou 
6851cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "daemon_init()");
6861cb875aeSCathy Zhou 
6871cb875aeSCathy Zhou 	if (getenv("SMF_FMRI") == NULL) {
688*bbf21555SRichard Lowe 		vrrp_log(VRRP_ERR, "daemon_init(): vrrpd is an smf(7) managed "
689c5e0ece0SCathy Zhou 		    "service and should not be run from the command line.");
6901cb875aeSCathy Zhou 		return (-1);
6911cb875aeSCathy Zhou 	}
6921cb875aeSCathy Zhou 
693c5e0ece0SCathy Zhou 	/*
694c5e0ece0SCathy Zhou 	 * Create the pipe used for the child process to inform the parent
695c5e0ece0SCathy Zhou 	 * process to exit after all initialization is done.
696c5e0ece0SCathy Zhou 	 */
697c5e0ece0SCathy Zhou 	if (pipe(pfds) < 0) {
698c5e0ece0SCathy Zhou 		vrrp_log(VRRP_ERR, "daemon_init(): pipe() failed: %s",
699c5e0ece0SCathy Zhou 		    strerror(errno));
7001cb875aeSCathy Zhou 		return (-1);
701c5e0ece0SCathy Zhou 	}
7021cb875aeSCathy Zhou 
703c5e0ece0SCathy Zhou 	if ((pid = fork()) < 0) {
704c5e0ece0SCathy Zhou 		vrrp_log(VRRP_ERR, "daemon_init(): fork() failed: %s",
705c5e0ece0SCathy Zhou 		    strerror(errno));
706c5e0ece0SCathy Zhou 		(void) close(pfds[0]);
707c5e0ece0SCathy Zhou 		(void) close(pfds[1]);
708c5e0ece0SCathy Zhou 		return (-1);
709c5e0ece0SCathy Zhou 	}
710c5e0ece0SCathy Zhou 
711c5e0ece0SCathy Zhou 	if (pid != 0) { /* Parent */
712c5e0ece0SCathy Zhou 		(void) close(pfds[1]);
713c5e0ece0SCathy Zhou 
714c5e0ece0SCathy Zhou 		/*
715c5e0ece0SCathy Zhou 		 * Read the child process's return value from the pfds.
716c5e0ece0SCathy Zhou 		 * If the child process exits unexpectedly, read() returns -1.
717c5e0ece0SCathy Zhou 		 */
718c5e0ece0SCathy Zhou 		if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) {
719c5e0ece0SCathy Zhou 			vrrp_log(VRRP_ERR, "daemon_init(): child process "
720c5e0ece0SCathy Zhou 			    "exited unexpectedly %s", strerror(errno));
721c5e0ece0SCathy Zhou 			(void) kill(pid, SIGTERM);
722c5e0ece0SCathy Zhou 			rv = EXIT_FAILURE;
723c5e0ece0SCathy Zhou 		}
724c5e0ece0SCathy Zhou 		(void) close(pfds[0]);
725c5e0ece0SCathy Zhou 		exit(rv);
7261cb875aeSCathy Zhou 	}
7271cb875aeSCathy Zhou 
7281cb875aeSCathy Zhou 	/*
7291cb875aeSCathy Zhou 	 * in child process, became a daemon, and return to main() to continue.
7301cb875aeSCathy Zhou 	 */
731c5e0ece0SCathy Zhou 	(void) close(pfds[0]);
7321cb875aeSCathy Zhou 	(void) chdir("/");
7331cb875aeSCathy Zhou 	(void) setsid();
7341cb875aeSCathy Zhou 	(void) close(0);
7351cb875aeSCathy Zhou 	(void) close(1);
7361cb875aeSCathy Zhou 	(void) close(2);
7371cb875aeSCathy Zhou 	(void) open("/dev/null", O_RDWR, 0);
7381cb875aeSCathy Zhou 	(void) dup2(0, 1);
7391cb875aeSCathy Zhou 	(void) dup2(0, 2);
7401cb875aeSCathy Zhou 	openlog("vrrpd", LOG_PID, LOG_DAEMON);
7411cb875aeSCathy Zhou 	vrrp_logflag = 1;
7421cb875aeSCathy Zhou 	return (0);
7431cb875aeSCathy Zhou }
7441cb875aeSCathy Zhou 
7451cb875aeSCathy Zhou static vrrp_err_t
vrrpd_init()7461cb875aeSCathy Zhou vrrpd_init()
7471cb875aeSCathy Zhou {
7481cb875aeSCathy Zhou 	vrrp_err_t	err = VRRP_ESYS;
7491cb875aeSCathy Zhou 
7501cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_init()");
7511cb875aeSCathy Zhou 
7521cb875aeSCathy Zhou 	TAILQ_INIT(&vrrp_vr_list);
7531cb875aeSCathy Zhou 	TAILQ_INIT(&vrrp_intf_list);
7541cb875aeSCathy Zhou 
7551cb875aeSCathy Zhou 	if (vrrp_open(&vrrpd_vh) != VRRP_SUCCESS) {
7561cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrp_open() failed");
7571cb875aeSCathy Zhou 		goto fail;
7581cb875aeSCathy Zhou 	}
7591cb875aeSCathy Zhou 
7601cb875aeSCathy Zhou 	if ((vrrpd_timerq = iu_tq_create()) == NULL) {
7611cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init(): iu_tq_create() failed");
7621cb875aeSCathy Zhou 		goto fail;
7631cb875aeSCathy Zhou 	}
7641cb875aeSCathy Zhou 
7651cb875aeSCathy Zhou 	if ((vrrpd_eh = iu_eh_create()) == NULL) {
7661cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init(): iu_eh_create() failed");
7671cb875aeSCathy Zhou 		goto fail;
7681cb875aeSCathy Zhou 	}
7691cb875aeSCathy Zhou 
7701cb875aeSCathy Zhou 	/*
7711cb875aeSCathy Zhou 	 * Create the AF_UNIX socket used to communicate with libvrrpadm.
7721cb875aeSCathy Zhou 	 *
7731cb875aeSCathy Zhou 	 * This socket is used to receive the administrative requests and
7741cb875aeSCathy Zhou 	 * send back the results.
7751cb875aeSCathy Zhou 	 */
7761cb875aeSCathy Zhou 	if (vrrpd_cmdsock_create() != VRRP_SUCCESS) {
7771cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrpd_cmdsock_create() "
7781cb875aeSCathy Zhou 		    "failed");
7791cb875aeSCathy Zhou 		goto fail;
7801cb875aeSCathy Zhou 	}
7811cb875aeSCathy Zhou 
7821cb875aeSCathy Zhou 	/*
7831cb875aeSCathy Zhou 	 * Create the VRRP control socket used to bring up/down the virtual
7841cb875aeSCathy Zhou 	 * IP addresses. It is also used to set the IFF_NOACCEPT flag of
7851cb875aeSCathy Zhou 	 * the virtual IP addresses.
7861cb875aeSCathy Zhou 	 */
7871cb875aeSCathy Zhou 	if (vrrpd_ctlsock_create() != VRRP_SUCCESS) {
7881cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrpd_ctlsock_create() "
7891cb875aeSCathy Zhou 		    "failed");
7901cb875aeSCathy Zhou 		goto fail;
7911cb875aeSCathy Zhou 	}
7921cb875aeSCathy Zhou 
7931cb875aeSCathy Zhou 	/*
7941cb875aeSCathy Zhou 	 * Create the PF_ROUTER socket used to listen to the routing socket
7951cb875aeSCathy Zhou 	 * messages and build the interface/IP address list.
7961cb875aeSCathy Zhou 	 */
7971cb875aeSCathy Zhou 	if (vrrpd_rtsock_create() != VRRP_SUCCESS) {
7981cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init(): vrrpd_rtsock_create() "
7991cb875aeSCathy Zhou 		    "failed");
8001cb875aeSCathy Zhou 		goto fail;
8011cb875aeSCathy Zhou 	}
8021cb875aeSCathy Zhou 
803f6da83d4SAnurag S. Maskey 	/* Open the libipadm handle */
804f6da83d4SAnurag S. Maskey 	if (ipadm_open(&vrrp_ipadm_handle, 0) != IPADM_SUCCESS) {
805f6da83d4SAnurag S. Maskey 		vrrp_log(VRRP_ERR, "vrrpd_init(): ipadm_open() failed");
806f6da83d4SAnurag S. Maskey 		goto fail;
807f6da83d4SAnurag S. Maskey 	}
808f6da83d4SAnurag S. Maskey 
8091cb875aeSCathy Zhou 	/*
8101cb875aeSCathy Zhou 	 * Build the list of interfaces and IP addresses. Also, start the time
8111cb875aeSCathy Zhou 	 * to scan the interfaces/IP addresses periodically.
8121cb875aeSCathy Zhou 	 */
8131cb875aeSCathy Zhou 	vrrpd_scan(AF_INET);
8141cb875aeSCathy Zhou 	vrrpd_scan(AF_INET6);
8151cb875aeSCathy Zhou 	if ((vrrp_scan_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
8161cb875aeSCathy Zhou 	    vrrpd_scan_interval, vrrpd_scan_timer, NULL)) == -1) {
8171cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init(): start scan_timer failed");
8181cb875aeSCathy Zhou 		goto fail;
8191cb875aeSCathy Zhou 	}
8201cb875aeSCathy Zhou 
8211cb875aeSCathy Zhou 	/*
8221cb875aeSCathy Zhou 	 * Initialize the VRRP multicast address.
8231cb875aeSCathy Zhou 	 */
8241cb875aeSCathy Zhou 	bzero(&vrrp_muladdr4, sizeof (vrrp_addr_t));
8251cb875aeSCathy Zhou 	vrrp_muladdr4.in4.sin_family = AF_INET;
8261cb875aeSCathy Zhou 	(void) inet_pton(AF_INET, "224.0.0.18", &vrrp_muladdr4.in4.sin_addr);
8271cb875aeSCathy Zhou 
8281cb875aeSCathy Zhou 	bzero(&vrrp_muladdr6, sizeof (vrrp_addr_t));
8291cb875aeSCathy Zhou 	vrrp_muladdr6.in6.sin6_family = AF_INET6;
8301cb875aeSCathy Zhou 	(void) inet_pton(AF_INET6, "ff02::12", &vrrp_muladdr6.in6.sin6_addr);
8311cb875aeSCathy Zhou 
8321cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
8331cb875aeSCathy Zhou 
8341cb875aeSCathy Zhou fail:
8351cb875aeSCathy Zhou 	vrrpd_fini();
8361cb875aeSCathy Zhou 	return (err);
8371cb875aeSCathy Zhou }
8381cb875aeSCathy Zhou 
8391cb875aeSCathy Zhou static void
vrrpd_fini()8401cb875aeSCathy Zhou vrrpd_fini()
8411cb875aeSCathy Zhou {
8421cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_fini()");
8431cb875aeSCathy Zhou 
8441cb875aeSCathy Zhou 	(void) iu_cancel_timer(vrrpd_timerq, vrrp_scan_timer_id, NULL);
8451cb875aeSCathy Zhou 	vrrp_scan_timer_id = -1;
8461cb875aeSCathy Zhou 
8471cb875aeSCathy Zhou 	vrrpd_rtsock_destroy();
8481cb875aeSCathy Zhou 	vrrpd_ctlsock_destroy();
8491cb875aeSCathy Zhou 	vrrpd_cmdsock_destroy();
8501cb875aeSCathy Zhou 
8511cb875aeSCathy Zhou 	if (vrrpd_eh != NULL) {
8521cb875aeSCathy Zhou 		iu_eh_destroy(vrrpd_eh);
8531cb875aeSCathy Zhou 		vrrpd_eh = NULL;
8541cb875aeSCathy Zhou 	}
8551cb875aeSCathy Zhou 
8561cb875aeSCathy Zhou 	if (vrrpd_timerq != NULL) {
8571cb875aeSCathy Zhou 		iu_tq_destroy(vrrpd_timerq);
8581cb875aeSCathy Zhou 		vrrpd_timerq = NULL;
8591cb875aeSCathy Zhou 	}
8601cb875aeSCathy Zhou 
8611cb875aeSCathy Zhou 	vrrp_close(vrrpd_vh);
8621cb875aeSCathy Zhou 	vrrpd_vh = NULL;
8631cb875aeSCathy Zhou 	assert(TAILQ_EMPTY(&vrrp_vr_list));
8641cb875aeSCathy Zhou 	assert(TAILQ_EMPTY(&vrrp_intf_list));
865f6da83d4SAnurag S. Maskey 
866f6da83d4SAnurag S. Maskey 	ipadm_close(vrrp_ipadm_handle);
8671cb875aeSCathy Zhou }
8681cb875aeSCathy Zhou 
8691cb875aeSCathy Zhou static void
vrrpd_cleanup(void)8701cb875aeSCathy Zhou vrrpd_cleanup(void)
8711cb875aeSCathy Zhou {
8721cb875aeSCathy Zhou 	vrrp_vr_t	*vr;
8731cb875aeSCathy Zhou 	vrrp_intf_t	*intf;
8741cb875aeSCathy Zhou 
8751cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_cleanup()");
8761cb875aeSCathy Zhou 
8771cb875aeSCathy Zhou 	while (!TAILQ_EMPTY(&vrrp_vr_list)) {
8781cb875aeSCathy Zhou 		vr = TAILQ_FIRST(&vrrp_vr_list);
8791cb875aeSCathy Zhou 		vrrpd_delete_vr(vr);
8801cb875aeSCathy Zhou 	}
8811cb875aeSCathy Zhou 
8821cb875aeSCathy Zhou 	while (!TAILQ_EMPTY(&vrrp_intf_list)) {
8831cb875aeSCathy Zhou 		intf = TAILQ_FIRST(&vrrp_intf_list);
8841cb875aeSCathy Zhou 		vrrpd_delete_if(intf, _B_FALSE);
8851cb875aeSCathy Zhou 	}
8861cb875aeSCathy Zhou 
8871cb875aeSCathy Zhou 	vrrpd_fini();
8881cb875aeSCathy Zhou 	closelog();
8891cb875aeSCathy Zhou 	exit(1);
8901cb875aeSCathy Zhou }
8911cb875aeSCathy Zhou 
8921cb875aeSCathy Zhou /*
8931cb875aeSCathy Zhou  * Read the configuration file and initialize all the existing VRRP routers.
8941cb875aeSCathy Zhou  */
8951cb875aeSCathy Zhou static void
vrrpd_initconf()8961cb875aeSCathy Zhou vrrpd_initconf()
8971cb875aeSCathy Zhou {
8981cb875aeSCathy Zhou 	FILE *fp;
8991cb875aeSCathy Zhou 	char line[LINE_MAX];
9001cb875aeSCathy Zhou 	int linenum = 0;
9011cb875aeSCathy Zhou 	vrrp_vr_conf_t conf;
9021cb875aeSCathy Zhou 	vrrp_err_t err;
9031cb875aeSCathy Zhou 
9041cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_initconf()");
9051cb875aeSCathy Zhou 
9061cb875aeSCathy Zhou 	if ((fp = fopen(vrrpd_conffile, "rF")) == NULL) {
9071cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "failed to open the configuration file %s",
9081cb875aeSCathy Zhou 		    vrrpd_conffile);
9091cb875aeSCathy Zhou 		return;
9101cb875aeSCathy Zhou 	}
9111cb875aeSCathy Zhou 
9121cb875aeSCathy Zhou 	while (fgets(line, sizeof (line), fp) != NULL) {
9131cb875aeSCathy Zhou 		linenum++;
9141cb875aeSCathy Zhou 		conf.vvc_vrid = VRRP_VRID_NONE;
9151cb875aeSCathy Zhou 		if ((err = vrrpd_read_vrconf(line, &conf)) != VRRP_SUCCESS) {
9161cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "failed to parse %d line %s",
9171cb875aeSCathy Zhou 			    linenum, line);
9181cb875aeSCathy Zhou 			continue;
9191cb875aeSCathy Zhou 		}
9201cb875aeSCathy Zhou 
9211cb875aeSCathy Zhou 		/*
9221cb875aeSCathy Zhou 		 * Blank or comment line
9231cb875aeSCathy Zhou 		 */
9241cb875aeSCathy Zhou 		if (conf.vvc_vrid == VRRP_VRID_NONE)
9251cb875aeSCathy Zhou 			continue;
9261cb875aeSCathy Zhou 
9271cb875aeSCathy Zhou 		/*
9281cb875aeSCathy Zhou 		 * No need to update the configuration since the VRRP router
9291cb875aeSCathy Zhou 		 * created/enabled based on the existing configuration.
9301cb875aeSCathy Zhou 		 */
9311cb875aeSCathy Zhou 		if ((err = vrrpd_create(&conf, _B_FALSE)) != VRRP_SUCCESS) {
9321cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "VRRP router %s creation failed: "
9331cb875aeSCathy Zhou 			    "%s", conf.vvc_name, vrrp_err2str(err));
9341cb875aeSCathy Zhou 			continue;
9351cb875aeSCathy Zhou 		}
9361cb875aeSCathy Zhou 
9371cb875aeSCathy Zhou 		if (conf.vvc_enabled &&
9381cb875aeSCathy Zhou 		    ((err = vrrpd_enable(conf.vvc_name, _B_FALSE)) !=
9391cb875aeSCathy Zhou 		    VRRP_SUCCESS)) {
9401cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "VRRP router %s enable failed: %s",
9411cb875aeSCathy Zhou 			    conf.vvc_name, vrrp_err2str(err));
9421cb875aeSCathy Zhou 		}
9431cb875aeSCathy Zhou 	}
9441cb875aeSCathy Zhou 
9451cb875aeSCathy Zhou 	(void) fclose(fp);
9461cb875aeSCathy Zhou }
9471cb875aeSCathy Zhou 
9481cb875aeSCathy Zhou /*
9491cb875aeSCathy Zhou  * Create the AF_UNIX socket used to communicate with libvrrpadm.
9501cb875aeSCathy Zhou  *
9511cb875aeSCathy Zhou  * This socket is used to receive the administrative request and
9521cb875aeSCathy Zhou  * send back the results.
9531cb875aeSCathy Zhou  */
9541cb875aeSCathy Zhou static vrrp_err_t
vrrpd_cmdsock_create()9551cb875aeSCathy Zhou vrrpd_cmdsock_create()
9561cb875aeSCathy Zhou {
9571cb875aeSCathy Zhou 	iu_event_id_t		eid;
9581cb875aeSCathy Zhou 	struct sockaddr_un	laddr;
9591cb875aeSCathy Zhou 	int			sock, flags;
9601cb875aeSCathy Zhou 
9611cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_cmdsock_create()");
9621cb875aeSCathy Zhou 
9631cb875aeSCathy Zhou 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
9641cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): socket(AF_UNIX) "
9651cb875aeSCathy Zhou 		    "failed: %s", strerror(errno));
9661cb875aeSCathy Zhou 		return (VRRP_ESYS);
9671cb875aeSCathy Zhou 	}
9681cb875aeSCathy Zhou 
9691cb875aeSCathy Zhou 	/*
9701cb875aeSCathy Zhou 	 * Set it to be non-blocking.
9711cb875aeSCathy Zhou 	 */
9721cb875aeSCathy Zhou 	flags = fcntl(sock, F_GETFL, 0);
9731cb875aeSCathy Zhou 	(void) fcntl(sock, F_SETFL, (flags | O_NONBLOCK));
9741cb875aeSCathy Zhou 
9751cb875aeSCathy Zhou 	/*
9761cb875aeSCathy Zhou 	 * Unlink first in case a previous daemon instance exited ungracefully.
9771cb875aeSCathy Zhou 	 */
9781cb875aeSCathy Zhou 	(void) unlink(VRRPD_SOCKET);
9791cb875aeSCathy Zhou 
9801cb875aeSCathy Zhou 	bzero(&laddr, sizeof (laddr));
9811cb875aeSCathy Zhou 	laddr.sun_family = AF_UNIX;
9821cb875aeSCathy Zhou 	(void) strlcpy(laddr.sun_path, VRRPD_SOCKET, sizeof (laddr.sun_path));
9831cb875aeSCathy Zhou 	if (bind(sock, (struct sockaddr *)&laddr, sizeof (laddr)) < 0) {
9841cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): bind() failed: %s",
9851cb875aeSCathy Zhou 		    strerror(errno));
9861cb875aeSCathy Zhou 		(void) close(sock);
9871cb875aeSCathy Zhou 		return (VRRP_ESYS);
9881cb875aeSCathy Zhou 	}
9891cb875aeSCathy Zhou 
9901cb875aeSCathy Zhou 	if (listen(sock, 30) < 0) {
9911cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): listen() "
9921cb875aeSCathy Zhou 		    "failed: %s", strerror(errno));
9931cb875aeSCathy Zhou 		(void) close(sock);
9941cb875aeSCathy Zhou 		return (VRRP_ESYS);
9951cb875aeSCathy Zhou 	}
9961cb875aeSCathy Zhou 
9971cb875aeSCathy Zhou 	if ((eid = iu_register_event(vrrpd_eh, sock, POLLIN,
9981cb875aeSCathy Zhou 	    vrrpd_cmdsock_handler, NULL)) == -1) {
9991cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_create(): iu_register_event()"
10001cb875aeSCathy Zhou 		    " failed");
10011cb875aeSCathy Zhou 		(void) close(sock);
10021cb875aeSCathy Zhou 		return (VRRP_ESYS);
10031cb875aeSCathy Zhou 	}
10041cb875aeSCathy Zhou 
10051cb875aeSCathy Zhou 	vrrpd_cmdsock_fd = sock;
10061cb875aeSCathy Zhou 	vrrpd_cmdsock_eid = eid;
10071cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
10081cb875aeSCathy Zhou }
10091cb875aeSCathy Zhou 
10101cb875aeSCathy Zhou static void
vrrpd_cmdsock_destroy()10111cb875aeSCathy Zhou vrrpd_cmdsock_destroy()
10121cb875aeSCathy Zhou {
10131cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_cmdsock_destroy()");
10141cb875aeSCathy Zhou 
10151cb875aeSCathy Zhou 	(void) iu_unregister_event(vrrpd_eh, vrrpd_cmdsock_eid, NULL);
10161cb875aeSCathy Zhou 	(void) close(vrrpd_cmdsock_fd);
10171cb875aeSCathy Zhou 	vrrpd_cmdsock_fd = -1;
10181cb875aeSCathy Zhou 	vrrpd_cmdsock_eid = -1;
10191cb875aeSCathy Zhou }
10201cb875aeSCathy Zhou 
10211cb875aeSCathy Zhou /*
10221cb875aeSCathy Zhou  * Create the PF_ROUTER sockets used to listen to the routing socket
10231cb875aeSCathy Zhou  * messages and build the interface/IP address list. Create one for
10241cb875aeSCathy Zhou  * each address family (IPv4 and IPv6).
10251cb875aeSCathy Zhou  */
10261cb875aeSCathy Zhou static vrrp_err_t
vrrpd_rtsock_create()10271cb875aeSCathy Zhou vrrpd_rtsock_create()
10281cb875aeSCathy Zhou {
10291cb875aeSCathy Zhou 	int		i, flags, sock;
10301cb875aeSCathy Zhou 	iu_event_id_t	eid;
10311cb875aeSCathy Zhou 
10321cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_rtsock_create()");
10331cb875aeSCathy Zhou 
10341cb875aeSCathy Zhou 	for (i = 0; i < 2; i++) {
10351cb875aeSCathy Zhou 		sock = socket(PF_ROUTE, SOCK_RAW, vrrpd_rtsocks[i].vrt_af);
10361cb875aeSCathy Zhou 		if (sock == -1) {
10371cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): socket() "
10381cb875aeSCathy Zhou 			    "failed: %s", strerror(errno));
10391cb875aeSCathy Zhou 			break;
10401cb875aeSCathy Zhou 		}
10411cb875aeSCathy Zhou 
10421cb875aeSCathy Zhou 		/*
10431cb875aeSCathy Zhou 		 * Set it to be non-blocking.
10441cb875aeSCathy Zhou 		 */
10451cb875aeSCathy Zhou 		if ((flags = fcntl(sock, F_GETFL, 0)) < 0) {
10461cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): "
10471cb875aeSCathy Zhou 			    "fcntl(F_GETFL) failed: %s", strerror(errno));
10481cb875aeSCathy Zhou 			break;
10491cb875aeSCathy Zhou 		}
10501cb875aeSCathy Zhou 
10511cb875aeSCathy Zhou 		if ((fcntl(sock, F_SETFL, flags | O_NONBLOCK)) < 0) {
10521cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): "
10531cb875aeSCathy Zhou 			    "fcntl(F_SETFL) failed: %s", strerror(errno));
10541cb875aeSCathy Zhou 			break;
10551cb875aeSCathy Zhou 		}
10561cb875aeSCathy Zhou 
10571cb875aeSCathy Zhou 		if ((eid = iu_register_event(vrrpd_eh, sock, POLLIN,
10581cb875aeSCathy Zhou 		    vrrpd_rtsock_handler, &(vrrpd_rtsocks[i].vrt_af))) == -1) {
10591cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_rtsock_create(): register "
10601cb875aeSCathy Zhou 			    "rtsock %d(%s) failed", sock,
10611cb875aeSCathy Zhou 			    af_str(vrrpd_rtsocks[i].vrt_af));
10621cb875aeSCathy Zhou 			break;
10631cb875aeSCathy Zhou 		}
10641cb875aeSCathy Zhou 
10651cb875aeSCathy Zhou 		vrrpd_rtsocks[i].vrt_fd = sock;
10661cb875aeSCathy Zhou 		vrrpd_rtsocks[i].vrt_eid = eid;
10671cb875aeSCathy Zhou 	}
10681cb875aeSCathy Zhou 
10691cb875aeSCathy Zhou 	if (i != 2) {
10701cb875aeSCathy Zhou 		(void) close(sock);
10711cb875aeSCathy Zhou 		vrrpd_rtsock_destroy();
10721cb875aeSCathy Zhou 		return (VRRP_ESYS);
10731cb875aeSCathy Zhou 	}
10741cb875aeSCathy Zhou 
10751cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
10761cb875aeSCathy Zhou }
10771cb875aeSCathy Zhou 
10781cb875aeSCathy Zhou static void
vrrpd_rtsock_destroy()10791cb875aeSCathy Zhou vrrpd_rtsock_destroy()
10801cb875aeSCathy Zhou {
10811cb875aeSCathy Zhou 	int		i;
10821cb875aeSCathy Zhou 
10831cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_rtsock_destroy()");
10841cb875aeSCathy Zhou 	for (i = 0; i < 2; i++) {
10851cb875aeSCathy Zhou 		(void) iu_unregister_event(vrrpd_eh, vrrpd_rtsocks[i].vrt_eid,
10861cb875aeSCathy Zhou 		    NULL);
10871cb875aeSCathy Zhou 		(void) close(vrrpd_rtsocks[i].vrt_fd);
10881cb875aeSCathy Zhou 		vrrpd_rtsocks[i].vrt_eid = -1;
10891cb875aeSCathy Zhou 		vrrpd_rtsocks[i].vrt_fd = -1;
10901cb875aeSCathy Zhou 	}
10911cb875aeSCathy Zhou }
10921cb875aeSCathy Zhou 
10931cb875aeSCathy Zhou /*
10941cb875aeSCathy Zhou  * Create the VRRP control socket used to bring up/down the virtual
10951cb875aeSCathy Zhou  * IP addresses. It is also used to set the IFF_NOACCEPT flag of
10961cb875aeSCathy Zhou  * the virtual IP addresses.
10971cb875aeSCathy Zhou  */
10981cb875aeSCathy Zhou static vrrp_err_t
vrrpd_ctlsock_create()10991cb875aeSCathy Zhou vrrpd_ctlsock_create()
11001cb875aeSCathy Zhou {
11011cb875aeSCathy Zhou 	int	s, s6;
11021cb875aeSCathy Zhou 	int	on = _B_TRUE;
11031cb875aeSCathy Zhou 
11041cb875aeSCathy Zhou 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
11051cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): socket(INET) "
11061cb875aeSCathy Zhou 		    "failed: %s", strerror(errno));
11071cb875aeSCathy Zhou 		return (VRRP_ESYS);
11081cb875aeSCathy Zhou 	}
11091cb875aeSCathy Zhou 	if (setsockopt(s, SOL_SOCKET, SO_VRRP, &on, sizeof (on)) < 0) {
11101cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): "
11111cb875aeSCathy Zhou 		    "setsockopt(INET, SO_VRRP) failed: %s", strerror(errno));
11121cb875aeSCathy Zhou 		(void) close(s);
11131cb875aeSCathy Zhou 		return (VRRP_ESYS);
11141cb875aeSCathy Zhou 	}
11151cb875aeSCathy Zhou 
11161cb875aeSCathy Zhou 	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
11171cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): socket(INET6) "
11181cb875aeSCathy Zhou 		    "failed: %s", strerror(errno));
11191cb875aeSCathy Zhou 		(void) close(s);
11201cb875aeSCathy Zhou 		return (VRRP_ESYS);
11211cb875aeSCathy Zhou 	}
11221cb875aeSCathy Zhou 	if (setsockopt(s6, SOL_SOCKET, SO_VRRP, &on, sizeof (on)) < 0) {
11231cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_ctlsock_create(): "
11241cb875aeSCathy Zhou 		    "setsockopt(INET6, SO_VRRP) failed: %s", strerror(errno));
11251cb875aeSCathy Zhou 		(void) close(s);
11261cb875aeSCathy Zhou 		(void) close(s6);
11271cb875aeSCathy Zhou 		return (VRRP_ESYS);
11281cb875aeSCathy Zhou 	}
11291cb875aeSCathy Zhou 
11301cb875aeSCathy Zhou 	vrrpd_ctlsock_fd = s;
11311cb875aeSCathy Zhou 	vrrpd_ctlsock6_fd = s6;
11321cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
11331cb875aeSCathy Zhou }
11341cb875aeSCathy Zhou 
11351cb875aeSCathy Zhou static void
vrrpd_ctlsock_destroy()11361cb875aeSCathy Zhou vrrpd_ctlsock_destroy()
11371cb875aeSCathy Zhou {
11381cb875aeSCathy Zhou 	(void) close(vrrpd_ctlsock_fd);
11391cb875aeSCathy Zhou 	vrrpd_ctlsock_fd = -1;
11401cb875aeSCathy Zhou 	(void) close(vrrpd_ctlsock6_fd);
11411cb875aeSCathy Zhou 	vrrpd_ctlsock6_fd = -1;
11421cb875aeSCathy Zhou }
11431cb875aeSCathy Zhou 
11441cb875aeSCathy Zhou /*ARGSUSED*/
11451cb875aeSCathy Zhou static void
vrrpd_cmd_create(void * arg1,void * arg2,size_t * arg2_sz)11461cb875aeSCathy Zhou vrrpd_cmd_create(void *arg1, void *arg2, size_t *arg2_sz)
11471cb875aeSCathy Zhou {
11481cb875aeSCathy Zhou 	vrrp_cmd_create_t	*cmd = (vrrp_cmd_create_t *)arg1;
11491cb875aeSCathy Zhou 	vrrp_ret_create_t	*ret = (vrrp_ret_create_t *)arg2;
11501cb875aeSCathy Zhou 	vrrp_err_t		err;
11511cb875aeSCathy Zhou 
11521cb875aeSCathy Zhou 	err = vrrpd_create(&cmd->vcc_conf, _B_TRUE);
11531cb875aeSCathy Zhou 	if (err == VRRP_SUCCESS && cmd->vcc_conf.vvc_enabled) {
11541cb875aeSCathy Zhou 		/*
11551cb875aeSCathy Zhou 		 * No need to update the configuration since it is already
11561cb875aeSCathy Zhou 		 * done in the above vrrpd_create() call
11571cb875aeSCathy Zhou 		 */
11581cb875aeSCathy Zhou 		err = vrrpd_enable(cmd->vcc_conf.vvc_name, _B_FALSE);
11591cb875aeSCathy Zhou 		if (err != VRRP_SUCCESS)
11601cb875aeSCathy Zhou 			(void) vrrpd_delete(cmd->vcc_conf.vvc_name);
11611cb875aeSCathy Zhou 	}
11621cb875aeSCathy Zhou 	ret->vrc_err = err;
11631cb875aeSCathy Zhou }
11641cb875aeSCathy Zhou 
11651cb875aeSCathy Zhou /*ARGSUSED*/
11661cb875aeSCathy Zhou static void
vrrpd_cmd_delete(void * arg1,void * arg2,size_t * arg2_sz)11671cb875aeSCathy Zhou vrrpd_cmd_delete(void *arg1, void *arg2, size_t *arg2_sz)
11681cb875aeSCathy Zhou {
11691cb875aeSCathy Zhou 	vrrp_cmd_delete_t	*cmd = (vrrp_cmd_delete_t *)arg1;
11701cb875aeSCathy Zhou 	vrrp_ret_delete_t	*ret = (vrrp_ret_delete_t *)arg2;
11711cb875aeSCathy Zhou 
11721cb875aeSCathy Zhou 	ret->vrd_err = vrrpd_delete(cmd->vcd_name);
11731cb875aeSCathy Zhou }
11741cb875aeSCathy Zhou 
11751cb875aeSCathy Zhou /*ARGSUSED*/
11761cb875aeSCathy Zhou static void
vrrpd_cmd_enable(void * arg1,void * arg2,size_t * arg2_sz)11771cb875aeSCathy Zhou vrrpd_cmd_enable(void *arg1, void *arg2, size_t *arg2_sz)
11781cb875aeSCathy Zhou {
11791cb875aeSCathy Zhou 	vrrp_cmd_enable_t	*cmd = (vrrp_cmd_enable_t *)arg1;
11801cb875aeSCathy Zhou 	vrrp_ret_enable_t	*ret = (vrrp_ret_enable_t *)arg2;
11811cb875aeSCathy Zhou 
11821cb875aeSCathy Zhou 	ret->vrs_err = vrrpd_enable(cmd->vcs_name, _B_TRUE);
11831cb875aeSCathy Zhou }
11841cb875aeSCathy Zhou 
11851cb875aeSCathy Zhou /*ARGSUSED*/
11861cb875aeSCathy Zhou static void
vrrpd_cmd_disable(void * arg1,void * arg2,size_t * arg2_sz)11871cb875aeSCathy Zhou vrrpd_cmd_disable(void *arg1, void *arg2, size_t *arg2_sz)
11881cb875aeSCathy Zhou {
11891cb875aeSCathy Zhou 	vrrp_cmd_disable_t	*cmd = (vrrp_cmd_disable_t *)arg1;
11901cb875aeSCathy Zhou 	vrrp_ret_disable_t	*ret = (vrrp_ret_disable_t *)arg2;
11911cb875aeSCathy Zhou 
11921cb875aeSCathy Zhou 	ret->vrx_err = vrrpd_disable(cmd->vcx_name);
11931cb875aeSCathy Zhou }
11941cb875aeSCathy Zhou 
11951cb875aeSCathy Zhou /*ARGSUSED*/
11961cb875aeSCathy Zhou static void
vrrpd_cmd_modify(void * arg1,void * arg2,size_t * arg2_sz)11971cb875aeSCathy Zhou vrrpd_cmd_modify(void *arg1, void *arg2, size_t *arg2_sz)
11981cb875aeSCathy Zhou {
11991cb875aeSCathy Zhou 	vrrp_cmd_modify_t	*cmd = (vrrp_cmd_modify_t *)arg1;
12001cb875aeSCathy Zhou 	vrrp_ret_modify_t	*ret = (vrrp_ret_modify_t *)arg2;
12011cb875aeSCathy Zhou 
12021cb875aeSCathy Zhou 	ret->vrm_err = vrrpd_modify(&cmd->vcm_conf, cmd->vcm_mask);
12031cb875aeSCathy Zhou }
12041cb875aeSCathy Zhou 
12051cb875aeSCathy Zhou static void
vrrpd_cmd_query(void * arg1,void * arg2,size_t * arg2_sz)12061cb875aeSCathy Zhou vrrpd_cmd_query(void *arg1, void *arg2, size_t *arg2_sz)
12071cb875aeSCathy Zhou {
12081cb875aeSCathy Zhou 	vrrp_cmd_query_t	*cmd = (vrrp_cmd_query_t *)arg1;
12091cb875aeSCathy Zhou 
12101cb875aeSCathy Zhou 	vrrpd_query(cmd->vcq_name, arg2, arg2_sz);
12111cb875aeSCathy Zhou }
12121cb875aeSCathy Zhou 
12131cb875aeSCathy Zhou static void
vrrpd_cmd_list(void * arg1,void * arg2,size_t * arg2_sz)12141cb875aeSCathy Zhou vrrpd_cmd_list(void *arg1, void *arg2, size_t *arg2_sz)
12151cb875aeSCathy Zhou {
12161cb875aeSCathy Zhou 	vrrp_cmd_list_t	*cmd = (vrrp_cmd_list_t *)arg1;
12171cb875aeSCathy Zhou 
12181cb875aeSCathy Zhou 	vrrpd_list(cmd->vcl_vrid, cmd->vcl_ifname, cmd->vcl_af, arg2, arg2_sz);
12191cb875aeSCathy Zhou }
12201cb875aeSCathy Zhou 
12211cb875aeSCathy Zhou /*
12221cb875aeSCathy Zhou  * Write-type requeset must have the solaris.network.vrrp authorization.
12231cb875aeSCathy Zhou  */
12241cb875aeSCathy Zhou static boolean_t
vrrp_auth_check(int connfd,vrrp_cmd_info_t * cinfo)12251cb875aeSCathy Zhou vrrp_auth_check(int connfd, vrrp_cmd_info_t *cinfo)
12261cb875aeSCathy Zhou {
12271cb875aeSCathy Zhou 	ucred_t		*cred = NULL;
12281cb875aeSCathy Zhou 	uid_t		uid;
12291cb875aeSCathy Zhou 	struct passwd	*pw;
12301cb875aeSCathy Zhou 	boolean_t	success = _B_FALSE;
12311cb875aeSCathy Zhou 
12321cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrp_auth_check()");
12331cb875aeSCathy Zhou 
12341cb875aeSCathy Zhou 	if (!cinfo->vi_setop)
12351cb875aeSCathy Zhou 		return (_B_TRUE);
12361cb875aeSCathy Zhou 
12371cb875aeSCathy Zhou 	/*
12381cb875aeSCathy Zhou 	 * Validate the credential
12391cb875aeSCathy Zhou 	 */
12401cb875aeSCathy Zhou 	if (getpeerucred(connfd, &cred) == (uid_t)-1) {
12411cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrp_auth_check(): getpeerucred() "
12421cb875aeSCathy Zhou 		    "failed: %s", strerror(errno));
12431cb875aeSCathy Zhou 		return (_B_FALSE);
12441cb875aeSCathy Zhou 	}
12451cb875aeSCathy Zhou 
12461cb875aeSCathy Zhou 	if ((uid = ucred_getruid((const ucred_t *)cred)) == (uid_t)-1) {
12471cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrp_auth_check(): ucred_getruid() "
12481cb875aeSCathy Zhou 		    "failed: %s", strerror(errno));
12491cb875aeSCathy Zhou 		goto done;
12501cb875aeSCathy Zhou 	}
12511cb875aeSCathy Zhou 
12521cb875aeSCathy Zhou 	if ((pw = getpwuid(uid)) == NULL) {
12531cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrp_auth_check(): getpwuid() failed");
12541cb875aeSCathy Zhou 		goto done;
12551cb875aeSCathy Zhou 	}
12561cb875aeSCathy Zhou 
12571cb875aeSCathy Zhou 	success = (chkauthattr("solaris.network.vrrp", pw->pw_name) == 1);
12581cb875aeSCathy Zhou 
12591cb875aeSCathy Zhou done:
12601cb875aeSCathy Zhou 	ucred_free(cred);
12611cb875aeSCathy Zhou 	return (success);
12621cb875aeSCathy Zhou }
12631cb875aeSCathy Zhou 
12641cb875aeSCathy Zhou /*
12651cb875aeSCathy Zhou  * Process the administrative request from libvrrpadm
12661cb875aeSCathy Zhou  */
12671cb875aeSCathy Zhou /* ARGSUSED */
12681cb875aeSCathy Zhou static void
vrrpd_cmdsock_handler(iu_eh_t * eh,int s,short events,iu_event_id_t id,void * arg)12691cb875aeSCathy Zhou vrrpd_cmdsock_handler(iu_eh_t *eh, int s, short events, iu_event_id_t id,
12701cb875aeSCathy Zhou     void *arg)
12711cb875aeSCathy Zhou {
12721cb875aeSCathy Zhou 	vrrp_cmd_info_t		*cinfo = NULL;
12731cb875aeSCathy Zhou 	vrrp_err_t		err = VRRP_SUCCESS;
12741cb875aeSCathy Zhou 	uchar_t			buf[BUFFSIZE], ackbuf[BUFFSIZE];
12751cb875aeSCathy Zhou 	size_t			cursize, acksize, len;
12761cb875aeSCathy Zhou 	uint32_t		cmd;
12771cb875aeSCathy Zhou 	int			connfd, i;
12781cb875aeSCathy Zhou 	struct sockaddr_in	from;
12791cb875aeSCathy Zhou 	socklen_t		fromlen;
12801cb875aeSCathy Zhou 
12811cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_cmdsock_handler()");
12821cb875aeSCathy Zhou 
12831cb875aeSCathy Zhou 	fromlen = (socklen_t)sizeof (from);
12841cb875aeSCathy Zhou 	if ((connfd = accept(s, (struct sockaddr *)&from, &fromlen)) < 0) {
12851cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler() accept(): %s",
12861cb875aeSCathy Zhou 		    strerror(errno));
12871cb875aeSCathy Zhou 		return;
12881cb875aeSCathy Zhou 	}
12891cb875aeSCathy Zhou 
12901cb875aeSCathy Zhou 	/*
12911cb875aeSCathy Zhou 	 * First get the type of the request
12921cb875aeSCathy Zhou 	 */
12931cb875aeSCathy Zhou 	cursize = 0;
12941cb875aeSCathy Zhou 	while (cursize < sizeof (uint32_t)) {
12951cb875aeSCathy Zhou 		len = read(connfd, buf + cursize,
12961cb875aeSCathy Zhou 		    sizeof (uint32_t) - cursize);
12971cb875aeSCathy Zhou 		if (len == (size_t)-1 && (errno == EAGAIN || errno == EINTR)) {
12981cb875aeSCathy Zhou 			continue;
12991cb875aeSCathy Zhou 		} else if (len > 0) {
13001cb875aeSCathy Zhou 			cursize += len;
13011cb875aeSCathy Zhou 			continue;
13021cb875aeSCathy Zhou 		}
13031cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): invalid message "
13041cb875aeSCathy Zhou 		    "length");
13051cb875aeSCathy Zhou 		(void) close(connfd);
13061cb875aeSCathy Zhou 		return;
13071cb875aeSCathy Zhou 	}
13081cb875aeSCathy Zhou 
13091cb875aeSCathy Zhou 	/* LINTED E_BAD_PTR_CAST_ALIGN */
13101cb875aeSCathy Zhou 	cmd = ((vrrp_cmd_t *)buf)->vc_cmd;
13111cb875aeSCathy Zhou 	for (i = 0; i < VRRP_DOOR_INFO_TABLE_SIZE; i++) {
13121cb875aeSCathy Zhou 		if (vrrp_cmd_info_tbl[i].vi_cmd == cmd) {
13131cb875aeSCathy Zhou 			cinfo = vrrp_cmd_info_tbl + i;
13141cb875aeSCathy Zhou 			break;
13151cb875aeSCathy Zhou 		}
13161cb875aeSCathy Zhou 	}
13171cb875aeSCathy Zhou 
13181cb875aeSCathy Zhou 	if (cinfo == NULL) {
13191cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): invalid request "
13201cb875aeSCathy Zhou 		    "type %d", cmd);
13211cb875aeSCathy Zhou 		err = VRRP_EINVAL;
13221cb875aeSCathy Zhou 		goto done;
13231cb875aeSCathy Zhou 	}
13241cb875aeSCathy Zhou 
13251cb875aeSCathy Zhou 	/*
13261cb875aeSCathy Zhou 	 * Get the rest of the request.
13271cb875aeSCathy Zhou 	 */
13281cb875aeSCathy Zhou 	assert(cursize == sizeof (uint32_t));
13291cb875aeSCathy Zhou 	while (cursize < cinfo->vi_reqsize) {
13301cb875aeSCathy Zhou 		len = read(connfd, buf + cursize,
13311cb875aeSCathy Zhou 		    cinfo->vi_reqsize - cursize);
13321cb875aeSCathy Zhou 		if (len == (size_t)-1 && (errno == EAGAIN || errno == EINTR)) {
13331cb875aeSCathy Zhou 			continue;
13341cb875aeSCathy Zhou 		} else if (len > 0) {
13351cb875aeSCathy Zhou 			cursize += len;
13361cb875aeSCathy Zhou 			continue;
13371cb875aeSCathy Zhou 		}
13381cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): invalid message "
13391cb875aeSCathy Zhou 		    "length");
13401cb875aeSCathy Zhou 		err = VRRP_EINVAL;
13411cb875aeSCathy Zhou 		goto done;
13421cb875aeSCathy Zhou 	}
13431cb875aeSCathy Zhou 
13441cb875aeSCathy Zhou 	/*
13451cb875aeSCathy Zhou 	 * Validate the authorization
13461cb875aeSCathy Zhou 	 */
13471cb875aeSCathy Zhou 	if (!vrrp_auth_check(connfd, cinfo)) {
13481cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler(): "
13491cb875aeSCathy Zhou 		    "not sufficient authorization");
13501cb875aeSCathy Zhou 		err = VRRP_EPERM;
13511cb875aeSCathy Zhou 	}
13521cb875aeSCathy Zhou 
13531cb875aeSCathy Zhou done:
13541cb875aeSCathy Zhou 	/*
13551cb875aeSCathy Zhou 	 * Ack the request
13561cb875aeSCathy Zhou 	 */
13571cb875aeSCathy Zhou 	if (err != 0) {
13581cb875aeSCathy Zhou 		/* LINTED E_BAD_PTR_CAST_ALIGN */
13591cb875aeSCathy Zhou 		((vrrp_ret_t *)ackbuf)->vr_err = err;
13601cb875aeSCathy Zhou 		acksize = sizeof (vrrp_ret_t);
13611cb875aeSCathy Zhou 	} else {
13621cb875aeSCathy Zhou 		/*
13631cb875aeSCathy Zhou 		 * If the size of ack is varied, the cmdfunc callback
13641cb875aeSCathy Zhou 		 * will set the right size.
13651cb875aeSCathy Zhou 		 */
13661cb875aeSCathy Zhou 		if ((acksize = cinfo->vi_acksize) == 0)
13671cb875aeSCathy Zhou 			acksize = sizeof (ackbuf);
13681cb875aeSCathy Zhou 
13691cb875aeSCathy Zhou 		/* LINTED E_BAD_PTR_CAST_ALIGN */
13701cb875aeSCathy Zhou 		cinfo->vi_cmdfunc((vrrp_cmd_t *)buf, ackbuf, &acksize);
13711cb875aeSCathy Zhou 	}
13721cb875aeSCathy Zhou 
13731cb875aeSCathy Zhou 	/*
13741cb875aeSCathy Zhou 	 * Send the ack back.
13751cb875aeSCathy Zhou 	 */
13761cb875aeSCathy Zhou 	cursize = 0;
13771cb875aeSCathy Zhou 	while (cursize < acksize) {
13781cb875aeSCathy Zhou 		len = sendto(connfd, ackbuf + cursize, acksize - cursize,
13791cb875aeSCathy Zhou 		    0, (struct sockaddr *)&from, fromlen);
13801cb875aeSCathy Zhou 		if (len == (size_t)-1 && errno == EAGAIN) {
13811cb875aeSCathy Zhou 			continue;
13821cb875aeSCathy Zhou 		} else if (len > 0) {
13831cb875aeSCathy Zhou 			cursize += len;
13841cb875aeSCathy Zhou 			continue;
13851cb875aeSCathy Zhou 		} else {
13861cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_cmdsock_handler() failed to "
13871cb875aeSCathy Zhou 			    "ack: %s", strerror(errno));
13881cb875aeSCathy Zhou 			break;
13891cb875aeSCathy Zhou 		}
13901cb875aeSCathy Zhou 	}
13911cb875aeSCathy Zhou 
13921cb875aeSCathy Zhou 	(void) shutdown(connfd, SHUT_RDWR);
13931cb875aeSCathy Zhou 	(void) close(connfd);
13941cb875aeSCathy Zhou }
13951cb875aeSCathy Zhou 
13961cb875aeSCathy Zhou /*
13971cb875aeSCathy Zhou  * Process the routing socket messages and update the interfaces/IP addresses
13981cb875aeSCathy Zhou  * list
13991cb875aeSCathy Zhou  */
14001cb875aeSCathy Zhou /* ARGSUSED */
14011cb875aeSCathy Zhou static void
vrrpd_rtsock_handler(iu_eh_t * eh,int s,short events,iu_event_id_t id,void * arg)14021cb875aeSCathy Zhou vrrpd_rtsock_handler(iu_eh_t *eh, int s, short events,
14031cb875aeSCathy Zhou     iu_event_id_t id, void *arg)
14041cb875aeSCathy Zhou {
14051cb875aeSCathy Zhou 	char			buf[BUFFSIZE];
14061cb875aeSCathy Zhou 	struct ifa_msghdr	*ifam;
14071cb875aeSCathy Zhou 	int			nbytes;
14081cb875aeSCathy Zhou 	int			af = *(int *)arg;
14091cb875aeSCathy Zhou 	boolean_t		scanif = _B_FALSE;
14101cb875aeSCathy Zhou 
14111cb875aeSCathy Zhou 	for (;;) {
14121cb875aeSCathy Zhou 		nbytes = read(s, buf, sizeof (buf));
14131cb875aeSCathy Zhou 		if (nbytes <= 0) {
14141cb875aeSCathy Zhou 			/* No more messages */
14151cb875aeSCathy Zhou 			break;
14161cb875aeSCathy Zhou 		}
14171cb875aeSCathy Zhou 
14181cb875aeSCathy Zhou 		/* LINTED E_BAD_PTR_CAST_ALIGN */
14191cb875aeSCathy Zhou 		ifam = (struct ifa_msghdr *)buf;
14201cb875aeSCathy Zhou 		if (ifam->ifam_version != RTM_VERSION) {
14211cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_rtsock_handler(): version %d "
14221cb875aeSCathy Zhou 			    "not understood", ifam->ifam_version);
14231cb875aeSCathy Zhou 			break;
14241cb875aeSCathy Zhou 		}
14251cb875aeSCathy Zhou 
14261cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_rtsock_handler(): recv %s event",
14271cb875aeSCathy Zhou 		    rtm_event2str(ifam->ifam_type));
14281cb875aeSCathy Zhou 
14291cb875aeSCathy Zhou 		switch (ifam->ifam_type) {
14301cb875aeSCathy Zhou 		case RTM_FREEADDR:
14311cb875aeSCathy Zhou 		case RTM_CHGADDR:
14321cb875aeSCathy Zhou 		case RTM_NEWADDR:
14331cb875aeSCathy Zhou 		case RTM_DELADDR:
14341cb875aeSCathy Zhou 			/*
14351cb875aeSCathy Zhou 			 * An IP address has been created/updated/deleted or
14361cb875aeSCathy Zhou 			 * brought up/down, re-initilialize the interface/IP
14371cb875aeSCathy Zhou 			 * address list.
14381cb875aeSCathy Zhou 			 */
14391cb875aeSCathy Zhou 			scanif = _B_TRUE;
14401cb875aeSCathy Zhou 			break;
14411cb875aeSCathy Zhou 		default:
14421cb875aeSCathy Zhou 			/* Not interesting */
14431cb875aeSCathy Zhou 			break;
14441cb875aeSCathy Zhou 		}
14451cb875aeSCathy Zhou 	}
14461cb875aeSCathy Zhou 
14471cb875aeSCathy Zhou 	if (scanif)
14481cb875aeSCathy Zhou 		vrrpd_scan(af);
14491cb875aeSCathy Zhou }
14501cb875aeSCathy Zhou 
14511cb875aeSCathy Zhou /*
14521cb875aeSCathy Zhou  * Periodically scan the interface/IP addresses on the system.
14531cb875aeSCathy Zhou  */
14541cb875aeSCathy Zhou /* ARGSUSED */
14551cb875aeSCathy Zhou static void
vrrpd_scan_timer(iu_tq_t * tq,void * arg)14561cb875aeSCathy Zhou vrrpd_scan_timer(iu_tq_t *tq, void *arg)
14571cb875aeSCathy Zhou {
14581cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_scan_timer()");
14591cb875aeSCathy Zhou 	vrrpd_scan(AF_INET);
14601cb875aeSCathy Zhou 	vrrpd_scan(AF_INET6);
14611cb875aeSCathy Zhou }
14621cb875aeSCathy Zhou 
14631cb875aeSCathy Zhou /*
14641cb875aeSCathy Zhou  * Get the list of the interface/IP addresses of the specified address
14651cb875aeSCathy Zhou  * family.
14661cb875aeSCathy Zhou  */
14671cb875aeSCathy Zhou static void
vrrpd_scan(int af)14681cb875aeSCathy Zhou vrrpd_scan(int af)
14691cb875aeSCathy Zhou {
14701cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_scan(%s)", af_str(af));
14711cb875aeSCathy Zhou 
14721cb875aeSCathy Zhou again:
14731cb875aeSCathy Zhou 	vrrpd_init_ipcache(af);
14741cb875aeSCathy Zhou 
1475f6da83d4SAnurag S. Maskey 	/* If interface index changes, walk again. */
1476f6da83d4SAnurag S. Maskey 	if (vrrpd_walk_addr_info(af) != IPADM_SUCCESS)
14771cb875aeSCathy Zhou 		goto again;
14781cb875aeSCathy Zhou 
14791cb875aeSCathy Zhou 	vrrpd_update_ipcache(af);
14801cb875aeSCathy Zhou }
14811cb875aeSCathy Zhou 
14821cb875aeSCathy Zhou /*
14831cb875aeSCathy Zhou  * First mark all IP addresses of the specific address family to be removed.
14841cb875aeSCathy Zhou  * This flag will then be cleared when we walk up all the IP addresses.
14851cb875aeSCathy Zhou  */
14861cb875aeSCathy Zhou static void
vrrpd_init_ipcache(int af)14871cb875aeSCathy Zhou vrrpd_init_ipcache(int af)
14881cb875aeSCathy Zhou {
14891cb875aeSCathy Zhou 	vrrp_intf_t	*intf, *next_intf;
14901cb875aeSCathy Zhou 	vrrp_ip_t	*ip, *nextip;
14911cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
14921cb875aeSCathy Zhou 
14931cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(%s)", af_str(af));
14941cb875aeSCathy Zhou 
14951cb875aeSCathy Zhou 	next_intf = TAILQ_FIRST(&vrrp_intf_list);
14961cb875aeSCathy Zhou 	while ((intf = next_intf) != NULL) {
14971cb875aeSCathy Zhou 		next_intf = TAILQ_NEXT(intf, vvi_next);
14981cb875aeSCathy Zhou 		if (intf->vvi_af != af)
14991cb875aeSCathy Zhou 			continue;
15001cb875aeSCathy Zhou 
15011cb875aeSCathy Zhou 		/*
15021cb875aeSCathy Zhou 		 * If the interface is still marked as new, it means that this
15031cb875aeSCathy Zhou 		 * vrrpd_init_ipcache() call is a result of ifindex change,
15041cb875aeSCathy Zhou 		 * which causes the re-walk of all the interfaces (see
15051cb875aeSCathy Zhou 		 * vrrpd_add_ipaddr()), and some interfaces are still marked
15061cb875aeSCathy Zhou 		 * as new during the last walk. In this case, delete this
15071cb875aeSCathy Zhou 		 * interface with the "update_vr" argument to be _B_FALSE,
15081cb875aeSCathy Zhou 		 * since no VRRP router has been assoicated with this
15091cb875aeSCathy Zhou 		 * interface yet (the association is done in
15101cb875aeSCathy Zhou 		 * vrrpd_update_ipcache()).
15111cb875aeSCathy Zhou 		 *
15121cb875aeSCathy Zhou 		 * This interface will be re-added later if it still exists.
15131cb875aeSCathy Zhou 		 */
15141cb875aeSCathy Zhou 		if (intf->vvi_state == NODE_STATE_NEW) {
15151cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(): remove %s "
15161cb875aeSCathy Zhou 			    "(%d), may be added later", intf->vvi_ifname,
15171cb875aeSCathy Zhou 			    intf->vvi_ifindex);
15181cb875aeSCathy Zhou 			vrrpd_delete_if(intf, _B_FALSE);
15191cb875aeSCathy Zhou 			continue;
15201cb875aeSCathy Zhou 		}
15211cb875aeSCathy Zhou 
15221cb875aeSCathy Zhou 		for (ip = TAILQ_FIRST(&intf->vvi_iplist); ip != NULL;
15231cb875aeSCathy Zhou 		    ip = nextip) {
15241cb875aeSCathy Zhou 			nextip = TAILQ_NEXT(ip, vip_next);
15251cb875aeSCathy Zhou 			/* LINTED E_CONSTANT_CONDITION */
15261cb875aeSCathy Zhou 			VRRPADDR2STR(af, &ip->vip_addr, abuf,
15271cb875aeSCathy Zhou 			    INET6_ADDRSTRLEN, _B_FALSE);
15281cb875aeSCathy Zhou 
15291cb875aeSCathy Zhou 			if (ip->vip_state != NODE_STATE_NEW) {
15301cb875aeSCathy Zhou 				vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(%s/%d, "
15311cb875aeSCathy Zhou 				    "%s(%s/0x%x))", intf->vvi_ifname,
15321cb875aeSCathy Zhou 				    intf->vvi_ifindex, ip->vip_lifname,
15331cb875aeSCathy Zhou 				    abuf, ip->vip_flags);
15341cb875aeSCathy Zhou 				ip->vip_state = NODE_STATE_STALE;
15351cb875aeSCathy Zhou 				continue;
15361cb875aeSCathy Zhou 			}
15371cb875aeSCathy Zhou 
15381cb875aeSCathy Zhou 			/*
15391cb875aeSCathy Zhou 			 * If the IP is still marked as new, it means that
15401cb875aeSCathy Zhou 			 * this vrrpd_init_ipcache() call is a result of
15411cb875aeSCathy Zhou 			 * ifindex change, which causes the re-walk of all
15421cb875aeSCathy Zhou 			 * the IP addresses (see vrrpd_add_ipaddr()).
15431cb875aeSCathy Zhou 			 * Delete this IP.
15441cb875aeSCathy Zhou 			 *
15451cb875aeSCathy Zhou 			 * This IP will be readded later if it still exists.
15461cb875aeSCathy Zhou 			 */
15471cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG0, "vrrpd_init_ipcache(): remove "
15481cb875aeSCathy Zhou 			    "%s/%d , %s(%s)", intf->vvi_ifname,
15491cb875aeSCathy Zhou 			    intf->vvi_ifindex, ip->vip_lifname, abuf);
15501cb875aeSCathy Zhou 			vrrpd_delete_ip(intf, ip);
15511cb875aeSCathy Zhou 		}
15521cb875aeSCathy Zhou 	}
15531cb875aeSCathy Zhou }
15541cb875aeSCathy Zhou 
15551cb875aeSCathy Zhou /*
1556f6da83d4SAnurag S. Maskey  * Walk all the IP addresses of the given family and update its
1557f6da83d4SAnurag S. Maskey  * addresses list. Return IPADM_FAILURE if it is required to walk
15581cb875aeSCathy Zhou  * all the interfaces again (one of the interface index changes in between).
15591cb875aeSCathy Zhou  */
1560f6da83d4SAnurag S. Maskey static ipadm_status_t
vrrpd_walk_addr_info(int af)1561f6da83d4SAnurag S. Maskey vrrpd_walk_addr_info(int af)
15621cb875aeSCathy Zhou {
1563f6da83d4SAnurag S. Maskey 	ipadm_addr_info_t	*ainfo, *ainfop;
1564f6da83d4SAnurag S. Maskey 	ipadm_status_t		ipstatus;
1565f6da83d4SAnurag S. Maskey 	char			*lifname;
156664639aafSDarren Reed 	struct sockaddr_storage	stor;
1567f6da83d4SAnurag S. Maskey 	vrrp_addr_t		*addr;
15681cb875aeSCathy Zhou 	int			ifindex;
15691cb875aeSCathy Zhou 	uint64_t		flags;
15701cb875aeSCathy Zhou 
1571f6da83d4SAnurag S. Maskey 	vrrp_log(VRRP_DBG0, "vrrpd_walk_addr_info(%s)", af_str(af));
15721cb875aeSCathy Zhou 
1573f6da83d4SAnurag S. Maskey 	ipstatus = ipadm_addr_info(vrrp_ipadm_handle, NULL, &ainfo, 0, 0);
1574f6da83d4SAnurag S. Maskey 	if (ipstatus != IPADM_SUCCESS) {
1575f6da83d4SAnurag S. Maskey 		vrrp_log(VRRP_ERR, "vrrpd_walk_addr_info(%s): "
1576f6da83d4SAnurag S. Maskey 		    "ipadm_addr_info() failed: %s",
1577f6da83d4SAnurag S. Maskey 		    af_str(af), ipadm_status2str(ipstatus));
1578f6da83d4SAnurag S. Maskey 		return (IPADM_SUCCESS);
15791cb875aeSCathy Zhou 	}
15801cb875aeSCathy Zhou 
1581f6da83d4SAnurag S. Maskey 	for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) {
158264639aafSDarren Reed 		if (ainfop->ia_ifa.ifa_addr->sa_family != af)
1583f6da83d4SAnurag S. Maskey 			continue;
1584f6da83d4SAnurag S. Maskey 
1585f6da83d4SAnurag S. Maskey 		lifname = ainfop->ia_ifa.ifa_name;
1586f6da83d4SAnurag S. Maskey 		flags = ainfop->ia_ifa.ifa_flags;
158764639aafSDarren Reed 		(void) memcpy(&stor, ainfop->ia_ifa.ifa_addr, sizeof (stor));
158864639aafSDarren Reed 		addr = (vrrp_addr_t *)&stor;
1589f6da83d4SAnurag S. Maskey 
1590f6da83d4SAnurag S. Maskey 		vrrp_log(VRRP_DBG0, "vrrpd_walk_addr_info(%s): %s",
1591f6da83d4SAnurag S. Maskey 		    af_str(af), lifname);
1592f6da83d4SAnurag S. Maskey 
1593f6da83d4SAnurag S. Maskey 		/* Skip virtual/IPMP/P2P interfaces */
1594f6da83d4SAnurag S. Maskey 		if (flags & (IFF_VIRTUAL|IFF_IPMP|IFF_POINTOPOINT)) {
1595f6da83d4SAnurag S. Maskey 			vrrp_log(VRRP_DBG0, "vrrpd_walk_addr_info(%s): "
1596f6da83d4SAnurag S. Maskey 			    "skipped %s", af_str(af), lifname);
1597f6da83d4SAnurag S. Maskey 			continue;
1598f6da83d4SAnurag S. Maskey 		}
1599f6da83d4SAnurag S. Maskey 
1600f6da83d4SAnurag S. Maskey 		/* Filter out the all-zero IP address */
1601f6da83d4SAnurag S. Maskey 		if (VRRPADDR_UNSPECIFIED(af, addr))
1602f6da83d4SAnurag S. Maskey 			continue;
1603f6da83d4SAnurag S. Maskey 
1604f6da83d4SAnurag S. Maskey 		if ((ifindex = if_nametoindex(lifname)) == 0) {
16051cb875aeSCathy Zhou 			if (errno != ENXIO && errno != ENOENT) {
1606f6da83d4SAnurag S. Maskey 				vrrp_log(VRRP_ERR, "vrrpd_walk_addr_info(%s): "
1607f6da83d4SAnurag S. Maskey 				    "if_nametoindex() failed for %s: %s",
1608f6da83d4SAnurag S. Maskey 				    af_str(af), lifname, strerror(errno));
16091cb875aeSCathy Zhou 			}
1610f6da83d4SAnurag S. Maskey 			break;
16111cb875aeSCathy Zhou 		}
16121cb875aeSCathy Zhou 
16131cb875aeSCathy Zhou 		/*
1614f6da83d4SAnurag S. Maskey 		 * The interface is unplumbed/replumbed during the walk.  Try
1615f6da83d4SAnurag S. Maskey 		 * to walk the IP addresses one more time.
16161cb875aeSCathy Zhou 		 */
1617f6da83d4SAnurag S. Maskey 		if (vrrpd_add_ipaddr(lifname, af, addr, ifindex, flags)
1618f6da83d4SAnurag S. Maskey 		    == VRRP_EAGAIN) {
1619f6da83d4SAnurag S. Maskey 			ipstatus = IPADM_FAILURE;
1620f6da83d4SAnurag S. Maskey 			break;
1621f6da83d4SAnurag S. Maskey 		}
16221cb875aeSCathy Zhou 	}
16231cb875aeSCathy Zhou 
1624f6da83d4SAnurag S. Maskey 	ipadm_free_addr_info(ainfo);
1625f6da83d4SAnurag S. Maskey 	return (ipstatus);
16261cb875aeSCathy Zhou }
16271cb875aeSCathy Zhou 
16281cb875aeSCathy Zhou /*
16291cb875aeSCathy Zhou  * Given the information of each IP address, update the interface and
16301cb875aeSCathy Zhou  * IP addresses list
16311cb875aeSCathy Zhou  */
16321cb875aeSCathy Zhou static vrrp_err_t
vrrpd_add_ipaddr(char * lifname,int af,vrrp_addr_t * addr,int ifindex,uint64_t flags)16331cb875aeSCathy Zhou vrrpd_add_ipaddr(char *lifname, int af, vrrp_addr_t *addr, int ifindex,
16341cb875aeSCathy Zhou     uint64_t flags)
16351cb875aeSCathy Zhou {
16361cb875aeSCathy Zhou 	char		ifname[LIFNAMSIZ], *c;
16371cb875aeSCathy Zhou 	vrrp_intf_t	*intf;
16381cb875aeSCathy Zhou 	vrrp_ip_t	*ip;
16391cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
16401cb875aeSCathy Zhou 	vrrp_err_t	err;
16411cb875aeSCathy Zhou 
16421cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
16431cb875aeSCathy Zhou 	VRRPADDR2STR(af, addr, abuf, INET6_ADDRSTRLEN, _B_FALSE);
16441cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s, %s, %d, 0x%x)", lifname,
16451cb875aeSCathy Zhou 	    abuf, ifindex, flags);
16461cb875aeSCathy Zhou 
16471cb875aeSCathy Zhou 	/*
16481cb875aeSCathy Zhou 	 * Get the physical interface name from the logical interface name.
16491cb875aeSCathy Zhou 	 */
16501cb875aeSCathy Zhou 	(void) strlcpy(ifname, lifname, sizeof (ifname));
16511cb875aeSCathy Zhou 	if ((c = strchr(ifname, ':')) != NULL)
16521cb875aeSCathy Zhou 		*c = '\0';
16531cb875aeSCathy Zhou 
16541cb875aeSCathy Zhou 	if ((intf = vrrpd_lookup_if(ifname, af)) == NULL) {
16551cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(): %s is new", ifname);
16561cb875aeSCathy Zhou 		err = vrrpd_create_if(ifname, af, ifindex, &intf);
16571cb875aeSCathy Zhou 		if (err != VRRP_SUCCESS)
16581cb875aeSCathy Zhou 			return (err);
16591cb875aeSCathy Zhou 	} else if (intf->vvi_ifindex != ifindex) {
16601cb875aeSCathy Zhou 		/*
16611cb875aeSCathy Zhou 		 * If index changes, it means that this interface is
16621cb875aeSCathy Zhou 		 * unplumbed/replumbed since we last checked. If this
16631cb875aeSCathy Zhou 		 * interface is not used by any VRRP router, just
16641cb875aeSCathy Zhou 		 * update its ifindex, and the IP addresses list will
16651cb875aeSCathy Zhou 		 * be updated later. Otherwise, return EAGAIN to rewalk
16661cb875aeSCathy Zhou 		 * all the IP addresses from the beginning.
16671cb875aeSCathy Zhou 		 */
16681cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s) ifindex changed ",
16691cb875aeSCathy Zhou 		    "from %d to %d", ifname, intf->vvi_ifindex, ifindex);
16701cb875aeSCathy Zhou 		if (!IS_PRIMARY_INTF(intf) && !IS_VIRTUAL_INTF(intf)) {
16711cb875aeSCathy Zhou 			intf->vvi_ifindex = ifindex;
16721cb875aeSCathy Zhou 		} else {
16731cb875aeSCathy Zhou 			/*
16741cb875aeSCathy Zhou 			 * delete this interface from the list if this
16751cb875aeSCathy Zhou 			 * interface has already been assoicated with
16761cb875aeSCathy Zhou 			 * any VRRP routers.
16771cb875aeSCathy Zhou 			 */
16781cb875aeSCathy Zhou 			vrrpd_delete_if(intf, _B_TRUE);
16791cb875aeSCathy Zhou 			return (VRRP_EAGAIN);
16801cb875aeSCathy Zhou 		}
16811cb875aeSCathy Zhou 	}
16821cb875aeSCathy Zhou 
16831cb875aeSCathy Zhou 	/*
16841cb875aeSCathy Zhou 	 * Does this IP address already exist?
16851cb875aeSCathy Zhou 	 */
16861cb875aeSCathy Zhou 	TAILQ_FOREACH(ip, &intf->vvi_iplist, vip_next) {
16871cb875aeSCathy Zhou 		if (strcmp(ip->vip_lifname, lifname) == 0)
16881cb875aeSCathy Zhou 			break;
16891cb875aeSCathy Zhou 	}
16901cb875aeSCathy Zhou 
16911cb875aeSCathy Zhou 	if (ip != NULL) {
16921cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s, %s) IP exists",
16931cb875aeSCathy Zhou 		    lifname, abuf);
16941cb875aeSCathy Zhou 		ip->vip_state = NODE_STATE_NONE;
16951cb875aeSCathy Zhou 		ip->vip_flags = flags;
16961cb875aeSCathy Zhou 		if (ipaddr_cmp(af, addr, &ip->vip_addr) != 0) {
16971cb875aeSCathy Zhou 			/*
16981cb875aeSCathy Zhou 			 * Address has been changed, mark it as new
16991cb875aeSCathy Zhou 			 * If this address is already selected as the
17001cb875aeSCathy Zhou 			 * primary IP address, the new IP will be checked
17011cb875aeSCathy Zhou 			 * to see whether it is still qualified as the
17021cb875aeSCathy Zhou 			 * primary IP address. If not, the primary IP
17031cb875aeSCathy Zhou 			 * address will be reselected.
17041cb875aeSCathy Zhou 			 */
17051cb875aeSCathy Zhou 			(void) memcpy(&ip->vip_addr, addr,
17061cb875aeSCathy Zhou 			    sizeof (vrrp_addr_t));
17071cb875aeSCathy Zhou 
17081cb875aeSCathy Zhou 			ip->vip_state = NODE_STATE_NEW;
17091cb875aeSCathy Zhou 		}
17101cb875aeSCathy Zhou 	} else {
17111cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_add_ipaddr(%s, %s) IP is new",
17121cb875aeSCathy Zhou 		    lifname, abuf);
17131cb875aeSCathy Zhou 
17141cb875aeSCathy Zhou 		err = vrrpd_create_ip(intf, lifname, addr, flags);
17151cb875aeSCathy Zhou 		if (err != VRRP_SUCCESS)
17161cb875aeSCathy Zhou 			return (err);
17171cb875aeSCathy Zhou 	}
17181cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
17191cb875aeSCathy Zhou }
17201cb875aeSCathy Zhou 
17211cb875aeSCathy Zhou /*
17221cb875aeSCathy Zhou  * Update the interface and IP addresses list. Remove the ones that have been
17231cb875aeSCathy Zhou  * staled since last time we walk the IP addresses and updated the ones that
17241cb875aeSCathy Zhou  * have been changed.
17251cb875aeSCathy Zhou  */
17261cb875aeSCathy Zhou static void
vrrpd_update_ipcache(int af)17271cb875aeSCathy Zhou vrrpd_update_ipcache(int af)
17281cb875aeSCathy Zhou {
17291cb875aeSCathy Zhou 	vrrp_intf_t	*intf, *nextif;
17301cb875aeSCathy Zhou 	vrrp_ip_t	*ip, *nextip;
17311cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
17321cb875aeSCathy Zhou 	boolean_t	primary_selected;
17331cb875aeSCathy Zhou 	boolean_t	primary_now_selected;
17341cb875aeSCathy Zhou 	boolean_t	need_reenable = _B_FALSE;
17351cb875aeSCathy Zhou 
17361cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(%s)", af_str(af));
17371cb875aeSCathy Zhou 
17381cb875aeSCathy Zhou 	nextif = TAILQ_FIRST(&vrrp_intf_list);
17391cb875aeSCathy Zhou 	while ((intf = nextif) != NULL) {
17401cb875aeSCathy Zhou 		nextif = TAILQ_NEXT(intf, vvi_next);
17411cb875aeSCathy Zhou 		if (intf->vvi_af != af)
17421cb875aeSCathy Zhou 			continue;
17431cb875aeSCathy Zhou 
17441cb875aeSCathy Zhou 		/*
17451cb875aeSCathy Zhou 		 * Does the interface already select its primary IP address?
17461cb875aeSCathy Zhou 		 */
17471cb875aeSCathy Zhou 		primary_selected = (intf->vvi_pip != NULL);
17481cb875aeSCathy Zhou 		assert(!primary_selected || IS_PRIMARY_INTF(intf));
17491cb875aeSCathy Zhou 
17501cb875aeSCathy Zhou 		/*
17511cb875aeSCathy Zhou 		 * Removed the IP addresses that have been unconfigured.
17521cb875aeSCathy Zhou 		 */
17531cb875aeSCathy Zhou 		for (ip = TAILQ_FIRST(&intf->vvi_iplist); ip != NULL;
17541cb875aeSCathy Zhou 		    ip = nextip) {
17551cb875aeSCathy Zhou 			nextip = TAILQ_NEXT(ip, vip_next);
17561cb875aeSCathy Zhou 			if (ip->vip_state != NODE_STATE_STALE)
17571cb875aeSCathy Zhou 				continue;
17581cb875aeSCathy Zhou 
17591cb875aeSCathy Zhou 			/* LINTED E_CONSTANT_CONDITION */
17601cb875aeSCathy Zhou 			VRRPADDR2STR(af, &ip->vip_addr, abuf, INET6_ADDRSTRLEN,
17611cb875aeSCathy Zhou 			    _B_FALSE);
17621cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): IP %s "
17631cb875aeSCathy Zhou 			    "is removed over %s", abuf, intf->vvi_ifname);
17641cb875aeSCathy Zhou 			vrrpd_delete_ip(intf, ip);
17651cb875aeSCathy Zhou 		}
17661cb875aeSCathy Zhou 
17671cb875aeSCathy Zhou 		/*
17681cb875aeSCathy Zhou 		 * No IP addresses left, delete this interface.
17691cb875aeSCathy Zhou 		 */
17701cb875aeSCathy Zhou 		if (TAILQ_EMPTY(&intf->vvi_iplist)) {
17711cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): "
17721cb875aeSCathy Zhou 			    "no IP left over %s", intf->vvi_ifname);
17731cb875aeSCathy Zhou 			vrrpd_delete_if(intf, _B_TRUE);
17741cb875aeSCathy Zhou 			continue;
17751cb875aeSCathy Zhou 		}
17761cb875aeSCathy Zhou 
17771cb875aeSCathy Zhou 		/*
17781cb875aeSCathy Zhou 		 * If this is selected ss the physical interface for any
17791cb875aeSCathy Zhou 		 * VRRP router, reselect the primary address if needed.
17801cb875aeSCathy Zhou 		 */
17811cb875aeSCathy Zhou 		if (IS_PRIMARY_INTF(intf)) {
17821cb875aeSCathy Zhou 			vrrpd_reselect_primary(intf);
17831cb875aeSCathy Zhou 			primary_now_selected = (intf->vvi_pip != NULL);
17841cb875aeSCathy Zhou 
17851cb875aeSCathy Zhou 			/*
17861cb875aeSCathy Zhou 			 * Cannot find the new primary IP address.
17871cb875aeSCathy Zhou 			 */
17881cb875aeSCathy Zhou 			if (primary_selected && !primary_now_selected) {
17891cb875aeSCathy Zhou 				vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache() "
17901cb875aeSCathy Zhou 				    "reselect primary IP on %s failed",
17911cb875aeSCathy Zhou 				    intf->vvi_ifname);
17921cb875aeSCathy Zhou 				vrrpd_remove_if(intf, _B_TRUE);
17931cb875aeSCathy Zhou 			} else if (!primary_selected && primary_now_selected) {
17941cb875aeSCathy Zhou 				/*
17951cb875aeSCathy Zhou 				 * The primary IP address is successfully
17961cb875aeSCathy Zhou 				 * selected on the physical interfacew we
17971cb875aeSCathy Zhou 				 * need to walk through all the VRRP routers
17981cb875aeSCathy Zhou 				 * that is created on this physical interface
17991cb875aeSCathy Zhou 				 * and see whether they can now be enabled.
18001cb875aeSCathy Zhou 				 */
18011cb875aeSCathy Zhou 				need_reenable = _B_TRUE;
18021cb875aeSCathy Zhou 			}
18031cb875aeSCathy Zhou 		}
18041cb875aeSCathy Zhou 
18051cb875aeSCathy Zhou 		/*
18061cb875aeSCathy Zhou 		 * For every new virtual IP address, bring up/down it based
18071cb875aeSCathy Zhou 		 * on the state of VRRP router.
18081cb875aeSCathy Zhou 		 *
18091cb875aeSCathy Zhou 		 * Note that it is fine to not update the IP's vip_flags field
18101cb875aeSCathy Zhou 		 * even if vrrpd_virtualip_updateone() changed the address's
18111cb875aeSCathy Zhou 		 * up/down state, since the vip_flags field is only used for
18121cb875aeSCathy Zhou 		 * select primary IP address over a physical interface, and
18131cb875aeSCathy Zhou 		 * vrrpd_virtualip_updateone() only affects the virtual IP
18141cb875aeSCathy Zhou 		 * address's status.
18151cb875aeSCathy Zhou 		 */
18161cb875aeSCathy Zhou 		for (ip = TAILQ_FIRST(&intf->vvi_iplist); ip != NULL;
18171cb875aeSCathy Zhou 		    ip = nextip) {
18181cb875aeSCathy Zhou 			nextip = TAILQ_NEXT(ip, vip_next);
18191cb875aeSCathy Zhou 			/* LINTED E_CONSTANT_CONDITION */
18201cb875aeSCathy Zhou 			VRRPADDR2STR(af, &ip->vip_addr, abuf, INET6_ADDRSTRLEN,
18211cb875aeSCathy Zhou 			    _B_FALSE);
18221cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): "
18231cb875aeSCathy Zhou 			    "IP %s over %s%s", abuf, intf->vvi_ifname,
18241cb875aeSCathy Zhou 			    ip->vip_state == NODE_STATE_NEW ? " is new" : "");
18251cb875aeSCathy Zhou 
18261cb875aeSCathy Zhou 			if (IS_VIRTUAL_INTF(intf)) {
18271cb875aeSCathy Zhou 				/*
18281cb875aeSCathy Zhou 				 * If this IP is new, update its up/down state
18291cb875aeSCathy Zhou 				 * based on the virtual interface's state
18301cb875aeSCathy Zhou 				 * (which is determined by the VRRP router's
18311cb875aeSCathy Zhou 				 * state). Otherwise, check only and prompt
18321cb875aeSCathy Zhou 				 * warnings if its up/down state has been
18331cb875aeSCathy Zhou 				 * changed.
18341cb875aeSCathy Zhou 				 */
18351cb875aeSCathy Zhou 				if (vrrpd_virtualip_updateone(intf, ip,
18361cb875aeSCathy Zhou 				    ip->vip_state == NODE_STATE_NONE) !=
18371cb875aeSCathy Zhou 				    VRRP_SUCCESS) {
18381cb875aeSCathy Zhou 					vrrp_log(VRRP_DBG0,
18391cb875aeSCathy Zhou 					    "vrrpd_update_ipcache(): "
18401cb875aeSCathy Zhou 					    "IP %s over %s update failed", abuf,
18411cb875aeSCathy Zhou 					    intf->vvi_ifname);
18421cb875aeSCathy Zhou 					vrrpd_delete_ip(intf, ip);
18431cb875aeSCathy Zhou 					continue;
18441cb875aeSCathy Zhou 				}
18451cb875aeSCathy Zhou 			}
18461cb875aeSCathy Zhou 			ip->vip_state = NODE_STATE_NONE;
18471cb875aeSCathy Zhou 		}
18481cb875aeSCathy Zhou 
18491cb875aeSCathy Zhou 		/*
18501cb875aeSCathy Zhou 		 * The IP address is deleted when it is failed to be brought
18511cb875aeSCathy Zhou 		 * up. If no IP addresses are left, delete this interface.
18521cb875aeSCathy Zhou 		 */
18531cb875aeSCathy Zhou 		if (TAILQ_EMPTY(&intf->vvi_iplist)) {
18541cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG0, "vrrpd_update_ipcache(): "
18551cb875aeSCathy Zhou 			    "no IP left over %s", intf->vvi_ifname);
18561cb875aeSCathy Zhou 			vrrpd_delete_if(intf, _B_TRUE);
18571cb875aeSCathy Zhou 			continue;
18581cb875aeSCathy Zhou 		}
18591cb875aeSCathy Zhou 
18601cb875aeSCathy Zhou 		if (intf->vvi_state == NODE_STATE_NEW) {
18611cb875aeSCathy Zhou 			/*
18621cb875aeSCathy Zhou 			 * A new interface is found. This interface can be
18631cb875aeSCathy Zhou 			 * the primary interface or the virtual VNIC
18641cb875aeSCathy Zhou 			 * interface.  Again, we need to walk throught all
18651cb875aeSCathy Zhou 			 * the VRRP routers to see whether some of them can
18661cb875aeSCathy Zhou 			 * now be enabled because of the new primary IP
18671cb875aeSCathy Zhou 			 * address or the new virtual IP addresses.
18681cb875aeSCathy Zhou 			 */
18691cb875aeSCathy Zhou 			intf->vvi_state = NODE_STATE_NONE;
18701cb875aeSCathy Zhou 			need_reenable = _B_TRUE;
18711cb875aeSCathy Zhou 		}
18721cb875aeSCathy Zhou 	}
18731cb875aeSCathy Zhou 
18741cb875aeSCathy Zhou 	if (need_reenable)
18751cb875aeSCathy Zhou 		vrrpd_reenable_all_vr();
18761cb875aeSCathy Zhou }
18771cb875aeSCathy Zhou 
18781cb875aeSCathy Zhou /*
18791cb875aeSCathy Zhou  * Reselect primary IP if:
18801cb875aeSCathy Zhou  * - The existing primary IP is no longer qualified (removed or it is down or
18811cb875aeSCathy Zhou  *   not a link-local IP for IPv6 VRRP router);
18821cb875aeSCathy Zhou  * - This is a physical interface but no primary IP is chosen;
18831cb875aeSCathy Zhou  */
18841cb875aeSCathy Zhou static void
vrrpd_reselect_primary(vrrp_intf_t * intf)18851cb875aeSCathy Zhou vrrpd_reselect_primary(vrrp_intf_t *intf)
18861cb875aeSCathy Zhou {
18871cb875aeSCathy Zhou 	vrrp_ip_t	*ip;
18881cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
18891cb875aeSCathy Zhou 
18901cb875aeSCathy Zhou 	assert(IS_PRIMARY_INTF(intf));
18911cb875aeSCathy Zhou 
18921cb875aeSCathy Zhou 	/*
18931cb875aeSCathy Zhou 	 * If the interface's old primary IP address is still valid, return
18941cb875aeSCathy Zhou 	 */
18951cb875aeSCathy Zhou 	if (((ip = intf->vvi_pip) != NULL) && (QUALIFY_PRIMARY_ADDR(intf, ip)))
18961cb875aeSCathy Zhou 		return;
18971cb875aeSCathy Zhou 
18981cb875aeSCathy Zhou 	if (ip != NULL) {
18991cb875aeSCathy Zhou 		/* LINTED E_CONSTANT_CONDITION */
19001cb875aeSCathy Zhou 		VRRPADDR2STR(intf->vvi_af, &ip->vip_addr, abuf,
19011cb875aeSCathy Zhou 		    sizeof (abuf), _B_FALSE);
19021cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_reselect_primary(%s): primary IP %s "
19031cb875aeSCathy Zhou 		    "is no longer qualified", intf->vvi_ifname, abuf);
19041cb875aeSCathy Zhou 	}
19051cb875aeSCathy Zhou 
19061cb875aeSCathy Zhou 	ip = vrrpd_select_primary(intf);
19071cb875aeSCathy Zhou 	intf->vvi_pip = ip;
19081cb875aeSCathy Zhou 
19091cb875aeSCathy Zhou 	if (ip != NULL) {
19101cb875aeSCathy Zhou 		/* LINTED E_CONSTANT_CONDITION */
19111cb875aeSCathy Zhou 		VRRPADDR2STR(intf->vvi_af, &ip->vip_addr, abuf,
19121cb875aeSCathy Zhou 		    sizeof (abuf), _B_FALSE);
19131cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_reselect_primary(%s): primary IP %s "
19141cb875aeSCathy Zhou 		    "is selected", intf->vvi_ifname, abuf);
19151cb875aeSCathy Zhou 	}
19161cb875aeSCathy Zhou }
19171cb875aeSCathy Zhou 
19181cb875aeSCathy Zhou /*
19191cb875aeSCathy Zhou  * Select the primary IP address. Since the link-local IP address is always
19201cb875aeSCathy Zhou  * at the head of the IP address list, try to find the first UP IP address
19211cb875aeSCathy Zhou  * and see whether it qualify.
19221cb875aeSCathy Zhou  */
19231cb875aeSCathy Zhou static vrrp_ip_t *
vrrpd_select_primary(vrrp_intf_t * pif)19241cb875aeSCathy Zhou vrrpd_select_primary(vrrp_intf_t *pif)
19251cb875aeSCathy Zhou {
19261cb875aeSCathy Zhou 	vrrp_ip_t	*pip;
19271cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
19281cb875aeSCathy Zhou 
19291cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_select_primary(%s)", pif->vvi_ifname);
19301cb875aeSCathy Zhou 
19311cb875aeSCathy Zhou 	TAILQ_FOREACH(pip, &pif->vvi_iplist, vip_next) {
19321cb875aeSCathy Zhou 		assert(pip->vip_state != NODE_STATE_STALE);
19331cb875aeSCathy Zhou 
19341cb875aeSCathy Zhou 		/* LINTED E_CONSTANT_CONDITION */
19351cb875aeSCathy Zhou 		VRRPADDR2STR(pif->vvi_af, &pip->vip_addr, abuf,
19361cb875aeSCathy Zhou 		    INET6_ADDRSTRLEN, _B_FALSE);
19371cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_select_primary(%s): %s is %s",
19381cb875aeSCathy Zhou 		    pif->vvi_ifname, abuf,
19391cb875aeSCathy Zhou 		    (pip->vip_flags & IFF_UP) ? "up" : "down");
19401cb875aeSCathy Zhou 
19411cb875aeSCathy Zhou 		if (pip->vip_flags & IFF_UP)
19421cb875aeSCathy Zhou 			break;
19431cb875aeSCathy Zhou 	}
19441cb875aeSCathy Zhou 
19451cb875aeSCathy Zhou 	/*
19461cb875aeSCathy Zhou 	 * Is this valid primary IP address?
19471cb875aeSCathy Zhou 	 */
19481cb875aeSCathy Zhou 	if (pip == NULL || !QUALIFY_PRIMARY_ADDR(pif, pip)) {
19491cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_select_primary(%s/%s) failed",
19501cb875aeSCathy Zhou 		    pif->vvi_ifname, af_str(pif->vvi_af));
19511cb875aeSCathy Zhou 		return (NULL);
19521cb875aeSCathy Zhou 	}
19531cb875aeSCathy Zhou 	return (pip);
19541cb875aeSCathy Zhou }
19551cb875aeSCathy Zhou 
19561cb875aeSCathy Zhou /*
19571cb875aeSCathy Zhou  * This is a new interface. Check whether any VRRP router is waiting for it
19581cb875aeSCathy Zhou  */
19591cb875aeSCathy Zhou static void
vrrpd_reenable_all_vr()19601cb875aeSCathy Zhou vrrpd_reenable_all_vr()
19611cb875aeSCathy Zhou {
19621cb875aeSCathy Zhou 	vrrp_vr_t *vr;
19631cb875aeSCathy Zhou 
19641cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_reenable_all_vr()");
19651cb875aeSCathy Zhou 
19661cb875aeSCathy Zhou 	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
19671cb875aeSCathy Zhou 		if (vr->vvr_conf.vvc_enabled)
19681cb875aeSCathy Zhou 			(void) vrrpd_enable_vr(vr);
19691cb875aeSCathy Zhou 	}
19701cb875aeSCathy Zhou }
19711cb875aeSCathy Zhou 
19721cb875aeSCathy Zhou /*
19731cb875aeSCathy Zhou  * If primary_addr_gone is _B_TRUE, it means that we failed to select
19741cb875aeSCathy Zhou  * the primary IP address on this (physical) interface; otherwise,
19751cb875aeSCathy Zhou  * it means the interface is no longer available.
19761cb875aeSCathy Zhou  */
19771cb875aeSCathy Zhou static void
vrrpd_remove_if(vrrp_intf_t * intf,boolean_t primary_addr_gone)19781cb875aeSCathy Zhou vrrpd_remove_if(vrrp_intf_t *intf, boolean_t primary_addr_gone)
19791cb875aeSCathy Zhou {
19801cb875aeSCathy Zhou 	vrrp_vr_t *vr;
19811cb875aeSCathy Zhou 
19821cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_remove_if(%s): %s", intf->vvi_ifname,
19831cb875aeSCathy Zhou 	    primary_addr_gone ? "primary address gone" : "interface deleted");
19841cb875aeSCathy Zhou 
19851cb875aeSCathy Zhou 	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
19861cb875aeSCathy Zhou 		if (vr->vvr_conf.vvc_enabled)
19871cb875aeSCathy Zhou 			vrrpd_disable_vr(vr, intf, primary_addr_gone);
19881cb875aeSCathy Zhou 	}
19891cb875aeSCathy Zhou }
19901cb875aeSCathy Zhou 
19911cb875aeSCathy Zhou /*
19921cb875aeSCathy Zhou  * Update the VRRP configuration file based on the given configuration.
19931cb875aeSCathy Zhou  * op is either VRRP_CONF_UPDATE or VRRP_CONF_DELETE
19941cb875aeSCathy Zhou  */
19951cb875aeSCathy Zhou static vrrp_err_t
vrrpd_updateconf(vrrp_vr_conf_t * newconf,uint_t op)19961cb875aeSCathy Zhou vrrpd_updateconf(vrrp_vr_conf_t *newconf, uint_t op)
19971cb875aeSCathy Zhou {
19981cb875aeSCathy Zhou 	vrrp_vr_conf_t	conf;
19991cb875aeSCathy Zhou 	FILE		*fp, *nfp;
20001cb875aeSCathy Zhou 	int		nfd;
20011cb875aeSCathy Zhou 	char		line[LINE_MAX];
20021cb875aeSCathy Zhou 	char		newfile[MAXPATHLEN];
20031cb875aeSCathy Zhou 	boolean_t	found = _B_FALSE;
20041cb875aeSCathy Zhou 	vrrp_err_t	err = VRRP_SUCCESS;
20051cb875aeSCathy Zhou 
20061cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_updateconf(%s, %s)", newconf->vvc_name,
20071cb875aeSCathy Zhou 	    op == VRRP_CONF_UPDATE ? "update" : "delete");
20081cb875aeSCathy Zhou 
20091cb875aeSCathy Zhou 	if ((fp = fopen(vrrpd_conffile, "r+F")) == NULL) {
20102954adb0SRob Gulewich 		if (errno != ENOENT) {
20112954adb0SRob Gulewich 			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): open %s for "
20122954adb0SRob Gulewich 			    "update failed: %s", vrrpd_conffile,
20132954adb0SRob Gulewich 			    strerror(errno));
20141cb875aeSCathy Zhou 			return (VRRP_EDB);
20151cb875aeSCathy Zhou 		}
20161cb875aeSCathy Zhou 
20172954adb0SRob Gulewich 		if ((fp = fopen(vrrpd_conffile, "w+F")) == NULL) {
20182954adb0SRob Gulewich 			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): open %s for "
20192954adb0SRob Gulewich 			    "write failed: %s", vrrpd_conffile,
20202954adb0SRob Gulewich 			    strerror(errno));
20212954adb0SRob Gulewich 			return (VRRP_EDB);
20222954adb0SRob Gulewich 		}
20232954adb0SRob Gulewich 	}
20242954adb0SRob Gulewich 
20251cb875aeSCathy Zhou 	(void) snprintf(newfile, MAXPATHLEN, "%s.new", vrrpd_conffile);
20261cb875aeSCathy Zhou 	if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
20271cb875aeSCathy Zhou 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
20281cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): open %s failed: %s",
20291cb875aeSCathy Zhou 		    newfile, strerror(errno));
20301cb875aeSCathy Zhou 		(void) fclose(fp);
20311cb875aeSCathy Zhou 		return (VRRP_EDB);
20321cb875aeSCathy Zhou 	}
20331cb875aeSCathy Zhou 
20341cb875aeSCathy Zhou 	if ((nfp = fdopen(nfd, "wF")) == NULL) {
20351cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): fdopen(%s) failed: %s",
20361cb875aeSCathy Zhou 		    newfile, strerror(errno));
20371cb875aeSCathy Zhou 		goto done;
20381cb875aeSCathy Zhou 	}
20391cb875aeSCathy Zhou 
20401cb875aeSCathy Zhou 	while (fgets(line, sizeof (line), fp) != NULL) {
20411cb875aeSCathy Zhou 		conf.vvc_vrid = VRRP_VRID_NONE;
20421cb875aeSCathy Zhou 		if (!found && (err = vrrpd_read_vrconf(line, &conf)) !=
20431cb875aeSCathy Zhou 		    VRRP_SUCCESS) {
20441cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): invalid "
20451cb875aeSCathy Zhou 			    "configuration format: %s", line);
20461cb875aeSCathy Zhou 			goto done;
20471cb875aeSCathy Zhou 		}
20481cb875aeSCathy Zhou 
20491cb875aeSCathy Zhou 		/*
20501cb875aeSCathy Zhou 		 * Write this line out if:
20511cb875aeSCathy Zhou 		 * - this is a comment line; or
20521cb875aeSCathy Zhou 		 * - we've done updating/deleting the the given VR; or
20531cb875aeSCathy Zhou 		 * - if the name of the VR read from this line does not match
20541cb875aeSCathy Zhou 		 *   the VR name that we are about to update/delete;
20551cb875aeSCathy Zhou 		 */
20561cb875aeSCathy Zhou 		if (found || conf.vvc_vrid == VRRP_VRID_NONE ||
20571cb875aeSCathy Zhou 		    strcmp(conf.vvc_name, newconf->vvc_name) != 0) {
20581cb875aeSCathy Zhou 			if (fputs(line, nfp) != EOF)
20591cb875aeSCathy Zhou 				continue;
20601cb875aeSCathy Zhou 
20611cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
20621cb875aeSCathy Zhou 			    "write line %s", line);
20631cb875aeSCathy Zhou 			err = VRRP_EDB;
20641cb875aeSCathy Zhou 			goto done;
20651cb875aeSCathy Zhou 		}
20661cb875aeSCathy Zhou 
20671cb875aeSCathy Zhou 		/*
20681cb875aeSCathy Zhou 		 * Otherwise, update/skip the line.
20691cb875aeSCathy Zhou 		 */
20701cb875aeSCathy Zhou 		found = _B_TRUE;
20711cb875aeSCathy Zhou 		if (op == VRRP_CONF_DELETE)
20721cb875aeSCathy Zhou 			continue;
20731cb875aeSCathy Zhou 
20741cb875aeSCathy Zhou 		assert(op == VRRP_CONF_UPDATE);
20751cb875aeSCathy Zhou 		if ((err = vrrpd_write_vrconf(line, sizeof (line),
20761cb875aeSCathy Zhou 		    newconf)) != VRRP_SUCCESS) {
20771cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
20781cb875aeSCathy Zhou 			    "update configuration for %s", newconf->vvc_name);
20791cb875aeSCathy Zhou 			goto done;
20801cb875aeSCathy Zhou 		}
20811cb875aeSCathy Zhou 		if (fputs(line, nfp) == EOF) {
20821cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
20831cb875aeSCathy Zhou 			    "write line %s", line);
20841cb875aeSCathy Zhou 			err = VRRP_EDB;
20851cb875aeSCathy Zhou 			goto done;
20861cb875aeSCathy Zhou 		}
20871cb875aeSCathy Zhou 	}
20881cb875aeSCathy Zhou 
20891cb875aeSCathy Zhou 	/*
20901cb875aeSCathy Zhou 	 * If we get to the end of the file and have not seen the router that
20911cb875aeSCathy Zhou 	 * we are about to update, write it out.
20921cb875aeSCathy Zhou 	 */
20931cb875aeSCathy Zhou 	if (!found && op == VRRP_CONF_UPDATE) {
20941cb875aeSCathy Zhou 		if ((err = vrrpd_write_vrconf(line, sizeof (line),
20951cb875aeSCathy Zhou 		    newconf)) == VRRP_SUCCESS && fputs(line, nfp) == EOF) {
20961cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
20971cb875aeSCathy Zhou 			    "write line %s", line);
20981cb875aeSCathy Zhou 			err = VRRP_EDB;
20991cb875aeSCathy Zhou 		}
21001cb875aeSCathy Zhou 	} else if (!found && op == VRRP_CONF_DELETE) {
21011cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to find "
21021cb875aeSCathy Zhou 		    "configuation for %s", newconf->vvc_name);
21031cb875aeSCathy Zhou 		err = VRRP_ENOTFOUND;
21041cb875aeSCathy Zhou 	}
21051cb875aeSCathy Zhou 
21061cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS)
21071cb875aeSCathy Zhou 		goto done;
21081cb875aeSCathy Zhou 
21091cb875aeSCathy Zhou 	if (fflush(nfp) == EOF || rename(newfile, vrrpd_conffile) < 0) {
21101cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_updateconf(): failed to "
21111cb875aeSCathy Zhou 		    "rename file %s", newfile);
21121cb875aeSCathy Zhou 		err = VRRP_EDB;
21131cb875aeSCathy Zhou 	}
21141cb875aeSCathy Zhou 
21151cb875aeSCathy Zhou done:
21161cb875aeSCathy Zhou 	(void) fclose(fp);
21171cb875aeSCathy Zhou 	(void) fclose(nfp);
21181cb875aeSCathy Zhou 	(void) unlink(newfile);
21191cb875aeSCathy Zhou 	return (err);
21201cb875aeSCathy Zhou }
21211cb875aeSCathy Zhou 
21221cb875aeSCathy Zhou static vrrp_err_t
vrrpd_write_vrconf(char * line,size_t len,vrrp_vr_conf_t * conf)21231cb875aeSCathy Zhou vrrpd_write_vrconf(char *line, size_t len, vrrp_vr_conf_t *conf)
21241cb875aeSCathy Zhou {
21251cb875aeSCathy Zhou 	vrrp_prop_t	*prop;
21261cb875aeSCathy Zhou 	int		n, i;
21271cb875aeSCathy Zhou 
21281cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_write_vrconf(%s)", conf->vvc_name);
21291cb875aeSCathy Zhou 
21301cb875aeSCathy Zhou 	for (i = 0; i < VRRP_PROP_INFO_TABSIZE; i++) {
21311cb875aeSCathy Zhou 		prop = &vrrp_prop_info_tbl[i];
21321cb875aeSCathy Zhou 		n = snprintf(line, len, i == 0 ? "%s=" : " %s=",
21331cb875aeSCathy Zhou 		    prop->vs_propname);
21341cb875aeSCathy Zhou 		if (n < 0 || n >= len)
21351cb875aeSCathy Zhou 			break;
21361cb875aeSCathy Zhou 		len -= n;
21371cb875aeSCathy Zhou 		line += n;
21381cb875aeSCathy Zhou 		n = prop->vs_propwrite(conf, line, len);
21391cb875aeSCathy Zhou 		if (n < 0 || n >= len)
21401cb875aeSCathy Zhou 			break;
21411cb875aeSCathy Zhou 		len -= n;
21421cb875aeSCathy Zhou 		line += n;
21431cb875aeSCathy Zhou 	}
21441cb875aeSCathy Zhou 	if (i != VRRP_PROP_INFO_TABSIZE) {
21451cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_write_vrconf(%s): buffer size too"
21461cb875aeSCathy Zhou 		    "small", conf->vvc_name);
21471cb875aeSCathy Zhou 		return (VRRP_EDB);
21481cb875aeSCathy Zhou 	}
21491cb875aeSCathy Zhou 	n = snprintf(line, len, "\n");
21501cb875aeSCathy Zhou 	if (n < 0 || n >= len) {
21511cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_write_vrconf(%s): buffer size too"
21521cb875aeSCathy Zhou 		    "small", conf->vvc_name);
21531cb875aeSCathy Zhou 		return (VRRP_EDB);
21541cb875aeSCathy Zhou 	}
21551cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
21561cb875aeSCathy Zhou }
21571cb875aeSCathy Zhou 
21581cb875aeSCathy Zhou static vrrp_err_t
vrrpd_read_vrconf(char * line,vrrp_vr_conf_t * conf)21591cb875aeSCathy Zhou vrrpd_read_vrconf(char *line, vrrp_vr_conf_t *conf)
21601cb875aeSCathy Zhou {
21611cb875aeSCathy Zhou 	char		*str, *token;
21621cb875aeSCathy Zhou 	char		*next;
21631cb875aeSCathy Zhou 	vrrp_err_t	err = VRRP_SUCCESS;
21641cb875aeSCathy Zhou 	char		tmpbuf[MAXLINELEN];
21651cb875aeSCathy Zhou 
21661cb875aeSCathy Zhou 	str = tmpbuf;
21671cb875aeSCathy Zhou 	(void) strlcpy(tmpbuf, line, MAXLINELEN);
21681cb875aeSCathy Zhou 
21691cb875aeSCathy Zhou 	/*
21701cb875aeSCathy Zhou 	 * Skip leading spaces, blank lines, and comments.
21711cb875aeSCathy Zhou 	 */
21721cb875aeSCathy Zhou 	skip_whitespace(str);
21731cb875aeSCathy Zhou 	if ((str - tmpbuf == strlen(tmpbuf)) || (*str == '#')) {
21741cb875aeSCathy Zhou 		conf->vvc_vrid = VRRP_VRID_NONE;
21751cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
21761cb875aeSCathy Zhou 	}
21771cb875aeSCathy Zhou 
21781cb875aeSCathy Zhou 	/*
21791cb875aeSCathy Zhou 	 * Read each VR properties.
21801cb875aeSCathy Zhou 	 */
21811cb875aeSCathy Zhou 	for (token = strtok_r(str, " \n\t", &next); token != NULL;
21821cb875aeSCathy Zhou 	    token = strtok_r(NULL, " \n\t", &next)) {
21831cb875aeSCathy Zhou 		if ((err = vrrpd_readprop(token, conf)) != VRRP_SUCCESS)
21841cb875aeSCathy Zhou 			break;
21851cb875aeSCathy Zhou 	}
21861cb875aeSCathy Zhou 
21871cb875aeSCathy Zhou 	/* All properties read but no VRID defined */
21881cb875aeSCathy Zhou 	if (err == VRRP_SUCCESS && conf->vvc_vrid == VRRP_VRID_NONE)
21891cb875aeSCathy Zhou 		err = VRRP_EINVAL;
21901cb875aeSCathy Zhou 
21911cb875aeSCathy Zhou 	return (err);
21921cb875aeSCathy Zhou }
21931cb875aeSCathy Zhou 
21941cb875aeSCathy Zhou static vrrp_err_t
vrrpd_readprop(const char * str,vrrp_vr_conf_t * conf)21951cb875aeSCathy Zhou vrrpd_readprop(const char *str, vrrp_vr_conf_t *conf)
21961cb875aeSCathy Zhou {
21971cb875aeSCathy Zhou 	vrrp_prop_t	*prop;
21981cb875aeSCathy Zhou 	char		*pstr;
21991cb875aeSCathy Zhou 	int		i;
22001cb875aeSCathy Zhou 
22011cb875aeSCathy Zhou 	if ((pstr = strchr(str, '=')) == NULL) {
22021cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_readprop(%s): invalid property", str);
22031cb875aeSCathy Zhou 		return (VRRP_EINVAL);
22041cb875aeSCathy Zhou 	}
22051cb875aeSCathy Zhou 
22061cb875aeSCathy Zhou 	*pstr++ = '\0';
22071cb875aeSCathy Zhou 	for (i = 0; i < VRRP_PROP_INFO_TABSIZE; i++) {
22081cb875aeSCathy Zhou 		prop = &vrrp_prop_info_tbl[i];
22091cb875aeSCathy Zhou 		if (strcasecmp(str, prop->vs_propname) == 0) {
22101cb875aeSCathy Zhou 			if (prop->vs_propread(conf, pstr))
22111cb875aeSCathy Zhou 				break;
22121cb875aeSCathy Zhou 		}
22131cb875aeSCathy Zhou 	}
22141cb875aeSCathy Zhou 
22151cb875aeSCathy Zhou 	if (i == VRRP_PROP_INFO_TABSIZE) {
22161cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_readprop(%s): invalid property", str);
22171cb875aeSCathy Zhou 		return (VRRP_EINVAL);
22181cb875aeSCathy Zhou 	}
22191cb875aeSCathy Zhou 
22201cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
22211cb875aeSCathy Zhou }
22221cb875aeSCathy Zhou 
22231cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_name(vrrp_vr_conf_t * conf,const char * str)22241cb875aeSCathy Zhou vrrp_rd_prop_name(vrrp_vr_conf_t *conf, const char *str)
22251cb875aeSCathy Zhou {
22261cb875aeSCathy Zhou 	size_t size = sizeof (conf->vvc_name);
22271cb875aeSCathy Zhou 	return (strlcpy(conf->vvc_name, str, size) < size);
22281cb875aeSCathy Zhou }
22291cb875aeSCathy Zhou 
22301cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_vrid(vrrp_vr_conf_t * conf,const char * str)22311cb875aeSCathy Zhou vrrp_rd_prop_vrid(vrrp_vr_conf_t *conf, const char *str)
22321cb875aeSCathy Zhou {
22331cb875aeSCathy Zhou 	conf->vvc_vrid = strtol(str, NULL, 0);
22341cb875aeSCathy Zhou 	return (!(conf->vvc_vrid < VRRP_VRID_MIN ||
22351cb875aeSCathy Zhou 	    conf->vvc_vrid > VRRP_VRID_MAX ||
22361cb875aeSCathy Zhou 	    (conf->vvc_vrid == 0 && errno != 0)));
22371cb875aeSCathy Zhou }
22381cb875aeSCathy Zhou 
22391cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_af(vrrp_vr_conf_t * conf,const char * str)22401cb875aeSCathy Zhou vrrp_rd_prop_af(vrrp_vr_conf_t *conf, const char *str)
22411cb875aeSCathy Zhou {
22421cb875aeSCathy Zhou 	if (strcasecmp(str, "AF_INET") == 0)
22431cb875aeSCathy Zhou 		conf->vvc_af = AF_INET;
22441cb875aeSCathy Zhou 	else if (strcasecmp(str, "AF_INET6") == 0)
22451cb875aeSCathy Zhou 		conf->vvc_af = AF_INET6;
22461cb875aeSCathy Zhou 	else
22471cb875aeSCathy Zhou 		return (_B_FALSE);
22481cb875aeSCathy Zhou 	return (_B_TRUE);
22491cb875aeSCathy Zhou }
22501cb875aeSCathy Zhou 
22511cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_pri(vrrp_vr_conf_t * conf,const char * str)22521cb875aeSCathy Zhou vrrp_rd_prop_pri(vrrp_vr_conf_t *conf, const char *str)
22531cb875aeSCathy Zhou {
22541cb875aeSCathy Zhou 	conf->vvc_pri = strtol(str, NULL, 0);
22551cb875aeSCathy Zhou 	return (!(conf->vvc_pri < VRRP_PRI_MIN ||
22561cb875aeSCathy Zhou 	    conf->vvc_pri > VRRP_PRI_OWNER ||
22571cb875aeSCathy Zhou 	    (conf->vvc_pri == 0 && errno != 0)));
22581cb875aeSCathy Zhou }
22591cb875aeSCathy Zhou 
22601cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_adver_int(vrrp_vr_conf_t * conf,const char * str)22611cb875aeSCathy Zhou vrrp_rd_prop_adver_int(vrrp_vr_conf_t *conf, const char *str)
22621cb875aeSCathy Zhou {
22631cb875aeSCathy Zhou 	conf->vvc_adver_int = strtol(str, NULL, 0);
22641cb875aeSCathy Zhou 	return (!(conf->vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
22651cb875aeSCathy Zhou 	    conf->vvc_adver_int > VRRP_MAX_ADVER_INT_MAX ||
22661cb875aeSCathy Zhou 	    (conf->vvc_adver_int == 0 && errno != 0)));
22671cb875aeSCathy Zhou }
22681cb875aeSCathy Zhou 
22691cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_preempt(vrrp_vr_conf_t * conf,const char * str)22701cb875aeSCathy Zhou vrrp_rd_prop_preempt(vrrp_vr_conf_t *conf, const char *str)
22711cb875aeSCathy Zhou {
22721cb875aeSCathy Zhou 	if (strcasecmp(str, "true") == 0)
22731cb875aeSCathy Zhou 		conf->vvc_preempt = _B_TRUE;
22741cb875aeSCathy Zhou 	else if (strcasecmp(str, "false") == 0)
22751cb875aeSCathy Zhou 		conf->vvc_preempt = _B_FALSE;
22761cb875aeSCathy Zhou 	else
22771cb875aeSCathy Zhou 		return (_B_FALSE);
22781cb875aeSCathy Zhou 	return (_B_TRUE);
22791cb875aeSCathy Zhou }
22801cb875aeSCathy Zhou 
22811cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_accept(vrrp_vr_conf_t * conf,const char * str)22821cb875aeSCathy Zhou vrrp_rd_prop_accept(vrrp_vr_conf_t *conf, const char *str)
22831cb875aeSCathy Zhou {
22841cb875aeSCathy Zhou 	if (strcasecmp(str, "true") == 0)
22851cb875aeSCathy Zhou 		conf->vvc_accept = _B_TRUE;
22861cb875aeSCathy Zhou 	else if (strcasecmp(str, "false") == 0)
22871cb875aeSCathy Zhou 		conf->vvc_accept = _B_FALSE;
22881cb875aeSCathy Zhou 	else
22891cb875aeSCathy Zhou 		return (_B_FALSE);
22901cb875aeSCathy Zhou 	return (_B_TRUE);
22911cb875aeSCathy Zhou }
22921cb875aeSCathy Zhou 
22931cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_enabled(vrrp_vr_conf_t * conf,const char * str)22941cb875aeSCathy Zhou vrrp_rd_prop_enabled(vrrp_vr_conf_t *conf, const char *str)
22951cb875aeSCathy Zhou {
22961cb875aeSCathy Zhou 	if (strcasecmp(str, "enabled") == 0)
22971cb875aeSCathy Zhou 		conf->vvc_enabled = _B_TRUE;
22981cb875aeSCathy Zhou 	else if (strcasecmp(str, "disabled") == 0)
22991cb875aeSCathy Zhou 		conf->vvc_enabled = _B_FALSE;
23001cb875aeSCathy Zhou 	else
23011cb875aeSCathy Zhou 		return (_B_FALSE);
23021cb875aeSCathy Zhou 	return (_B_TRUE);
23031cb875aeSCathy Zhou }
23041cb875aeSCathy Zhou 
23051cb875aeSCathy Zhou static boolean_t
vrrp_rd_prop_ifname(vrrp_vr_conf_t * conf,const char * str)23061cb875aeSCathy Zhou vrrp_rd_prop_ifname(vrrp_vr_conf_t *conf, const char *str)
23071cb875aeSCathy Zhou {
23081cb875aeSCathy Zhou 	size_t size = sizeof (conf->vvc_link);
23091cb875aeSCathy Zhou 	return (strlcpy(conf->vvc_link, str, size) < size);
23101cb875aeSCathy Zhou }
23111cb875aeSCathy Zhou 
23121cb875aeSCathy Zhou static int
vrrp_wt_prop_name(vrrp_vr_conf_t * conf,char * str,size_t size)23131cb875aeSCathy Zhou vrrp_wt_prop_name(vrrp_vr_conf_t *conf, char *str, size_t size)
23141cb875aeSCathy Zhou {
23151cb875aeSCathy Zhou 	return (snprintf(str, size, "%s", conf->vvc_name));
23161cb875aeSCathy Zhou }
23171cb875aeSCathy Zhou 
23181cb875aeSCathy Zhou static int
vrrp_wt_prop_pri(vrrp_vr_conf_t * conf,char * str,size_t size)23191cb875aeSCathy Zhou vrrp_wt_prop_pri(vrrp_vr_conf_t *conf, char *str, size_t size)
23201cb875aeSCathy Zhou {
23211cb875aeSCathy Zhou 	return (snprintf(str, size, "%d", conf->vvc_pri));
23221cb875aeSCathy Zhou }
23231cb875aeSCathy Zhou 
23241cb875aeSCathy Zhou static int
vrrp_wt_prop_adver_int(vrrp_vr_conf_t * conf,char * str,size_t size)23251cb875aeSCathy Zhou vrrp_wt_prop_adver_int(vrrp_vr_conf_t *conf, char *str, size_t size)
23261cb875aeSCathy Zhou {
23271cb875aeSCathy Zhou 	return (snprintf(str, size, "%d", conf->vvc_adver_int));
23281cb875aeSCathy Zhou }
23291cb875aeSCathy Zhou 
23301cb875aeSCathy Zhou static int
vrrp_wt_prop_preempt(vrrp_vr_conf_t * conf,char * str,size_t size)23311cb875aeSCathy Zhou vrrp_wt_prop_preempt(vrrp_vr_conf_t *conf, char *str, size_t size)
23321cb875aeSCathy Zhou {
23331cb875aeSCathy Zhou 	return (snprintf(str, size, "%s",
23341cb875aeSCathy Zhou 	    conf->vvc_preempt ? "true" : "false"));
23351cb875aeSCathy Zhou }
23361cb875aeSCathy Zhou 
23371cb875aeSCathy Zhou static int
vrrp_wt_prop_accept(vrrp_vr_conf_t * conf,char * str,size_t size)23381cb875aeSCathy Zhou vrrp_wt_prop_accept(vrrp_vr_conf_t *conf, char *str, size_t size)
23391cb875aeSCathy Zhou {
23401cb875aeSCathy Zhou 	return (snprintf(str, size, "%s",
23411cb875aeSCathy Zhou 	    conf->vvc_accept ? "true" : "false"));
23421cb875aeSCathy Zhou }
23431cb875aeSCathy Zhou 
23441cb875aeSCathy Zhou static int
vrrp_wt_prop_enabled(vrrp_vr_conf_t * conf,char * str,size_t size)23451cb875aeSCathy Zhou vrrp_wt_prop_enabled(vrrp_vr_conf_t *conf, char *str, size_t size)
23461cb875aeSCathy Zhou {
23471cb875aeSCathy Zhou 	return (snprintf(str, size, "%s",
23481cb875aeSCathy Zhou 	    conf->vvc_enabled ? "enabled" : "disabled"));
23491cb875aeSCathy Zhou }
23501cb875aeSCathy Zhou 
23511cb875aeSCathy Zhou static int
vrrp_wt_prop_vrid(vrrp_vr_conf_t * conf,char * str,size_t size)23521cb875aeSCathy Zhou vrrp_wt_prop_vrid(vrrp_vr_conf_t *conf, char *str, size_t size)
23531cb875aeSCathy Zhou {
23541cb875aeSCathy Zhou 	return (snprintf(str, size, "%d", conf->vvc_vrid));
23551cb875aeSCathy Zhou }
23561cb875aeSCathy Zhou 
23571cb875aeSCathy Zhou static int
vrrp_wt_prop_af(vrrp_vr_conf_t * conf,char * str,size_t size)23581cb875aeSCathy Zhou vrrp_wt_prop_af(vrrp_vr_conf_t *conf, char *str, size_t size)
23591cb875aeSCathy Zhou {
23601cb875aeSCathy Zhou 	return (snprintf(str, size, "%s",
23611cb875aeSCathy Zhou 	    conf->vvc_af == AF_INET ? "AF_INET" : "AF_INET6"));
23621cb875aeSCathy Zhou }
23631cb875aeSCathy Zhou 
23641cb875aeSCathy Zhou static int
vrrp_wt_prop_ifname(vrrp_vr_conf_t * conf,char * str,size_t size)23651cb875aeSCathy Zhou vrrp_wt_prop_ifname(vrrp_vr_conf_t *conf, char *str, size_t size)
23661cb875aeSCathy Zhou {
23671cb875aeSCathy Zhou 	return (snprintf(str, size, "%s", conf->vvc_link));
23681cb875aeSCathy Zhou }
23691cb875aeSCathy Zhou 
23701cb875aeSCathy Zhou static char *
af_str(int af)23711cb875aeSCathy Zhou af_str(int af)
23721cb875aeSCathy Zhou {
23731cb875aeSCathy Zhou 	if (af == 4 || af == AF_INET)
23741cb875aeSCathy Zhou 		return ("AF_INET");
23751cb875aeSCathy Zhou 	else if (af == 6 || af == AF_INET6)
23761cb875aeSCathy Zhou 		return ("AF_INET6");
23771cb875aeSCathy Zhou 	else if (af == AF_UNSPEC)
23781cb875aeSCathy Zhou 		return ("AF_UNSPEC");
23791cb875aeSCathy Zhou 	else
23801cb875aeSCathy Zhou 		return ("AF_error");
23811cb875aeSCathy Zhou }
23821cb875aeSCathy Zhou 
23831cb875aeSCathy Zhou static vrrp_err_t
vrrpd_create_vr(vrrp_vr_conf_t * conf)23841cb875aeSCathy Zhou vrrpd_create_vr(vrrp_vr_conf_t *conf)
23851cb875aeSCathy Zhou {
23861cb875aeSCathy Zhou 	vrrp_vr_t	*vr;
23871cb875aeSCathy Zhou 
23881cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_create_vr(%s)", conf->vvc_name);
23891cb875aeSCathy Zhou 
23901cb875aeSCathy Zhou 	if ((vr = malloc(sizeof (vrrp_vr_t))) == NULL) {
23911cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_create_vr(): memory allocation for %s"
23921cb875aeSCathy Zhou 		    " failed", conf->vvc_name);
23931cb875aeSCathy Zhou 		return (VRRP_ENOMEM);
23941cb875aeSCathy Zhou 	}
23951cb875aeSCathy Zhou 
23961cb875aeSCathy Zhou 	bzero(vr, sizeof (vrrp_vr_t));
23971cb875aeSCathy Zhou 	vr->vvr_state = VRRP_STATE_NONE;
23981cb875aeSCathy Zhou 	vr->vvr_timer_id = -1;
23991cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_NONE, VRRP_STATE_INIT, vr);
24001cb875aeSCathy Zhou 	(void) memcpy(&vr->vvr_conf, conf, sizeof (vrrp_vr_conf_t));
24011cb875aeSCathy Zhou 	vr->vvr_conf.vvc_enabled = _B_FALSE;
24021cb875aeSCathy Zhou 	TAILQ_INSERT_HEAD(&vrrp_vr_list, vr, vvr_next);
24031cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
24041cb875aeSCathy Zhou }
24051cb875aeSCathy Zhou 
24061cb875aeSCathy Zhou static void
vrrpd_delete_vr(vrrp_vr_t * vr)24071cb875aeSCathy Zhou vrrpd_delete_vr(vrrp_vr_t *vr)
24081cb875aeSCathy Zhou {
24091cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_delete_vr(%s)", vr->vvr_conf.vvc_name);
24101cb875aeSCathy Zhou 	if (vr->vvr_conf.vvc_enabled)
24111cb875aeSCathy Zhou 		vrrpd_disable_vr(vr, NULL, _B_FALSE);
24121cb875aeSCathy Zhou 	assert(vr->vvr_state == VRRP_STATE_INIT);
24131cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_INIT, VRRP_STATE_NONE, vr);
24141cb875aeSCathy Zhou 	TAILQ_REMOVE(&vrrp_vr_list, vr, vvr_next);
24151cb875aeSCathy Zhou 	(void) free(vr);
24161cb875aeSCathy Zhou }
24171cb875aeSCathy Zhou 
24181cb875aeSCathy Zhou static vrrp_err_t
vrrpd_enable_vr(vrrp_vr_t * vr)24191cb875aeSCathy Zhou vrrpd_enable_vr(vrrp_vr_t *vr)
24201cb875aeSCathy Zhou {
24211cb875aeSCathy Zhou 	vrrp_err_t	rx_err, tx_err, err = VRRP_EINVAL;
24221cb875aeSCathy Zhou 
24231cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_enable_vr(%s)", vr->vvr_conf.vvc_name);
24241cb875aeSCathy Zhou 
24251cb875aeSCathy Zhou 	assert(vr->vvr_conf.vvc_enabled);
24261cb875aeSCathy Zhou 
24271cb875aeSCathy Zhou 	/*
24281cb875aeSCathy Zhou 	 * This VRRP router has been successfully enabled and start
24291cb875aeSCathy Zhou 	 * participating.
24301cb875aeSCathy Zhou 	 */
24311cb875aeSCathy Zhou 	if (vr->vvr_state != VRRP_STATE_INIT)
24321cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
24331cb875aeSCathy Zhou 
24341cb875aeSCathy Zhou 	if ((rx_err = vrrpd_init_rxsock(vr)) == VRRP_SUCCESS) {
24351cb875aeSCathy Zhou 		/*
24361cb875aeSCathy Zhou 		 * Select the primary IP address. Even if this time
24371cb875aeSCathy Zhou 		 * primary IP selection failed, we will reselect the
24381cb875aeSCathy Zhou 		 * primary IP address when new IP address comes up.
24391cb875aeSCathy Zhou 		 */
24401cb875aeSCathy Zhou 		vrrpd_reselect_primary(vr->vvr_pif);
24411cb875aeSCathy Zhou 		if (vr->vvr_pif->vvi_pip == NULL) {
24421cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG0, "vrrpd_enable_vr(%s): "
24431cb875aeSCathy Zhou 			    "select_primary over %s failed",
24441cb875aeSCathy Zhou 			    vr->vvr_conf.vvc_name, vr->vvr_pif->vvi_ifname);
24451cb875aeSCathy Zhou 			rx_err = VRRP_ENOPRIM;
24461cb875aeSCathy Zhou 		}
24471cb875aeSCathy Zhou 	}
24481cb875aeSCathy Zhou 
24491cb875aeSCathy Zhou 	/*
24501cb875aeSCathy Zhou 	 * Initialize the TX socket used for this vrrp_vr_t to send the
24511cb875aeSCathy Zhou 	 * multicast packets.
24521cb875aeSCathy Zhou 	 */
24531cb875aeSCathy Zhou 	tx_err = vrrpd_init_txsock(vr);
24541cb875aeSCathy Zhou 
24551cb875aeSCathy Zhou 	/*
24561cb875aeSCathy Zhou 	 * Only start the state transition if sockets for both RX and TX are
24571cb875aeSCathy Zhou 	 * initialized correctly.
24581cb875aeSCathy Zhou 	 */
24591cb875aeSCathy Zhou 	if (rx_err != VRRP_SUCCESS || tx_err != VRRP_SUCCESS) {
24601cb875aeSCathy Zhou 		/*
24611cb875aeSCathy Zhou 		 * Record the error information for diagnose purpose.
24621cb875aeSCathy Zhou 		 */
24631cb875aeSCathy Zhou 		vr->vvr_err = (rx_err == VRRP_SUCCESS) ? tx_err : rx_err;
24641cb875aeSCathy Zhou 		return (err);
24651cb875aeSCathy Zhou 	}
24661cb875aeSCathy Zhou 
24671cb875aeSCathy Zhou 	if (vr->vvr_conf.vvc_pri == 255)
24681cb875aeSCathy Zhou 		err = vrrpd_state_i2m(vr);
24691cb875aeSCathy Zhou 	else
24701cb875aeSCathy Zhou 		err = vrrpd_state_i2b(vr);
24711cb875aeSCathy Zhou 
24721cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
24731cb875aeSCathy Zhou 		vr->vvr_err = err;
24741cb875aeSCathy Zhou 		vr->vvr_pif->vvi_pip = NULL;
24751cb875aeSCathy Zhou 		vrrpd_fini_txsock(vr);
24761cb875aeSCathy Zhou 		vrrpd_fini_rxsock(vr);
24771cb875aeSCathy Zhou 	}
24781cb875aeSCathy Zhou 	return (err);
24791cb875aeSCathy Zhou }
24801cb875aeSCathy Zhou 
24811cb875aeSCathy Zhou /*
24821cb875aeSCathy Zhou  * Given the removed interface, see whether the given VRRP router would
24831cb875aeSCathy Zhou  * be affected and stop participating the VRRP protocol.
24841cb875aeSCathy Zhou  *
24851cb875aeSCathy Zhou  * If intf is NULL, VR disabling request is coming from the admin.
24861cb875aeSCathy Zhou  */
24871cb875aeSCathy Zhou static void
vrrpd_disable_vr(vrrp_vr_t * vr,vrrp_intf_t * intf,boolean_t primary_addr_gone)24881cb875aeSCathy Zhou vrrpd_disable_vr(vrrp_vr_t *vr, vrrp_intf_t *intf, boolean_t primary_addr_gone)
24891cb875aeSCathy Zhou {
24901cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_disable_vr(%s): %s%s", vr->vvr_conf.vvc_name,
24911cb875aeSCathy Zhou 	    intf == NULL ? "requested by admin" : intf->vvi_ifname,
24921cb875aeSCathy Zhou 	    intf == NULL ? "" : (primary_addr_gone ? "primary address gone" :
24931cb875aeSCathy Zhou 	    "interface deleted"));
24941cb875aeSCathy Zhou 
24951cb875aeSCathy Zhou 	/*
24961cb875aeSCathy Zhou 	 * An interface is deleted, see whether this interface is the
24971cb875aeSCathy Zhou 	 * physical interface or the VNIC of the given VRRP router.
24981cb875aeSCathy Zhou 	 * If so, continue to disable the VRRP router.
24991cb875aeSCathy Zhou 	 */
25001cb875aeSCathy Zhou 	if (!primary_addr_gone && (intf != NULL) && (intf != vr->vvr_pif) &&
25011cb875aeSCathy Zhou 	    (intf != vr->vvr_vif)) {
25021cb875aeSCathy Zhou 		return;
25031cb875aeSCathy Zhou 	}
25041cb875aeSCathy Zhou 
25051cb875aeSCathy Zhou 	/*
25061cb875aeSCathy Zhou 	 * If this is the case that the primary IP address is gone,
25071cb875aeSCathy Zhou 	 * and we failed to reselect another primary IP address,
25081cb875aeSCathy Zhou 	 * continue to disable the VRRP router.
25091cb875aeSCathy Zhou 	 */
25101cb875aeSCathy Zhou 	if (primary_addr_gone && intf != vr->vvr_pif)
25111cb875aeSCathy Zhou 		return;
25121cb875aeSCathy Zhou 
25131cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): disabling",
25141cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name);
25151cb875aeSCathy Zhou 
25161cb875aeSCathy Zhou 	if (vr->vvr_state == VRRP_STATE_MASTER) {
25171cb875aeSCathy Zhou 		/*
25181cb875aeSCathy Zhou 		 * If this router is disabled by the administrator, send
25191cb875aeSCathy Zhou 		 * the zero-priority advertisement to indicate the Master
25201cb875aeSCathy Zhou 		 * stops participating VRRP.
25211cb875aeSCathy Zhou 		 */
25221cb875aeSCathy Zhou 		if (intf == NULL)
25231cb875aeSCathy Zhou 			(void) vrrpd_send_adv(vr, _B_TRUE);
25241cb875aeSCathy Zhou 
25251cb875aeSCathy Zhou 		vrrpd_state_m2i(vr);
25261cb875aeSCathy Zhou 	} else  if (vr->vvr_state == VRRP_STATE_BACKUP) {
25271cb875aeSCathy Zhou 		vrrpd_state_b2i(vr);
25281cb875aeSCathy Zhou 	}
25291cb875aeSCathy Zhou 
25301cb875aeSCathy Zhou 	/*
25311cb875aeSCathy Zhou 	 * If no primary IP address can be selected, the VRRP router
25321cb875aeSCathy Zhou 	 * stays at the INIT state and will become BACKUP and MASTER when
25331cb875aeSCathy Zhou 	 * a primary IP address is reselected.
25341cb875aeSCathy Zhou 	 */
25351cb875aeSCathy Zhou 	if (primary_addr_gone) {
25361cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): primary IP "
25371cb875aeSCathy Zhou 		    "is removed", vr->vvr_conf.vvc_name);
25381cb875aeSCathy Zhou 		vr->vvr_err = VRRP_ENOPRIM;
25391cb875aeSCathy Zhou 	} else if (intf == NULL) {
25401cb875aeSCathy Zhou 		/*
25411cb875aeSCathy Zhou 		 * The VRRP router is disable by the administrator
25421cb875aeSCathy Zhou 		 */
25431cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): disabled by admin",
25441cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name);
25451cb875aeSCathy Zhou 		vr->vvr_err = VRRP_SUCCESS;
25461cb875aeSCathy Zhou 		vrrpd_fini_txsock(vr);
25471cb875aeSCathy Zhou 		vrrpd_fini_rxsock(vr);
25481cb875aeSCathy Zhou 	} else if (intf == vr->vvr_pif) {
25491cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): physical interface "
25501cb875aeSCathy Zhou 		    "%s removed", vr->vvr_conf.vvc_name, intf->vvi_ifname);
25511cb875aeSCathy Zhou 		vr->vvr_err = VRRP_ENOPRIM;
25521cb875aeSCathy Zhou 		vrrpd_fini_rxsock(vr);
25531cb875aeSCathy Zhou 	} else if (intf == vr->vvr_vif) {
25541cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_disable_vr(%s): VNIC interface %s"
25551cb875aeSCathy Zhou 		    " removed", vr->vvr_conf.vvc_name, intf->vvi_ifname);
25561cb875aeSCathy Zhou 		vr->vvr_err = VRRP_ENOVIRT;
25571cb875aeSCathy Zhou 		vrrpd_fini_txsock(vr);
25581cb875aeSCathy Zhou 	}
25591cb875aeSCathy Zhou }
25601cb875aeSCathy Zhou 
25611cb875aeSCathy Zhou vrrp_err_t
vrrpd_create(vrrp_vr_conf_t * conf,boolean_t updateconf)25621cb875aeSCathy Zhou vrrpd_create(vrrp_vr_conf_t *conf, boolean_t updateconf)
25631cb875aeSCathy Zhou {
25641cb875aeSCathy Zhou 	vrrp_err_t	err = VRRP_SUCCESS;
25651cb875aeSCathy Zhou 
25661cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_create(%s, %s, %d)", conf->vvc_name,
25671cb875aeSCathy Zhou 	    conf->vvc_link, conf->vvc_vrid);
25681cb875aeSCathy Zhou 
25691cb875aeSCathy Zhou 	assert(conf != NULL);
25701cb875aeSCathy Zhou 
25711cb875aeSCathy Zhou 	/*
25721cb875aeSCathy Zhou 	 * Sanity check
25731cb875aeSCathy Zhou 	 */
25741cb875aeSCathy Zhou 	if ((strlen(conf->vvc_name) == 0) ||
25751cb875aeSCathy Zhou 	    (strlen(conf->vvc_link) == 0) ||
25761cb875aeSCathy Zhou 	    (conf->vvc_vrid < VRRP_VRID_MIN ||
25771cb875aeSCathy Zhou 	    conf->vvc_vrid > VRRP_VRID_MAX) ||
25781cb875aeSCathy Zhou 	    (conf->vvc_pri < VRRP_PRI_MIN ||
25791cb875aeSCathy Zhou 	    conf->vvc_pri > VRRP_PRI_OWNER) ||
25801cb875aeSCathy Zhou 	    (conf->vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
25811cb875aeSCathy Zhou 	    conf->vvc_adver_int > VRRP_MAX_ADVER_INT_MAX) ||
25821cb875aeSCathy Zhou 	    (conf->vvc_af != AF_INET && conf->vvc_af != AF_INET6) ||
25831cb875aeSCathy Zhou 	    (conf->vvc_pri == VRRP_PRI_OWNER && !conf->vvc_accept)) {
25841cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_create(%s): invalid argument",
25851cb875aeSCathy Zhou 		    conf->vvc_name);
25861cb875aeSCathy Zhou 		return (VRRP_EINVAL);
25871cb875aeSCathy Zhou 	}
25881cb875aeSCathy Zhou 
25891cb875aeSCathy Zhou 	if (!vrrp_valid_name(conf->vvc_name)) {
25901cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_create(): %s is not a valid router "
25911cb875aeSCathy Zhou 		    "name", conf->vvc_name);
25921cb875aeSCathy Zhou 		return (VRRP_EINVALVRNAME);
25931cb875aeSCathy Zhou 	}
25941cb875aeSCathy Zhou 
25951cb875aeSCathy Zhou 	if (vrrpd_lookup_vr_by_name(conf->vvc_name) != NULL) {
25961cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_create(): %s already exists",
25971cb875aeSCathy Zhou 		    conf->vvc_name);
25981cb875aeSCathy Zhou 		return (VRRP_EINSTEXIST);
25991cb875aeSCathy Zhou 	}
26001cb875aeSCathy Zhou 
26011cb875aeSCathy Zhou 	if (vrrpd_lookup_vr_by_vrid(conf->vvc_link, conf->vvc_vrid,
26021cb875aeSCathy Zhou 	    conf->vvc_af) != NULL) {
26031cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_create(): VRID %d/%s over %s "
26041cb875aeSCathy Zhou 		    "already exists", conf->vvc_vrid, af_str(conf->vvc_af),
26051cb875aeSCathy Zhou 		    conf->vvc_link);
26061cb875aeSCathy Zhou 		return (VRRP_EVREXIST);
26071cb875aeSCathy Zhou 	}
26081cb875aeSCathy Zhou 
26091cb875aeSCathy Zhou 	if (updateconf && (err = vrrpd_updateconf(conf,
26101cb875aeSCathy Zhou 	    VRRP_CONF_UPDATE)) != VRRP_SUCCESS) {
26111cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_create(): failed to update "
26121cb875aeSCathy Zhou 		    "configuration for %s", conf->vvc_name);
26131cb875aeSCathy Zhou 		return (err);
26141cb875aeSCathy Zhou 	}
26151cb875aeSCathy Zhou 
26161cb875aeSCathy Zhou 	err = vrrpd_create_vr(conf);
26171cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS && updateconf)
26181cb875aeSCathy Zhou 		(void) vrrpd_updateconf(conf, VRRP_CONF_DELETE);
26191cb875aeSCathy Zhou 
26201cb875aeSCathy Zhou 	return (err);
26211cb875aeSCathy Zhou }
26221cb875aeSCathy Zhou 
26231cb875aeSCathy Zhou static vrrp_err_t
vrrpd_delete(const char * vn)26241cb875aeSCathy Zhou vrrpd_delete(const char *vn)
26251cb875aeSCathy Zhou {
26261cb875aeSCathy Zhou 	vrrp_vr_t	*vr;
26271cb875aeSCathy Zhou 	vrrp_err_t	err;
26281cb875aeSCathy Zhou 
26291cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_delete(%s)", vn);
26301cb875aeSCathy Zhou 
26311cb875aeSCathy Zhou 	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
26321cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_delete(): %s not exists", vn);
26331cb875aeSCathy Zhou 		return (VRRP_ENOTFOUND);
26341cb875aeSCathy Zhou 	}
26351cb875aeSCathy Zhou 
26361cb875aeSCathy Zhou 	err = vrrpd_updateconf(&vr->vvr_conf, VRRP_CONF_DELETE);
26371cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
26381cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_delete(): failed to delete "
26391cb875aeSCathy Zhou 		    "configuration for %s", vr->vvr_conf.vvc_name);
26401cb875aeSCathy Zhou 		return (err);
26411cb875aeSCathy Zhou 	}
26421cb875aeSCathy Zhou 
26431cb875aeSCathy Zhou 	vrrpd_delete_vr(vr);
26441cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
26451cb875aeSCathy Zhou }
26461cb875aeSCathy Zhou 
26471cb875aeSCathy Zhou static vrrp_err_t
vrrpd_enable(const char * vn,boolean_t updateconf)26481cb875aeSCathy Zhou vrrpd_enable(const char *vn, boolean_t updateconf)
26491cb875aeSCathy Zhou {
26501cb875aeSCathy Zhou 	vrrp_vr_t		*vr;
26511cb875aeSCathy Zhou 	vrrp_vr_conf_t		*conf;
26521cb875aeSCathy Zhou 	uint32_t		flags;
26531cb875aeSCathy Zhou 	datalink_class_t	class;
26541cb875aeSCathy Zhou 	vrrp_err_t		err = VRRP_SUCCESS;
26551cb875aeSCathy Zhou 
26561cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_enable(%s)", vn);
26571cb875aeSCathy Zhou 
26581cb875aeSCathy Zhou 	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
26591cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_enable(): %s does not exist", vn);
26601cb875aeSCathy Zhou 		return (VRRP_ENOTFOUND);
26611cb875aeSCathy Zhou 	}
26621cb875aeSCathy Zhou 
26631cb875aeSCathy Zhou 	/*
26641cb875aeSCathy Zhou 	 * The VR is already enabled.
26651cb875aeSCathy Zhou 	 */
26661cb875aeSCathy Zhou 	conf = &vr->vvr_conf;
26671cb875aeSCathy Zhou 	if (conf->vvc_enabled) {
26681cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_enable(): %s is already "
26691cb875aeSCathy Zhou 		    "enabled", vn);
26701cb875aeSCathy Zhou 		return (VRRP_EALREADY);
26711cb875aeSCathy Zhou 	}
26721cb875aeSCathy Zhou 
26731cb875aeSCathy Zhou 	/*
26741cb875aeSCathy Zhou 	 * Check whether the link exists.
26751cb875aeSCathy Zhou 	 */
26761cb875aeSCathy Zhou 	if ((strlen(conf->vvc_link) == 0) || dladm_name2info(vrrpd_vh->vh_dh,
26771cb875aeSCathy Zhou 	    conf->vvc_link, NULL, &flags, &class, NULL) != DLADM_STATUS_OK ||
26781cb875aeSCathy Zhou 	    !(flags & DLADM_OPT_ACTIVE) || ((class != DATALINK_CLASS_PHYS) &&
26792954adb0SRob Gulewich 	    (class != DATALINK_CLASS_VLAN) && (class != DATALINK_CLASS_AGGR) &&
26802954adb0SRob Gulewich 	    (class != DATALINK_CLASS_VNIC))) {
26811cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_enable(%s): invalid link %s",
26821cb875aeSCathy Zhou 		    vn, conf->vvc_link);
26831cb875aeSCathy Zhou 		return (VRRP_EINVALLINK);
26841cb875aeSCathy Zhou 	}
26851cb875aeSCathy Zhou 
26861cb875aeSCathy Zhou 	/*
26871cb875aeSCathy Zhou 	 * Get the associated VNIC name by the given interface/vrid/
26881cb875aeSCathy Zhou 	 * address famitly.
26891cb875aeSCathy Zhou 	 */
26901cb875aeSCathy Zhou 	err = vrrp_get_vnicname(vrrpd_vh, conf->vvc_vrid,
26911cb875aeSCathy Zhou 	    conf->vvc_af, conf->vvc_link, NULL, NULL, vr->vvr_vnic,
26921cb875aeSCathy Zhou 	    sizeof (vr->vvr_vnic));
26931cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
26941cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_enable(%s): no VNIC for VRID %d/%s "
26951cb875aeSCathy Zhou 		    "over %s", vn, conf->vvc_vrid, af_str(conf->vvc_af),
26961cb875aeSCathy Zhou 		    conf->vvc_link);
26971cb875aeSCathy Zhou 		err = VRRP_ENOVNIC;
26981cb875aeSCathy Zhou 		goto fail;
26991cb875aeSCathy Zhou 	}
27001cb875aeSCathy Zhou 
27011cb875aeSCathy Zhou 	/*
27021cb875aeSCathy Zhou 	 * Find the right VNIC, primary interface and get the list of the
27031cb875aeSCathy Zhou 	 * protected IP adressses and primary IP address. Note that if
27041cb875aeSCathy Zhou 	 * either interface is NULL (no IP addresses configured over the
27051cb875aeSCathy Zhou 	 * interface), we will still continue and mark this VRRP router
27061cb875aeSCathy Zhou 	 * as "enabled".
27071cb875aeSCathy Zhou 	 */
27081cb875aeSCathy Zhou 	vr->vvr_conf.vvc_enabled = _B_TRUE;
27091cb875aeSCathy Zhou 	if (updateconf && (err = vrrpd_updateconf(&vr->vvr_conf,
27101cb875aeSCathy Zhou 	    VRRP_CONF_UPDATE)) != VRRP_SUCCESS) {
27111cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_enable(): failed to update "
27121cb875aeSCathy Zhou 		    "configuration for %s", vr->vvr_conf.vvc_name);
27131cb875aeSCathy Zhou 		goto fail;
27141cb875aeSCathy Zhou 	}
27151cb875aeSCathy Zhou 
27161cb875aeSCathy Zhou 	/*
27171cb875aeSCathy Zhou 	 * If vrrpd_setup_vr() fails, it is possible that there is no IP
27181cb875aeSCathy Zhou 	 * addresses over ether the primary interface or the VNIC yet,
27191cb875aeSCathy Zhou 	 * return success in this case, the VRRP router will stay in
27201cb875aeSCathy Zhou 	 * the initialized state and start to work when the IP address is
27211cb875aeSCathy Zhou 	 * configured.
27221cb875aeSCathy Zhou 	 */
27231cb875aeSCathy Zhou 	(void) vrrpd_enable_vr(vr);
27241cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
27251cb875aeSCathy Zhou 
27261cb875aeSCathy Zhou fail:
27271cb875aeSCathy Zhou 	vr->vvr_conf.vvc_enabled = _B_FALSE;
27281cb875aeSCathy Zhou 	vr->vvr_vnic[0] = '\0';
27291cb875aeSCathy Zhou 	return (err);
27301cb875aeSCathy Zhou }
27311cb875aeSCathy Zhou 
27321cb875aeSCathy Zhou static vrrp_err_t
vrrpd_disable(const char * vn)27331cb875aeSCathy Zhou vrrpd_disable(const char *vn)
27341cb875aeSCathy Zhou {
27351cb875aeSCathy Zhou 	vrrp_vr_t	*vr;
27361cb875aeSCathy Zhou 	vrrp_err_t	err;
27371cb875aeSCathy Zhou 
27381cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_disable(%s)", vn);
27391cb875aeSCathy Zhou 
27401cb875aeSCathy Zhou 	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
27411cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_disable(): %s does not exist", vn);
27421cb875aeSCathy Zhou 		return (VRRP_ENOTFOUND);
27431cb875aeSCathy Zhou 	}
27441cb875aeSCathy Zhou 
27451cb875aeSCathy Zhou 	/*
27461cb875aeSCathy Zhou 	 * The VR is already disable.
27471cb875aeSCathy Zhou 	 */
27481cb875aeSCathy Zhou 	if (!vr->vvr_conf.vvc_enabled) {
27491cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_disable(): %s was not enabled", vn);
27501cb875aeSCathy Zhou 		return (VRRP_EALREADY);
27511cb875aeSCathy Zhou 	}
27521cb875aeSCathy Zhou 
27531cb875aeSCathy Zhou 	vr->vvr_conf.vvc_enabled = _B_FALSE;
27541cb875aeSCathy Zhou 	err = vrrpd_updateconf(&vr->vvr_conf, VRRP_CONF_UPDATE);
27551cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
27561cb875aeSCathy Zhou 		vr->vvr_conf.vvc_enabled = _B_TRUE;
27571cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_disable(): failed to update "
27581cb875aeSCathy Zhou 		    "configuration for %s", vr->vvr_conf.vvc_name);
27591cb875aeSCathy Zhou 		return (err);
27601cb875aeSCathy Zhou 	}
27611cb875aeSCathy Zhou 
27621cb875aeSCathy Zhou 	vrrpd_disable_vr(vr, NULL, _B_FALSE);
27631cb875aeSCathy Zhou 	vr->vvr_vnic[0] = '\0';
27641cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
27651cb875aeSCathy Zhou }
27661cb875aeSCathy Zhou 
27671cb875aeSCathy Zhou static vrrp_err_t
vrrpd_modify(vrrp_vr_conf_t * conf,uint32_t mask)27681cb875aeSCathy Zhou vrrpd_modify(vrrp_vr_conf_t *conf, uint32_t mask)
27691cb875aeSCathy Zhou {
27701cb875aeSCathy Zhou 	vrrp_vr_t	*vr;
27711cb875aeSCathy Zhou 	vrrp_vr_conf_t	savconf;
27721cb875aeSCathy Zhou 	int		pri;
27731cb875aeSCathy Zhou 	boolean_t	accept, set_accept = _B_FALSE;
27741cb875aeSCathy Zhou 	vrrp_err_t	err;
27751cb875aeSCathy Zhou 
27761cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_modify(%s)", conf->vvc_name);
27771cb875aeSCathy Zhou 
27781cb875aeSCathy Zhou 	if (mask == 0)
27791cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
27801cb875aeSCathy Zhou 
27811cb875aeSCathy Zhou 	if ((vr = vrrpd_lookup_vr_by_name(conf->vvc_name)) == NULL) {
27821cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_modify(): cannot find the given "
27831cb875aeSCathy Zhou 		    "VR instance: %s", conf->vvc_name);
27841cb875aeSCathy Zhou 		return (VRRP_ENOTFOUND);
27851cb875aeSCathy Zhou 	}
27861cb875aeSCathy Zhou 
27871cb875aeSCathy Zhou 	if (mask & VRRP_CONF_INTERVAL) {
27881cb875aeSCathy Zhou 		if (conf->vvc_adver_int < VRRP_MAX_ADVER_INT_MIN ||
27891cb875aeSCathy Zhou 		    conf->vvc_adver_int > VRRP_MAX_ADVER_INT_MAX) {
27901cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG1, "vrrpd_modify(%s): invalid "
27911cb875aeSCathy Zhou 			    "adver_interval %d", conf->vvc_name,
27921cb875aeSCathy Zhou 			    conf->vvc_adver_int);
27931cb875aeSCathy Zhou 			return (VRRP_EINVAL);
27941cb875aeSCathy Zhou 		}
27951cb875aeSCathy Zhou 	}
27961cb875aeSCathy Zhou 
27971cb875aeSCathy Zhou 	pri = vr->vvr_conf.vvc_pri;
27981cb875aeSCathy Zhou 	if (mask & VRRP_CONF_PRIORITY) {
27991cb875aeSCathy Zhou 		if (conf->vvc_pri < VRRP_PRI_MIN ||
28001cb875aeSCathy Zhou 		    conf->vvc_pri > VRRP_PRI_OWNER) {
28011cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG1, "vrrpd_modify(%s): invalid "
28021cb875aeSCathy Zhou 			    "priority %d", conf->vvc_name, conf->vvc_pri);
28031cb875aeSCathy Zhou 			return (VRRP_EINVAL);
28041cb875aeSCathy Zhou 		}
28051cb875aeSCathy Zhou 		pri = conf->vvc_pri;
28061cb875aeSCathy Zhou 	}
28071cb875aeSCathy Zhou 
28081cb875aeSCathy Zhou 	accept = vr->vvr_conf.vvc_accept;
28091cb875aeSCathy Zhou 	if (mask & VRRP_CONF_ACCEPT)
28101cb875aeSCathy Zhou 		accept = conf->vvc_accept;
28111cb875aeSCathy Zhou 
28121cb875aeSCathy Zhou 	if (pri == VRRP_PRI_OWNER && !accept) {
28131cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_modify(%s): accept mode must be "
28141cb875aeSCathy Zhou 		    "true for VRRP address owner", conf->vvc_name);
28151cb875aeSCathy Zhou 		return (VRRP_EINVAL);
28161cb875aeSCathy Zhou 	}
28171cb875aeSCathy Zhou 
28181cb875aeSCathy Zhou 	if ((mask & VRRP_CONF_ACCEPT) && (vr->vvr_conf.vvc_accept != accept)) {
28191cb875aeSCathy Zhou 		err = vrrpd_set_noaccept(vr, !accept);
28201cb875aeSCathy Zhou 		if (err != VRRP_SUCCESS) {
28211cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_modify(%s): access mode "
28221cb875aeSCathy Zhou 			    "updating failed: %s", conf->vvc_name,
28231cb875aeSCathy Zhou 			    vrrp_err2str(err));
28241cb875aeSCathy Zhou 			return (err);
28251cb875aeSCathy Zhou 		}
28261cb875aeSCathy Zhou 		set_accept = _B_TRUE;
28271cb875aeSCathy Zhou 	}
28281cb875aeSCathy Zhou 
28291cb875aeSCathy Zhou 	/*
28301cb875aeSCathy Zhou 	 * Save the current configuration, so it can be restored if the
28311cb875aeSCathy Zhou 	 * following fails.
28321cb875aeSCathy Zhou 	 */
28331cb875aeSCathy Zhou 	(void) memcpy(&savconf, &vr->vvr_conf, sizeof (vrrp_vr_conf_t));
28341cb875aeSCathy Zhou 	if (mask & VRRP_CONF_PREEMPT)
28351cb875aeSCathy Zhou 		vr->vvr_conf.vvc_preempt = conf->vvc_preempt;
28361cb875aeSCathy Zhou 
28371cb875aeSCathy Zhou 	if (mask & VRRP_CONF_ACCEPT)
28381cb875aeSCathy Zhou 		vr->vvr_conf.vvc_accept = accept;
28391cb875aeSCathy Zhou 
28401cb875aeSCathy Zhou 	if (mask & VRRP_CONF_PRIORITY)
28411cb875aeSCathy Zhou 		vr->vvr_conf.vvc_pri = pri;
28421cb875aeSCathy Zhou 
28431cb875aeSCathy Zhou 	if (mask & VRRP_CONF_INTERVAL)
28441cb875aeSCathy Zhou 		vr->vvr_conf.vvc_adver_int = conf->vvc_adver_int;
28451cb875aeSCathy Zhou 
28461cb875aeSCathy Zhou 	err = vrrpd_updateconf(&vr->vvr_conf, VRRP_CONF_UPDATE);
28471cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
28481cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_modify(%s): configuration update "
28491cb875aeSCathy Zhou 		    "failed: %s", conf->vvc_name, vrrp_err2str(err));
28501cb875aeSCathy Zhou 		if (set_accept)
28511cb875aeSCathy Zhou 			(void) vrrpd_set_noaccept(vr, accept);
28521cb875aeSCathy Zhou 		(void) memcpy(&vr->vvr_conf, &savconf, sizeof (vrrp_vr_conf_t));
28531cb875aeSCathy Zhou 		return (err);
28541cb875aeSCathy Zhou 	}
28551cb875aeSCathy Zhou 
28561cb875aeSCathy Zhou 	if ((mask & VRRP_CONF_PRIORITY) && (vr->vvr_state == VRRP_STATE_BACKUP))
28571cb875aeSCathy Zhou 		vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
28581cb875aeSCathy Zhou 
28591cb875aeSCathy Zhou 	if ((mask & VRRP_CONF_INTERVAL) && (vr->vvr_state == VRRP_STATE_MASTER))
28601cb875aeSCathy Zhou 		vr->vvr_timeout = conf->vvc_adver_int;
28611cb875aeSCathy Zhou 
28621cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
28631cb875aeSCathy Zhou }
28641cb875aeSCathy Zhou 
28651cb875aeSCathy Zhou static void
vrrpd_list(vrid_t vrid,char * ifname,int af,vrrp_ret_list_t * ret,size_t * sizep)28661cb875aeSCathy Zhou vrrpd_list(vrid_t vrid, char *ifname, int af, vrrp_ret_list_t *ret,
28671cb875aeSCathy Zhou     size_t *sizep)
28681cb875aeSCathy Zhou {
28691cb875aeSCathy Zhou 	vrrp_vr_t	*vr;
28701cb875aeSCathy Zhou 	char		*p = (char *)ret + sizeof (vrrp_ret_list_t);
28711cb875aeSCathy Zhou 	size_t		size = (*sizep) - sizeof (vrrp_ret_list_t);
28721cb875aeSCathy Zhou 
28731cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_list(%d_%s_%s)", vrid, ifname, af_str(af));
28741cb875aeSCathy Zhou 
28751cb875aeSCathy Zhou 	ret->vrl_cnt = 0;
28761cb875aeSCathy Zhou 	TAILQ_FOREACH(vr, &vrrp_vr_list, vvr_next) {
28771cb875aeSCathy Zhou 		if (vrid !=  VRRP_VRID_NONE && vr->vvr_conf.vvc_vrid != vrid)
28781cb875aeSCathy Zhou 			continue;
28791cb875aeSCathy Zhou 
28801cb875aeSCathy Zhou 		if (strlen(ifname) != 0 && strcmp(ifname,
28811cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_link) == 0) {
28821cb875aeSCathy Zhou 			continue;
28831cb875aeSCathy Zhou 		}
28841cb875aeSCathy Zhou 
28851cb875aeSCathy Zhou 		if ((af == AF_INET || af == AF_INET6) &&
28861cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_af != af)
28871cb875aeSCathy Zhou 			continue;
28881cb875aeSCathy Zhou 
28891cb875aeSCathy Zhou 		if (size < VRRP_NAME_MAX) {
28901cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG1, "vrrpd_list(): buffer size too "
28911cb875aeSCathy Zhou 			    "small to hold %d router names", ret->vrl_cnt);
28921cb875aeSCathy Zhou 			*sizep = sizeof (vrrp_ret_list_t);
28931cb875aeSCathy Zhou 			ret->vrl_err = VRRP_ETOOSMALL;
28941cb875aeSCathy Zhou 			return;
28951cb875aeSCathy Zhou 		}
28961cb875aeSCathy Zhou 		(void) strlcpy(p, vr->vvr_conf.vvc_name, VRRP_NAME_MAX);
28971cb875aeSCathy Zhou 		p += (strlen(vr->vvr_conf.vvc_name) + 1);
28981cb875aeSCathy Zhou 		ret->vrl_cnt++;
28991cb875aeSCathy Zhou 		size -= VRRP_NAME_MAX;
29001cb875aeSCathy Zhou 	}
29011cb875aeSCathy Zhou 
29021cb875aeSCathy Zhou 	*sizep = sizeof (vrrp_ret_list_t) + ret->vrl_cnt * VRRP_NAME_MAX;
29031cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_list() return %d", ret->vrl_cnt);
29041cb875aeSCathy Zhou 	ret->vrl_err = VRRP_SUCCESS;
29051cb875aeSCathy Zhou }
29061cb875aeSCathy Zhou 
29071cb875aeSCathy Zhou static void
vrrpd_query(const char * vn,vrrp_ret_query_t * ret,size_t * sizep)29081cb875aeSCathy Zhou vrrpd_query(const char *vn, vrrp_ret_query_t *ret, size_t *sizep)
29091cb875aeSCathy Zhou {
29101cb875aeSCathy Zhou 	vrrp_queryinfo_t	*infop;
29111cb875aeSCathy Zhou 	vrrp_vr_t		*vr;
29121cb875aeSCathy Zhou 	vrrp_intf_t		*vif;
29131cb875aeSCathy Zhou 	vrrp_ip_t		*ip;
29141cb875aeSCathy Zhou 	struct timeval		now;
29151cb875aeSCathy Zhou 	uint32_t		vipcnt = 0;
29161cb875aeSCathy Zhou 	size_t			size = *sizep;
29171cb875aeSCathy Zhou 
29181cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_query(%s)", vn);
29191cb875aeSCathy Zhou 
29201cb875aeSCathy Zhou 	if ((vr = vrrpd_lookup_vr_by_name(vn)) == NULL) {
29211cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_query(): %s does not exist", vn);
29221cb875aeSCathy Zhou 		*sizep = sizeof (vrrp_ret_query_t);
29231cb875aeSCathy Zhou 		ret->vrq_err = VRRP_ENOTFOUND;
29241cb875aeSCathy Zhou 		return;
29251cb875aeSCathy Zhou 	}
29261cb875aeSCathy Zhou 
29271cb875aeSCathy Zhou 	/*
29281cb875aeSCathy Zhou 	 * Get the virtual IP list if the router is not in the INIT state.
29291cb875aeSCathy Zhou 	 */
29301cb875aeSCathy Zhou 	if (vr->vvr_state != VRRP_STATE_INIT) {
29311cb875aeSCathy Zhou 		vif = vr->vvr_vif;
29321cb875aeSCathy Zhou 		TAILQ_FOREACH(ip, &vif->vvi_iplist, vip_next) {
29331cb875aeSCathy Zhou 			vipcnt++;
29341cb875aeSCathy Zhou 		}
29351cb875aeSCathy Zhou 	}
29361cb875aeSCathy Zhou 
29371cb875aeSCathy Zhou 	*sizep = sizeof (vrrp_ret_query_t);
29381cb875aeSCathy Zhou 	*sizep += (vipcnt == 0) ? 0 : (vipcnt - 1) * sizeof (vrrp_addr_t);
29391cb875aeSCathy Zhou 	if (*sizep > size) {
29401cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_query(): not enough space to hold "
29411cb875aeSCathy Zhou 		    "%d virtual IPs", vipcnt);
29421cb875aeSCathy Zhou 		*sizep = sizeof (vrrp_ret_query_t);
29431cb875aeSCathy Zhou 		ret->vrq_err = VRRP_ETOOSMALL;
29441cb875aeSCathy Zhou 		return;
29451cb875aeSCathy Zhou 	}
29461cb875aeSCathy Zhou 
29471cb875aeSCathy Zhou 	(void) gettimeofday(&now, NULL);
29481cb875aeSCathy Zhou 
29491cb875aeSCathy Zhou 	bzero(ret, *sizep);
29501cb875aeSCathy Zhou 	infop = &ret->vrq_qinfo;
29511cb875aeSCathy Zhou 	(void) memcpy(&infop->show_vi,
29521cb875aeSCathy Zhou 	    &(vr->vvr_conf), sizeof (vrrp_vr_conf_t));
29531cb875aeSCathy Zhou 	(void) memcpy(&infop->show_vs,
29541cb875aeSCathy Zhou 	    &(vr->vvr_sinfo), sizeof (vrrp_stateinfo_t));
29551cb875aeSCathy Zhou 	(void) strlcpy(infop->show_va.va_vnic, vr->vvr_vnic, MAXLINKNAMELEN);
29561cb875aeSCathy Zhou 	infop->show_vt.vt_since_last_tran = timeval_to_milli(
29571cb875aeSCathy Zhou 	    timeval_delta(now, vr->vvr_sinfo.vs_st_time));
29581cb875aeSCathy Zhou 
29591cb875aeSCathy Zhou 	if (vr->vvr_state == VRRP_STATE_INIT) {
29601cb875aeSCathy Zhou 		ret->vrq_err = VRRP_SUCCESS;
29611cb875aeSCathy Zhou 		return;
29621cb875aeSCathy Zhou 	}
29631cb875aeSCathy Zhou 
29641cb875aeSCathy Zhou 	vipcnt = 0;
29651cb875aeSCathy Zhou 	TAILQ_FOREACH(ip, &vif->vvi_iplist, vip_next) {
29661cb875aeSCathy Zhou 		(void) memcpy(&infop->show_va.va_vips[vipcnt++],
29671cb875aeSCathy Zhou 		    &ip->vip_addr, sizeof (vrrp_addr_t));
29681cb875aeSCathy Zhou 	}
29691cb875aeSCathy Zhou 	infop->show_va.va_vipcnt = vipcnt;
29701cb875aeSCathy Zhou 
29711cb875aeSCathy Zhou 	(void) memcpy(&infop->show_va.va_primary,
29721cb875aeSCathy Zhou 	    &vr->vvr_pif->vvi_pip->vip_addr, sizeof (vrrp_addr_t));
29731cb875aeSCathy Zhou 
29741cb875aeSCathy Zhou 	(void) memcpy(&infop->show_vp, &(vr->vvr_peer), sizeof (vrrp_peer_t));
29751cb875aeSCathy Zhou 
29761cb875aeSCathy Zhou 	/*
29771cb875aeSCathy Zhou 	 * Check whether there is a peer.
29781cb875aeSCathy Zhou 	 */
29791cb875aeSCathy Zhou 	if (!VRRPADDR_UNSPECIFIED(vr->vvr_conf.vvc_af,
29801cb875aeSCathy Zhou 	    &(vr->vvr_peer.vp_addr))) {
29811cb875aeSCathy Zhou 		infop->show_vt.vt_since_last_adv = timeval_to_milli(
29821cb875aeSCathy Zhou 		    timeval_delta(now, vr->vvr_peer.vp_time));
29831cb875aeSCathy Zhou 	}
29841cb875aeSCathy Zhou 
29851cb875aeSCathy Zhou 	if (vr->vvr_state == VRRP_STATE_BACKUP) {
29861cb875aeSCathy Zhou 		infop->show_vt.vt_master_down_intv =
29871cb875aeSCathy Zhou 		    MASTER_DOWN_INTERVAL_VR(vr);
29881cb875aeSCathy Zhou 	}
29891cb875aeSCathy Zhou 
29901cb875aeSCathy Zhou 	ret->vrq_err = VRRP_SUCCESS;
29911cb875aeSCathy Zhou }
29921cb875aeSCathy Zhou 
29931cb875aeSCathy Zhou /*
29941cb875aeSCathy Zhou  * Build the VRRP packet (not including the IP header). Return the
29951cb875aeSCathy Zhou  * payload length.
29961cb875aeSCathy Zhou  *
29971cb875aeSCathy Zhou  * If zero_pri is set to be B_TRUE, then this is the specical zero-priority
29981cb875aeSCathy Zhou  * advertisement which is sent by the Master to indicate that it has been
29991cb875aeSCathy Zhou  * stopped participating in VRRP.
30001cb875aeSCathy Zhou  */
30011cb875aeSCathy Zhou static size_t
vrrpd_build_vrrp(vrrp_vr_t * vr,uchar_t * buf,int buflen,boolean_t zero_pri)30021cb875aeSCathy Zhou vrrpd_build_vrrp(vrrp_vr_t *vr, uchar_t *buf, int buflen, boolean_t zero_pri)
30031cb875aeSCathy Zhou {
30041cb875aeSCathy Zhou 	/* LINTED E_BAD_PTR_CAST_ALIGN */
30051cb875aeSCathy Zhou 	vrrp_pkt_t	*vp = (vrrp_pkt_t *)buf;
30061cb875aeSCathy Zhou 	/* LINTED E_BAD_PTR_CAST_ALIGN */
30071cb875aeSCathy Zhou 	struct in_addr	*a4 = (struct in_addr *)(vp + 1);
30081cb875aeSCathy Zhou 	/* LINTED E_BAD_PTR_CAST_ALIGN */
30091cb875aeSCathy Zhou 	struct in6_addr *a6 = (struct in6_addr *)(vp + 1);
30101cb875aeSCathy Zhou 	vrrp_intf_t	*vif = vr->vvr_vif;
30111cb875aeSCathy Zhou 	vrrp_ip_t	*vip;
30121cb875aeSCathy Zhou 	int		af = vif->vvi_af;
30131cb875aeSCathy Zhou 	size_t		size = sizeof (vrrp_pkt_t);
30141cb875aeSCathy Zhou 	uint16_t	rsvd_adver_int;
30151cb875aeSCathy Zhou 	int		nip = 0;
30161cb875aeSCathy Zhou 
30171cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_build_vrrp(%s, %s_priority): intv %d",
30181cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name, zero_pri ? "zero" : "non-zero",
30191cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_adver_int);
30201cb875aeSCathy Zhou 
30211cb875aeSCathy Zhou 	TAILQ_FOREACH(vip, &vif->vvi_iplist, vip_next) {
30221cb875aeSCathy Zhou 		if ((size += ((af == AF_INET) ? sizeof (struct in_addr) :
30231cb875aeSCathy Zhou 		    sizeof (struct in6_addr))) > buflen) {
30241cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_build_vrrp(%s): buffer size "
30251cb875aeSCathy Zhou 			    "not big enough %d", vr->vvr_conf.vvc_name, size);
30261cb875aeSCathy Zhou 			return (0);
30271cb875aeSCathy Zhou 		}
30281cb875aeSCathy Zhou 
30291cb875aeSCathy Zhou 		if (af == AF_INET)
30301cb875aeSCathy Zhou 			a4[nip++] = vip->vip_addr.in4.sin_addr;
30311cb875aeSCathy Zhou 		else
30321cb875aeSCathy Zhou 			a6[nip++] = vip->vip_addr.in6.sin6_addr;
30331cb875aeSCathy Zhou 	}
30341cb875aeSCathy Zhou 
30351cb875aeSCathy Zhou 	if (nip == 0) {
30361cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_build_vrrp(%s): no virtual IP "
30371cb875aeSCathy Zhou 		    "address", vr->vvr_conf.vvc_name);
30381cb875aeSCathy Zhou 		return (0);
30391cb875aeSCathy Zhou 	}
30401cb875aeSCathy Zhou 
30411cb875aeSCathy Zhou 	vp->vp_vers_type = (VRRP_VERSION << 4) | VRRP_PKT_ADVERT;
30421cb875aeSCathy Zhou 	vp->vp_vrid = vr->vvr_conf.vvc_vrid;
30431cb875aeSCathy Zhou 	vp->vp_prio = zero_pri ? VRRP_PRIO_ZERO : vr->vvr_conf.vvc_pri;
30441cb875aeSCathy Zhou 
30451cb875aeSCathy Zhou 	rsvd_adver_int = MSEC2CENTISEC(vr->vvr_conf.vvc_adver_int) & 0x0fff;
30461cb875aeSCathy Zhou 	vp->vp_rsvd_adver_int = htons(rsvd_adver_int);
30471cb875aeSCathy Zhou 	vp->vp_ipnum = nip;
30481cb875aeSCathy Zhou 
30491cb875aeSCathy Zhou 	/*
30501cb875aeSCathy Zhou 	 * Set the checksum to 0 first, then caculate it.
30511cb875aeSCathy Zhou 	 */
30521cb875aeSCathy Zhou 	vp->vp_chksum = 0;
30531cb875aeSCathy Zhou 	if (af == AF_INET) {
30541cb875aeSCathy Zhou 		vp->vp_chksum = vrrp_cksum4(
30551cb875aeSCathy Zhou 		    &vr->vvr_pif->vvi_pip->vip_addr.in4.sin_addr,
30561cb875aeSCathy Zhou 		    &vrrp_muladdr4.in4.sin_addr, size, vp);
30571cb875aeSCathy Zhou 	} else {
30581cb875aeSCathy Zhou 		vp->vp_chksum = vrrp_cksum6(
30591cb875aeSCathy Zhou 		    &vr->vvr_pif->vvi_pip->vip_addr.in6.sin6_addr,
30601cb875aeSCathy Zhou 		    &vrrp_muladdr6.in6.sin6_addr, size, vp);
30611cb875aeSCathy Zhou 	}
30621cb875aeSCathy Zhou 
30631cb875aeSCathy Zhou 	return (size);
30641cb875aeSCathy Zhou }
30651cb875aeSCathy Zhou 
30661cb875aeSCathy Zhou /*
30671cb875aeSCathy Zhou  * We need to build the IPv4 header on our own.
30681cb875aeSCathy Zhou  */
30691cb875aeSCathy Zhou static vrrp_err_t
vrrpd_send_adv_v4(vrrp_vr_t * vr,uchar_t * buf,size_t len,boolean_t zero_pri)30701cb875aeSCathy Zhou vrrpd_send_adv_v4(vrrp_vr_t *vr, uchar_t *buf, size_t len, boolean_t zero_pri)
30711cb875aeSCathy Zhou {
30721cb875aeSCathy Zhou 	/* LINTED E_BAD_PTR_CAST_ALIGN */
30731cb875aeSCathy Zhou 	struct ip *ip = (struct ip *)buf;
30741cb875aeSCathy Zhou 	size_t plen;
30751cb875aeSCathy Zhou 
30761cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v4(%s)", vr->vvr_conf.vvc_name);
30771cb875aeSCathy Zhou 
30781cb875aeSCathy Zhou 	if ((plen = vrrpd_build_vrrp(vr, buf + sizeof (struct ip),
30791cb875aeSCathy Zhou 	    len - sizeof (struct ip), zero_pri)) == 0) {
30801cb875aeSCathy Zhou 		return (VRRP_ETOOSMALL);
30811cb875aeSCathy Zhou 	}
30821cb875aeSCathy Zhou 
30831cb875aeSCathy Zhou 	ip->ip_hl = sizeof (struct ip) >> 2;
30841cb875aeSCathy Zhou 	ip->ip_v = IPV4_VERSION;
30851cb875aeSCathy Zhou 	ip->ip_tos = 0;
30861cb875aeSCathy Zhou 	plen += sizeof (struct ip);
30871cb875aeSCathy Zhou 	ip->ip_len = htons(plen);
30881cb875aeSCathy Zhou 	ip->ip_off = 0;
30891cb875aeSCathy Zhou 	ip->ip_ttl = VRRP_IP_TTL;
30901cb875aeSCathy Zhou 	ip->ip_p = IPPROTO_VRRP;
30911cb875aeSCathy Zhou 	ip->ip_src = vr->vvr_pif->vvi_pip->vip_addr.in4.sin_addr;
30921cb875aeSCathy Zhou 	ip->ip_dst = vrrp_muladdr4.in4.sin_addr;
30931cb875aeSCathy Zhou 
30941cb875aeSCathy Zhou 	/*
30951cb875aeSCathy Zhou 	 * The kernel will set the IP cksum and the IPv4 identification.
30961cb875aeSCathy Zhou 	 */
30971cb875aeSCathy Zhou 	ip->ip_id = 0;
30981cb875aeSCathy Zhou 	ip->ip_sum = 0;
30991cb875aeSCathy Zhou 
31001cb875aeSCathy Zhou 	if ((len = sendto(vr->vvr_vif->vvi_sockfd, buf, plen, 0,
31011cb875aeSCathy Zhou 	    (const struct sockaddr *)&vrrp_muladdr4,
31021cb875aeSCathy Zhou 	    sizeof (struct sockaddr_in))) != plen) {
31031cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_send_adv_v4(): sendto() on "
31041cb875aeSCathy Zhou 		    "(vrid:%d, %s, %s) failed: %s sent:%d expect:%d",
31051cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_vrid, vr->vvr_vif->vvi_ifname,
31061cb875aeSCathy Zhou 		    af_str(vr->vvr_conf.vvc_af), strerror(errno), len, plen);
31071cb875aeSCathy Zhou 		return (VRRP_ESYS);
31081cb875aeSCathy Zhou 	}
31091cb875aeSCathy Zhou 
31101cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v4(%s) succeed",
31111cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name);
31121cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
31131cb875aeSCathy Zhou }
31141cb875aeSCathy Zhou 
31151cb875aeSCathy Zhou static vrrp_err_t
vrrpd_send_adv_v6(vrrp_vr_t * vr,uchar_t * buf,size_t len,boolean_t zero_pri)31161cb875aeSCathy Zhou vrrpd_send_adv_v6(vrrp_vr_t *vr, uchar_t *buf, size_t len, boolean_t zero_pri)
31171cb875aeSCathy Zhou {
31181cb875aeSCathy Zhou 	struct msghdr msg6;
31191cb875aeSCathy Zhou 	size_t hoplimit_space = 0;
31201cb875aeSCathy Zhou 	size_t pktinfo_space = 0;
31211cb875aeSCathy Zhou 	size_t bufspace = 0;
31221cb875aeSCathy Zhou 	struct in6_pktinfo *pktinfop;
31231cb875aeSCathy Zhou 	struct cmsghdr *cmsgp;
31241cb875aeSCathy Zhou 	uchar_t *cmsg_datap;
31251cb875aeSCathy Zhou 	struct iovec iov;
31261cb875aeSCathy Zhou 	size_t plen;
31271cb875aeSCathy Zhou 
31281cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v6(%s)", vr->vvr_conf.vvc_name);
31291cb875aeSCathy Zhou 
31301cb875aeSCathy Zhou 	if ((plen = vrrpd_build_vrrp(vr, buf, len, zero_pri)) == 0)
31311cb875aeSCathy Zhou 		return (VRRP_ETOOSMALL);
31321cb875aeSCathy Zhou 
31331cb875aeSCathy Zhou 	msg6.msg_control = NULL;
31341cb875aeSCathy Zhou 	msg6.msg_controllen = 0;
31351cb875aeSCathy Zhou 
31361cb875aeSCathy Zhou 	hoplimit_space = sizeof (int);
31371cb875aeSCathy Zhou 	bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
31381cb875aeSCathy Zhou 	    hoplimit_space + _MAX_ALIGNMENT;
31391cb875aeSCathy Zhou 
31401cb875aeSCathy Zhou 	pktinfo_space = sizeof (struct in6_pktinfo);
31411cb875aeSCathy Zhou 	bufspace += sizeof (struct cmsghdr) + _MAX_ALIGNMENT +
31421cb875aeSCathy Zhou 	    pktinfo_space + _MAX_ALIGNMENT;
31431cb875aeSCathy Zhou 
31441cb875aeSCathy Zhou 	/*
31451cb875aeSCathy Zhou 	 * We need to temporarily set the msg6.msg_controllen to bufspace
31461cb875aeSCathy Zhou 	 * (we will later trim it to actual length used). This is needed because
31471cb875aeSCathy Zhou 	 * CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
31481cb875aeSCathy Zhou 	 */
31491cb875aeSCathy Zhou 	bufspace += sizeof (struct cmsghdr);
31501cb875aeSCathy Zhou 	msg6.msg_controllen = bufspace;
31511cb875aeSCathy Zhou 
31521cb875aeSCathy Zhou 	msg6.msg_control = (struct cmsghdr *)malloc(bufspace);
31531cb875aeSCathy Zhou 	if (msg6.msg_control == NULL) {
31541cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_send_adv_v6(%s): memory allocation "
31551cb875aeSCathy Zhou 		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
31561cb875aeSCathy Zhou 		return (VRRP_ENOMEM);
31571cb875aeSCathy Zhou 	}
31581cb875aeSCathy Zhou 
31591cb875aeSCathy Zhou 	cmsgp = CMSG_FIRSTHDR(&msg6);
31601cb875aeSCathy Zhou 
31611cb875aeSCathy Zhou 	cmsgp->cmsg_level = IPPROTO_IPV6;
31621cb875aeSCathy Zhou 	cmsgp->cmsg_type = IPV6_HOPLIMIT;
31631cb875aeSCathy Zhou 	cmsg_datap = CMSG_DATA(cmsgp);
31641cb875aeSCathy Zhou 	/* LINTED */
31651cb875aeSCathy Zhou 	*(int *)cmsg_datap = VRRP_IP_TTL;
31661cb875aeSCathy Zhou 	cmsgp->cmsg_len = cmsg_datap + hoplimit_space - (uchar_t *)cmsgp;
31671cb875aeSCathy Zhou 	cmsgp = CMSG_NXTHDR(&msg6, cmsgp);
31681cb875aeSCathy Zhou 
31691cb875aeSCathy Zhou 	cmsgp->cmsg_level = IPPROTO_IPV6;
31701cb875aeSCathy Zhou 	cmsgp->cmsg_type = IPV6_PKTINFO;
31711cb875aeSCathy Zhou 	cmsg_datap = CMSG_DATA(cmsgp);
31721cb875aeSCathy Zhou 
31731cb875aeSCathy Zhou 	/* LINTED */
31741cb875aeSCathy Zhou 	pktinfop = (struct in6_pktinfo *)cmsg_datap;
31751cb875aeSCathy Zhou 	/*
31761cb875aeSCathy Zhou 	 * We don't know if pktinfop->ipi6_addr is aligned properly,
31771cb875aeSCathy Zhou 	 * therefore let's use bcopy, instead of assignment.
31781cb875aeSCathy Zhou 	 */
31791cb875aeSCathy Zhou 	(void) bcopy(&vr->vvr_pif->vvi_pip->vip_addr.in6.sin6_addr,
31801cb875aeSCathy Zhou 	    &pktinfop->ipi6_addr, sizeof (struct in6_addr));
31811cb875aeSCathy Zhou 
31821cb875aeSCathy Zhou 	/*
31831cb875aeSCathy Zhou 	 *  We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
31841cb875aeSCathy Zhou 	 */
31851cb875aeSCathy Zhou 	pktinfop->ipi6_ifindex = vr->vvr_vif->vvi_ifindex;
31861cb875aeSCathy Zhou 	cmsgp->cmsg_len = cmsg_datap + pktinfo_space - (uchar_t *)cmsgp;
31871cb875aeSCathy Zhou 	cmsgp = CMSG_NXTHDR(&msg6, cmsgp);
31881cb875aeSCathy Zhou 	msg6.msg_controllen = (char *)cmsgp - (char *)msg6.msg_control;
31891cb875aeSCathy Zhou 
31901cb875aeSCathy Zhou 	msg6.msg_name = &vrrp_muladdr6;
31911cb875aeSCathy Zhou 	msg6.msg_namelen = sizeof (struct sockaddr_in6);
31921cb875aeSCathy Zhou 
31931cb875aeSCathy Zhou 	iov.iov_base = buf;
31941cb875aeSCathy Zhou 	iov.iov_len = plen;
31951cb875aeSCathy Zhou 	msg6.msg_iov = &iov;
31961cb875aeSCathy Zhou 	msg6.msg_iovlen = 1;
31971cb875aeSCathy Zhou 
31981cb875aeSCathy Zhou 	if ((len = sendmsg(vr->vvr_vif->vvi_sockfd,
31991cb875aeSCathy Zhou 	    (const struct msghdr *)&msg6, 0)) != plen) {
32001cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_send_adv_v6(%s): sendmsg() failed: "
32011cb875aeSCathy Zhou 		    "%s expect %d sent %d", vr->vvr_conf.vvc_name,
32021cb875aeSCathy Zhou 		    strerror(errno), plen, len);
32031cb875aeSCathy Zhou 		(void) free(msg6.msg_control);
32041cb875aeSCathy Zhou 		return (VRRP_ESYS);
32051cb875aeSCathy Zhou 	}
32061cb875aeSCathy Zhou 
32071cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_send_adv_v6(%s) succeed",
32081cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name);
32091cb875aeSCathy Zhou 	(void) free(msg6.msg_control);
32101cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
32111cb875aeSCathy Zhou }
32121cb875aeSCathy Zhou 
32131cb875aeSCathy Zhou /*
32141cb875aeSCathy Zhou  * Send the VRRP advertisement packets.
32151cb875aeSCathy Zhou  */
32161cb875aeSCathy Zhou static vrrp_err_t
vrrpd_send_adv(vrrp_vr_t * vr,boolean_t zero_pri)32171cb875aeSCathy Zhou vrrpd_send_adv(vrrp_vr_t *vr, boolean_t zero_pri)
32181cb875aeSCathy Zhou {
32191cb875aeSCathy Zhou 	uint64_t buf[(IP_MAXPACKET + 1)/8];
32201cb875aeSCathy Zhou 
32211cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_send_adv(%s, %s_priority)",
32221cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name, zero_pri ? "zero" : "non_zero");
32231cb875aeSCathy Zhou 
32241cb875aeSCathy Zhou 	assert(vr->vvr_pif->vvi_pip != NULL);
32251cb875aeSCathy Zhou 
32261cb875aeSCathy Zhou 	if (vr->vvr_pif->vvi_pip == NULL) {
32271cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_send_adv(%s): no primary IP "
32281cb875aeSCathy Zhou 		    "address", vr->vvr_conf.vvc_name);
32291cb875aeSCathy Zhou 		return (VRRP_EINVAL);
32301cb875aeSCathy Zhou 	}
32311cb875aeSCathy Zhou 
32321cb875aeSCathy Zhou 	if (vr->vvr_conf.vvc_af == AF_INET) {
32331cb875aeSCathy Zhou 		return (vrrpd_send_adv_v4(vr, (uchar_t *)buf,
32341cb875aeSCathy Zhou 		    sizeof (buf), zero_pri));
32351cb875aeSCathy Zhou 	} else {
32361cb875aeSCathy Zhou 		return (vrrpd_send_adv_v6(vr, (uchar_t *)buf,
32371cb875aeSCathy Zhou 		    sizeof (buf), zero_pri));
32381cb875aeSCathy Zhou 	}
32391cb875aeSCathy Zhou }
32401cb875aeSCathy Zhou 
32411cb875aeSCathy Zhou static void
vrrpd_process_adv(vrrp_vr_t * vr,vrrp_addr_t * from,vrrp_pkt_t * vp)32421cb875aeSCathy Zhou vrrpd_process_adv(vrrp_vr_t *vr, vrrp_addr_t *from, vrrp_pkt_t *vp)
32431cb875aeSCathy Zhou {
32441cb875aeSCathy Zhou 	vrrp_vr_conf_t *conf = &vr->vvr_conf;
32451cb875aeSCathy Zhou 	char		peer[INET6_ADDRSTRLEN];
32461cb875aeSCathy Zhou 	char		local[INET6_ADDRSTRLEN];
32471cb875aeSCathy Zhou 	int		addr_cmp;
32481cb875aeSCathy Zhou 	uint16_t	peer_adver_int;
32491cb875aeSCathy Zhou 
32501cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
32511cb875aeSCathy Zhou 	VRRPADDR2STR(vr->vvr_conf.vvc_af, from, peer, INET6_ADDRSTRLEN,
32521cb875aeSCathy Zhou 	    _B_FALSE);
32531cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s) from %s", conf->vvc_name,
32541cb875aeSCathy Zhou 	    peer);
32551cb875aeSCathy Zhou 
32561cb875aeSCathy Zhou 	if (vr->vvr_state <= VRRP_STATE_INIT) {
32571cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): state: %s, not "
32581cb875aeSCathy Zhou 		    "ready", conf->vvc_name, vrrp_state2str(vr->vvr_state));
32591cb875aeSCathy Zhou 		return;
32601cb875aeSCathy Zhou 	}
32611cb875aeSCathy Zhou 
32621cb875aeSCathy Zhou 	peer_adver_int = CENTISEC2MSEC(ntohs(vp->vp_rsvd_adver_int) & 0x0fff);
32631cb875aeSCathy Zhou 
32641cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
32651cb875aeSCathy Zhou 	VRRPADDR2STR(vr->vvr_pif->vvi_af, &vr->vvr_pif->vvi_pip->vip_addr,
32661cb875aeSCathy Zhou 	    local, INET6_ADDRSTRLEN, _B_FALSE);
32671cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): local/state/pri"
32681cb875aeSCathy Zhou 	    "(%s/%s/%d) peer/pri/intv(%s/%d/%d)", conf->vvc_name, local,
32691cb875aeSCathy Zhou 	    vrrp_state2str(vr->vvr_state), conf->vvc_pri, peer,
32701cb875aeSCathy Zhou 	    vp->vp_prio, peer_adver_int);
32711cb875aeSCathy Zhou 
32721cb875aeSCathy Zhou 	addr_cmp = ipaddr_cmp(vr->vvr_pif->vvi_af, from,
32731cb875aeSCathy Zhou 	    &vr->vvr_pif->vvi_pip->vip_addr);
32741cb875aeSCathy Zhou 	if (addr_cmp == 0) {
32751cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): local message",
32761cb875aeSCathy Zhou 		    conf->vvc_name);
32771cb875aeSCathy Zhou 		return;
32781cb875aeSCathy Zhou 	} else if (conf->vvc_pri == vp->vp_prio) {
32791cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): peer IP %s is %s"
32801cb875aeSCathy Zhou 		    " than the local IP %s", conf->vvc_name, peer,
32811cb875aeSCathy Zhou 		    addr_cmp > 0 ? "greater" : "less", local);
32821cb875aeSCathy Zhou 	}
32831cb875aeSCathy Zhou 
32841cb875aeSCathy Zhou 	if (conf->vvc_pri == 255) {
32851cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_adv(%s): virtual address "
32861cb875aeSCathy Zhou 		    "owner received advertisement from %s", conf->vvc_name,
32871cb875aeSCathy Zhou 		    peer);
32881cb875aeSCathy Zhou 		return;
32891cb875aeSCathy Zhou 	}
32901cb875aeSCathy Zhou 
32911cb875aeSCathy Zhou 	(void) gettimeofday(&vr->vvr_peer_time, NULL);
32921cb875aeSCathy Zhou 	(void) memcpy(&vr->vvr_peer_addr, from, sizeof (vrrp_addr_t));
32931cb875aeSCathy Zhou 	vr->vvr_peer_prio = vp->vp_prio;
32941cb875aeSCathy Zhou 	vr->vvr_peer_adver_int = peer_adver_int;
32951cb875aeSCathy Zhou 
32961cb875aeSCathy Zhou 	if (vr->vvr_state == VRRP_STATE_BACKUP) {
32971cb875aeSCathy Zhou 		vr->vvr_master_adver_int = vr->vvr_peer_adver_int;
32981cb875aeSCathy Zhou 		if ((vp->vp_prio == VRRP_PRIO_ZERO) ||
32991cb875aeSCathy Zhou 		    (conf->vvc_preempt == _B_FALSE ||
33001cb875aeSCathy Zhou 		    vp->vp_prio >= conf->vvc_pri)) {
33011cb875aeSCathy Zhou 			(void) iu_cancel_timer(vrrpd_timerq,
33021cb875aeSCathy Zhou 			    vr->vvr_timer_id, NULL);
33031cb875aeSCathy Zhou 			if (vp->vp_prio == VRRP_PRIO_ZERO) {
33041cb875aeSCathy Zhou 				/* the master stops participating in VRRP */
33051cb875aeSCathy Zhou 				vr->vvr_timeout = SKEW_TIME_VR(vr);
33061cb875aeSCathy Zhou 			} else {
33071cb875aeSCathy Zhou 				vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
33081cb875aeSCathy Zhou 			}
33091cb875aeSCathy Zhou 			if ((vr->vvr_timer_id = iu_schedule_timer_ms(
33101cb875aeSCathy Zhou 			    vrrpd_timerq, vr->vvr_timeout, vrrp_b2m_timeout,
33111cb875aeSCathy Zhou 			    vr)) == -1) {
33121cb875aeSCathy Zhou 				vrrp_log(VRRP_ERR, "vrrpd_process_adv(%s): "
33131cb875aeSCathy Zhou 				    "start vrrp_b2m_timeout(%d) failed",
33141cb875aeSCathy Zhou 				    conf->vvc_name, vr->vvr_timeout);
33151cb875aeSCathy Zhou 			} else {
33161cb875aeSCathy Zhou 				vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): "
33171cb875aeSCathy Zhou 				    "start vrrp_b2m_timeout(%d)",
33181cb875aeSCathy Zhou 				    conf->vvc_name, vr->vvr_timeout);
33191cb875aeSCathy Zhou 			}
33201cb875aeSCathy Zhou 		}
33211cb875aeSCathy Zhou 	} else if (vr->vvr_state == VRRP_STATE_MASTER) {
33221cb875aeSCathy Zhou 		if (vp->vp_prio == VRRP_PRIO_ZERO) {
33231cb875aeSCathy Zhou 			(void) vrrpd_send_adv(vr, _B_FALSE);
33241cb875aeSCathy Zhou 			(void) iu_cancel_timer(vrrpd_timerq,
33251cb875aeSCathy Zhou 			    vr->vvr_timer_id, NULL);
33261cb875aeSCathy Zhou 			if ((vr->vvr_timer_id = iu_schedule_timer_ms(
33271cb875aeSCathy Zhou 			    vrrpd_timerq, vr->vvr_timeout, vrrp_adv_timeout,
33281cb875aeSCathy Zhou 			    vr)) == -1) {
33291cb875aeSCathy Zhou 				vrrp_log(VRRP_ERR, "vrrpd_process_adv(%s): "
33301cb875aeSCathy Zhou 				    "start vrrp_adv_timeout(%d) failed",
33311cb875aeSCathy Zhou 				    conf->vvc_name, vr->vvr_timeout);
33321cb875aeSCathy Zhou 			} else {
33331cb875aeSCathy Zhou 				vrrp_log(VRRP_DBG1, "vrrpd_process_adv(%s): "
33341cb875aeSCathy Zhou 				    "start vrrp_adv_timeout(%d)",
33351cb875aeSCathy Zhou 				    conf->vvc_name, vr->vvr_timeout);
33361cb875aeSCathy Zhou 			}
33371cb875aeSCathy Zhou 		} else if (vp->vp_prio > conf->vvc_pri ||
33381cb875aeSCathy Zhou 		    (vp->vp_prio == conf->vvc_pri && addr_cmp > 0)) {
33391cb875aeSCathy Zhou 			(void) vrrpd_state_m2b(vr);
33401cb875aeSCathy Zhou 		}
33411cb875aeSCathy Zhou 	} else {
33421cb875aeSCathy Zhou 		assert(_B_FALSE);
33431cb875aeSCathy Zhou 	}
33441cb875aeSCathy Zhou }
33451cb875aeSCathy Zhou 
33461cb875aeSCathy Zhou static vrrp_err_t
vrrpd_process_vrrp(vrrp_intf_t * pif,vrrp_pkt_t * vp,size_t len,vrrp_addr_t * from)33471cb875aeSCathy Zhou vrrpd_process_vrrp(vrrp_intf_t *pif, vrrp_pkt_t *vp, size_t len,
33481cb875aeSCathy Zhou     vrrp_addr_t *from)
33491cb875aeSCathy Zhou {
33501cb875aeSCathy Zhou 	vrrp_vr_t	*vr;
33511cb875aeSCathy Zhou 	uint8_t		vers_type;
33521cb875aeSCathy Zhou 	uint16_t	saved_cksum, cksum;
33531cb875aeSCathy Zhou 	char		peer[INET6_ADDRSTRLEN];
33541cb875aeSCathy Zhou 
33551cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
33561cb875aeSCathy Zhou 	VRRPADDR2STR(pif->vvi_af, from, peer, INET6_ADDRSTRLEN, _B_FALSE);
33571cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_process_vrrp(%s) from %s", pif->vvi_ifname,
33581cb875aeSCathy Zhou 	    peer);
33591cb875aeSCathy Zhou 
33601cb875aeSCathy Zhou 	if (len < sizeof (vrrp_pkt_t)) {
33611cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s): invalid message "
33621cb875aeSCathy Zhou 		    "length %d", len);
33631cb875aeSCathy Zhou 		return (VRRP_EINVAL);
33641cb875aeSCathy Zhou 	}
33651cb875aeSCathy Zhou 
33661cb875aeSCathy Zhou 	/*
33671cb875aeSCathy Zhou 	 * Verify: VRRP version number and packet type.
33681cb875aeSCathy Zhou 	 */
33691cb875aeSCathy Zhou 	vers_type = ((vp->vp_vers_type & VRRP_VER_MASK) >> 4);
33701cb875aeSCathy Zhou 	if (vers_type != VRRP_VERSION) {
33711cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s) unsupported "
33721cb875aeSCathy Zhou 		    "version %d", pif->vvi_ifname, vers_type);
33731cb875aeSCathy Zhou 		return (VRRP_EINVAL);
33741cb875aeSCathy Zhou 	}
33751cb875aeSCathy Zhou 
33761cb875aeSCathy Zhou 	if (vp->vp_ipnum == 0) {
33771cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s): zero IPvX count",
33781cb875aeSCathy Zhou 		    pif->vvi_ifname);
33791cb875aeSCathy Zhou 		return (VRRP_EINVAL);
33801cb875aeSCathy Zhou 	}
33811cb875aeSCathy Zhou 
33821cb875aeSCathy Zhou 	if (len - sizeof (vrrp_pkt_t) !=
33831cb875aeSCathy Zhou 	    vp->vp_ipnum * (pif->vvi_af == AF_INET ? sizeof (struct in_addr) :
33841cb875aeSCathy Zhou 	    sizeof (struct in6_addr))) {
33851cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s): invalid IPvX count"
33861cb875aeSCathy Zhou 		    " %d", pif->vvi_ifname, vp->vp_ipnum);
33871cb875aeSCathy Zhou 		return (VRRP_EINVAL);
33881cb875aeSCathy Zhou 	}
33891cb875aeSCathy Zhou 
33901cb875aeSCathy Zhou 	vers_type = (vp->vp_vers_type & VRRP_TYPE_MASK);
33911cb875aeSCathy Zhou 
33921cb875aeSCathy Zhou 	/*
33931cb875aeSCathy Zhou 	 * verify: VRRP checksum. Note that vrrp_cksum returns network byte
33941cb875aeSCathy Zhou 	 * order checksum value;
33951cb875aeSCathy Zhou 	 */
33961cb875aeSCathy Zhou 	saved_cksum = vp->vp_chksum;
33971cb875aeSCathy Zhou 	vp->vp_chksum = 0;
33981cb875aeSCathy Zhou 	if (pif->vvi_af == AF_INET) {
33991cb875aeSCathy Zhou 		cksum = vrrp_cksum4(&from->in4.sin_addr,
34001cb875aeSCathy Zhou 		    &vrrp_muladdr4.in4.sin_addr, len, vp);
34011cb875aeSCathy Zhou 	} else {
34021cb875aeSCathy Zhou 		cksum = vrrp_cksum6(&from->in6.sin6_addr,
34031cb875aeSCathy Zhou 		    &vrrp_muladdr6.in6.sin6_addr, len, vp);
34041cb875aeSCathy Zhou 	}
34051cb875aeSCathy Zhou 
34061cb875aeSCathy Zhou 	if (cksum != saved_cksum) {
34071cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_vrrp(%s) invalid "
34081cb875aeSCathy Zhou 		    "checksum: expected/real(0x%x/0x%x)", pif->vvi_ifname,
34091cb875aeSCathy Zhou 		    cksum, saved_cksum);
34101cb875aeSCathy Zhou 		return (VRRP_EINVAL);
34111cb875aeSCathy Zhou 	}
34121cb875aeSCathy Zhou 
34131cb875aeSCathy Zhou 	if ((vr = vrrpd_lookup_vr_by_vrid(pif->vvi_ifname, vp->vp_vrid,
34141cb875aeSCathy Zhou 	    pif->vvi_af)) != NULL && vers_type == VRRP_PKT_ADVERT) {
34151cb875aeSCathy Zhou 		vrrpd_process_adv(vr, from, vp);
34161cb875aeSCathy Zhou 	} else {
34171cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_process_vrrp(%s) VRID(%d/%s) "
34181cb875aeSCathy Zhou 		    "not configured", pif->vvi_ifname, vp->vp_vrid,
34191cb875aeSCathy Zhou 		    af_str(pif->vvi_af));
34201cb875aeSCathy Zhou 	}
34211cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
34221cb875aeSCathy Zhou }
34231cb875aeSCathy Zhou 
34241cb875aeSCathy Zhou /*
34251cb875aeSCathy Zhou  * IPv4 socket, the IPv4 header is included.
34261cb875aeSCathy Zhou  */
34271cb875aeSCathy Zhou static vrrp_err_t
vrrpd_process_adv_v4(vrrp_intf_t * pif,struct msghdr * msgp,size_t len)34281cb875aeSCathy Zhou vrrpd_process_adv_v4(vrrp_intf_t *pif, struct msghdr *msgp, size_t len)
34291cb875aeSCathy Zhou {
34301cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
34311cb875aeSCathy Zhou 	struct ip	*ip;
34321cb875aeSCathy Zhou 
34331cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG0, "vrrpd_process_adv_v4(%s, %d)",
34341cb875aeSCathy Zhou 	    pif->vvi_ifname, len);
34351cb875aeSCathy Zhou 
34361cb875aeSCathy Zhou 	ip = (struct ip *)msgp->msg_iov->iov_base;
34371cb875aeSCathy Zhou 
34381cb875aeSCathy Zhou 	/* Sanity check */
34391cb875aeSCathy Zhou 	if (len < sizeof (struct ip) || len < ntohs(ip->ip_len)) {
34401cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid length "
34411cb875aeSCathy Zhou 		    "%d", pif->vvi_ifname, len);
34421cb875aeSCathy Zhou 		return (VRRP_EINVAL);
34431cb875aeSCathy Zhou 	}
34441cb875aeSCathy Zhou 
34451cb875aeSCathy Zhou 	assert(ip->ip_v == IPV4_VERSION);
34461cb875aeSCathy Zhou 	assert(ip->ip_p == IPPROTO_VRRP);
34471cb875aeSCathy Zhou 	assert(msgp->msg_namelen == sizeof (struct sockaddr_in));
34481cb875aeSCathy Zhou 
34491cb875aeSCathy Zhou 	if (vrrp_muladdr4.in4.sin_addr.s_addr != ip->ip_dst.s_addr) {
34501cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
34511cb875aeSCathy Zhou 		    "destination %s", pif->vvi_ifname,
34521cb875aeSCathy Zhou 		    inet_ntop(pif->vvi_af, &(ip->ip_dst), abuf, sizeof (abuf)));
34531cb875aeSCathy Zhou 		return (VRRP_EINVAL);
34541cb875aeSCathy Zhou 	}
34551cb875aeSCathy Zhou 
34561cb875aeSCathy Zhou 	if (ip->ip_ttl != VRRP_IP_TTL) {
34571cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
34581cb875aeSCathy Zhou 		    "ttl %d", pif->vvi_ifname, ip->ip_ttl);
34591cb875aeSCathy Zhou 		return (VRRP_EINVAL);
34601cb875aeSCathy Zhou 	}
34611cb875aeSCathy Zhou 
34621cb875aeSCathy Zhou 	/*
34631cb875aeSCathy Zhou 	 * Note that the ip_len contains only the IP payload length.
34641cb875aeSCathy Zhou 	 */
34651cb875aeSCathy Zhou 	return (vrrpd_process_vrrp(pif,
34661cb875aeSCathy Zhou 	    /* LINTED E_BAD_PTR_CAST_ALIGN */
34671cb875aeSCathy Zhou 	    (vrrp_pkt_t *)((char *)ip + ip->ip_hl * 4), ntohs(ip->ip_len),
34681cb875aeSCathy Zhou 	    (vrrp_addr_t *)msgp->msg_name));
34691cb875aeSCathy Zhou }
34701cb875aeSCathy Zhou 
34711cb875aeSCathy Zhou /*
34721cb875aeSCathy Zhou  * IPv6 socket, check the ancillary_data.
34731cb875aeSCathy Zhou  */
34741cb875aeSCathy Zhou static vrrp_err_t
vrrpd_process_adv_v6(vrrp_intf_t * pif,struct msghdr * msgp,size_t len)34751cb875aeSCathy Zhou vrrpd_process_adv_v6(vrrp_intf_t *pif, struct msghdr *msgp, size_t len)
34761cb875aeSCathy Zhou {
34771cb875aeSCathy Zhou 	struct cmsghdr		*cmsgp;
34781cb875aeSCathy Zhou 	uchar_t			*cmsg_datap;
34791cb875aeSCathy Zhou 	struct in6_pktinfo	*pktinfop;
34801cb875aeSCathy Zhou 	char			abuf[INET6_ADDRSTRLEN];
34811cb875aeSCathy Zhou 	int			ttl;
34821cb875aeSCathy Zhou 
34831cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_process_adv_v6(%s, %d)",
34841cb875aeSCathy Zhou 	    pif->vvi_ifname, len);
34851cb875aeSCathy Zhou 
34861cb875aeSCathy Zhou 	/* Sanity check */
34871cb875aeSCathy Zhou 	if (len < sizeof (vrrp_pkt_t)) {
34881cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_process_adv_v6(%s): invalid length "
34891cb875aeSCathy Zhou 		    "%d", pif->vvi_ifname, len);
34901cb875aeSCathy Zhou 		return (VRRP_EINVAL);
34911cb875aeSCathy Zhou 	}
34921cb875aeSCathy Zhou 
34931cb875aeSCathy Zhou 	assert(msgp->msg_namelen == sizeof (struct sockaddr_in6));
34941cb875aeSCathy Zhou 
34951cb875aeSCathy Zhou 	for (cmsgp = CMSG_FIRSTHDR(msgp); cmsgp != NULL;
34961cb875aeSCathy Zhou 	    cmsgp = CMSG_NXTHDR(msgp, cmsgp)) {
34971cb875aeSCathy Zhou 		assert(cmsgp->cmsg_level == IPPROTO_IPV6);
34981cb875aeSCathy Zhou 		cmsg_datap = CMSG_DATA(cmsgp);
34991cb875aeSCathy Zhou 
35001cb875aeSCathy Zhou 		switch (cmsgp->cmsg_type) {
35011cb875aeSCathy Zhou 		case IPV6_HOPLIMIT:
35021cb875aeSCathy Zhou 			/* LINTED E_BAD_PTR_CAST_ALIGN */
35031cb875aeSCathy Zhou 			if ((ttl = *(int *)cmsg_datap) == VRRP_IP_TTL)
35041cb875aeSCathy Zhou 				break;
35051cb875aeSCathy Zhou 
35061cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
35071cb875aeSCathy Zhou 			    "ttl %d", pif->vvi_ifname, ttl);
35081cb875aeSCathy Zhou 			return (VRRP_EINVAL);
35091cb875aeSCathy Zhou 		case IPV6_PKTINFO:
35101cb875aeSCathy Zhou 			/* LINTED E_BAD_PTR_CAST_ALIGN */
35111cb875aeSCathy Zhou 			pktinfop = (struct in6_pktinfo *)cmsg_datap;
35121cb875aeSCathy Zhou 			if (IN6_ARE_ADDR_EQUAL(&pktinfop->ipi6_addr,
35131cb875aeSCathy Zhou 			    &vrrp_muladdr6.in6.sin6_addr)) {
35141cb875aeSCathy Zhou 				break;
35151cb875aeSCathy Zhou 			}
35161cb875aeSCathy Zhou 
35171cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_process_adv_v4(%s): invalid "
35181cb875aeSCathy Zhou 			    "destination %s", pif->vvi_ifname,
35191cb875aeSCathy Zhou 			    inet_ntop(pif->vvi_af, &pktinfop->ipi6_addr, abuf,
35201cb875aeSCathy Zhou 			    sizeof (abuf)));
35211cb875aeSCathy Zhou 			return (VRRP_EINVAL);
35221cb875aeSCathy Zhou 		}
35231cb875aeSCathy Zhou 	}
35241cb875aeSCathy Zhou 
35251cb875aeSCathy Zhou 	return (vrrpd_process_vrrp(pif, msgp->msg_iov->iov_base, len,
35261cb875aeSCathy Zhou 	    msgp->msg_name));
35271cb875aeSCathy Zhou }
35281cb875aeSCathy Zhou 
35291cb875aeSCathy Zhou /* ARGSUSED */
35301cb875aeSCathy Zhou static void
vrrpd_sock_handler(iu_eh_t * eh,int s,short events,iu_event_id_t id,void * arg)35311cb875aeSCathy Zhou vrrpd_sock_handler(iu_eh_t *eh, int s, short events, iu_event_id_t id,
35321cb875aeSCathy Zhou     void *arg)
35331cb875aeSCathy Zhou {
35341cb875aeSCathy Zhou 	struct msghdr		msg;
35351cb875aeSCathy Zhou 	vrrp_addr_t		from;
35361cb875aeSCathy Zhou 	uint64_t		buf[(IP_MAXPACKET + 1)/8];
35371cb875aeSCathy Zhou 	uint64_t		ancillary_data[(IP_MAXPACKET + 1)/8];
35381cb875aeSCathy Zhou 	vrrp_intf_t		*pif = arg;
35391cb875aeSCathy Zhou 	int			af = pif->vvi_af;
35401cb875aeSCathy Zhou 	int			len;
35411cb875aeSCathy Zhou 	struct iovec		iov;
35421cb875aeSCathy Zhou 
35431cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_sock_handler(%s)", pif->vvi_ifname);
35441cb875aeSCathy Zhou 
35451cb875aeSCathy Zhou 	msg.msg_name = (struct sockaddr *)&from;
35461cb875aeSCathy Zhou 	msg.msg_namelen = (af == AF_INET) ? sizeof (struct sockaddr_in) :
35471cb875aeSCathy Zhou 	    sizeof (struct sockaddr_in6);
35481cb875aeSCathy Zhou 	iov.iov_base = (char *)buf;
35491cb875aeSCathy Zhou 	iov.iov_len = sizeof (buf);
35501cb875aeSCathy Zhou 	msg.msg_iov = &iov;
35511cb875aeSCathy Zhou 	msg.msg_iovlen = 1;
35521cb875aeSCathy Zhou 	msg.msg_control = ancillary_data;
35531cb875aeSCathy Zhou 	msg.msg_controllen = sizeof (ancillary_data);
35541cb875aeSCathy Zhou 
35551cb875aeSCathy Zhou 	if ((len = recvmsg(s, &msg, 0)) == -1) {
35561cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_sock_handler() recvmsg(%s) "
35571cb875aeSCathy Zhou 		    "failed: %s", pif->vvi_ifname, strerror(errno));
35581cb875aeSCathy Zhou 		return;
35591cb875aeSCathy Zhou 	}
35601cb875aeSCathy Zhou 
35611cb875aeSCathy Zhou 	/*
35621cb875aeSCathy Zhou 	 * Ignore packets whose control buffers that don't fit
35631cb875aeSCathy Zhou 	 */
35641cb875aeSCathy Zhou 	if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
35651cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_sock_handler() %s buffer not "
35661cb875aeSCathy Zhou 		    "big enough", pif->vvi_ifname);
35671cb875aeSCathy Zhou 		return;
35681cb875aeSCathy Zhou 	}
35691cb875aeSCathy Zhou 
35701cb875aeSCathy Zhou 	if (af == AF_INET)
35711cb875aeSCathy Zhou 		(void) vrrpd_process_adv_v4(pif, &msg, len);
35721cb875aeSCathy Zhou 	else
35731cb875aeSCathy Zhou 		(void) vrrpd_process_adv_v6(pif, &msg, len);
35741cb875aeSCathy Zhou }
35751cb875aeSCathy Zhou 
35761cb875aeSCathy Zhou /*
35771cb875aeSCathy Zhou  * Create the socket which is used to receive VRRP packets. Virtual routers
35781cb875aeSCathy Zhou  * that configured on the same physical interface share the same socket.
35791cb875aeSCathy Zhou  */
35801cb875aeSCathy Zhou static vrrp_err_t
vrrpd_init_rxsock(vrrp_vr_t * vr)35811cb875aeSCathy Zhou vrrpd_init_rxsock(vrrp_vr_t *vr)
35821cb875aeSCathy Zhou {
35831cb875aeSCathy Zhou 	vrrp_intf_t *pif;	/* Physical interface used to recv packets */
35841cb875aeSCathy Zhou 	struct group_req greq;
35851cb875aeSCathy Zhou 	struct sockaddr_storage *muladdr;
35861cb875aeSCathy Zhou 	int af, proto;
35871cb875aeSCathy Zhou 	int on = 1;
35881cb875aeSCathy Zhou 	vrrp_err_t err = VRRP_SUCCESS;
35891cb875aeSCathy Zhou 
35901cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s)", vr->vvr_conf.vvc_name);
35911cb875aeSCathy Zhou 
35921cb875aeSCathy Zhou 	/*
35931cb875aeSCathy Zhou 	 * The RX sockets may already been initialized.
35941cb875aeSCathy Zhou 	 */
35951cb875aeSCathy Zhou 	if ((pif = vr->vvr_pif) != NULL) {
35961cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s) already done on %s",
35971cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name, pif->vvi_ifname);
35981cb875aeSCathy Zhou 		assert(pif->vvi_sockfd != -1);
35991cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
36001cb875aeSCathy Zhou 	}
36011cb875aeSCathy Zhou 
36021cb875aeSCathy Zhou 	/*
36031cb875aeSCathy Zhou 	 * If no IP addresses configured on the primary interface,
36041cb875aeSCathy Zhou 	 * return failure.
36051cb875aeSCathy Zhou 	 */
36061cb875aeSCathy Zhou 	af = vr->vvr_conf.vvc_af;
36071cb875aeSCathy Zhou 	pif = vrrpd_lookup_if(vr->vvr_conf.vvc_link, af);
36081cb875aeSCathy Zhou 	if (pif == NULL) {
36091cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s): no IP address "
36101cb875aeSCathy Zhou 		    "over %s/%s", vr->vvr_conf.vvc_name,
36111cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_link, af_str(af));
36121cb875aeSCathy Zhou 		return (VRRP_ENOPRIM);
36131cb875aeSCathy Zhou 	}
36141cb875aeSCathy Zhou 
36151cb875aeSCathy Zhou 	proto = (af == AF_INET ? IPPROTO_IP : IPPROTO_IPV6);
36161cb875aeSCathy Zhou 	if (pif->vvi_nvr++ == 0) {
36171cb875aeSCathy Zhou 		assert(pif->vvi_sockfd < 0);
36181cb875aeSCathy Zhou 		pif->vvi_sockfd = socket(af, SOCK_RAW, IPPROTO_VRRP);
36191cb875aeSCathy Zhou 		if (pif->vvi_sockfd < 0) {
36201cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): socket() "
36211cb875aeSCathy Zhou 			    "failed %s", vr->vvr_conf.vvc_name,
36221cb875aeSCathy Zhou 			    strerror(errno));
36231cb875aeSCathy Zhou 			err = VRRP_ESYS;
36241cb875aeSCathy Zhou 			goto done;
36251cb875aeSCathy Zhou 		}
36261cb875aeSCathy Zhou 
36271cb875aeSCathy Zhou 		/*
36281cb875aeSCathy Zhou 		 * Join the multicast group to receive VRRP packets.
36291cb875aeSCathy Zhou 		 */
36301cb875aeSCathy Zhou 		if (af == AF_INET) {
36311cb875aeSCathy Zhou 			muladdr = (struct sockaddr_storage *)
36321cb875aeSCathy Zhou 			    (void *)&vrrp_muladdr4;
36331cb875aeSCathy Zhou 		} else {
36341cb875aeSCathy Zhou 			muladdr = (struct sockaddr_storage *)
36351cb875aeSCathy Zhou 			    (void *)&vrrp_muladdr6;
36361cb875aeSCathy Zhou 		}
36371cb875aeSCathy Zhou 
36381cb875aeSCathy Zhou 		greq.gr_interface = pif->vvi_ifindex;
36391cb875aeSCathy Zhou 		(void) memcpy(&greq.gr_group, muladdr,
36401cb875aeSCathy Zhou 		    sizeof (struct sockaddr_storage));
36411cb875aeSCathy Zhou 		if (setsockopt(pif->vvi_sockfd, proto, MCAST_JOIN_GROUP, &greq,
36421cb875aeSCathy Zhou 		    sizeof (struct group_req)) < 0) {
36431cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
36441cb875aeSCathy Zhou 			    "join_group(%d) failed: %s", vr->vvr_conf.vvc_name,
36451cb875aeSCathy Zhou 			    pif->vvi_ifindex, strerror(errno));
36461cb875aeSCathy Zhou 			err = VRRP_ESYS;
36471cb875aeSCathy Zhou 			goto done;
36481cb875aeSCathy Zhou 		} else {
36491cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s): "
36501cb875aeSCathy Zhou 			    "join_group(%d) succeeded", vr->vvr_conf.vvc_name,
36511cb875aeSCathy Zhou 			    pif->vvi_ifindex);
36521cb875aeSCathy Zhou 		}
36531cb875aeSCathy Zhou 
36541cb875aeSCathy Zhou 		/*
36551cb875aeSCathy Zhou 		 * Unlike IPv4, the IPv6 raw socket does not pass the IP header
36561cb875aeSCathy Zhou 		 * when a packet is received. Call setsockopt() to receive such
36571cb875aeSCathy Zhou 		 * information.
36581cb875aeSCathy Zhou 		 */
36591cb875aeSCathy Zhou 		if (af == AF_INET6) {
36601cb875aeSCathy Zhou 			/*
36611cb875aeSCathy Zhou 			 * Enable receipt of destination address info
36621cb875aeSCathy Zhou 			 */
36631cb875aeSCathy Zhou 			if (setsockopt(pif->vvi_sockfd, proto, IPV6_RECVPKTINFO,
36641cb875aeSCathy Zhou 			    (char *)&on, sizeof (on)) < 0) {
36651cb875aeSCathy Zhou 				vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
36661cb875aeSCathy Zhou 				    "enable recvpktinfo failed: %s",
36671cb875aeSCathy Zhou 				    vr->vvr_conf.vvc_name, strerror(errno));
36681cb875aeSCathy Zhou 				err = VRRP_ESYS;
36691cb875aeSCathy Zhou 				goto done;
36701cb875aeSCathy Zhou 			}
36711cb875aeSCathy Zhou 
36721cb875aeSCathy Zhou 			/*
36731cb875aeSCathy Zhou 			 * Enable receipt of hoplimit info
36741cb875aeSCathy Zhou 			 */
36751cb875aeSCathy Zhou 			if (setsockopt(pif->vvi_sockfd, proto,
36761cb875aeSCathy Zhou 			    IPV6_RECVHOPLIMIT, (char *)&on, sizeof (on)) < 0) {
36771cb875aeSCathy Zhou 				vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
36781cb875aeSCathy Zhou 				    "enable recvhoplimit failed: %s",
36791cb875aeSCathy Zhou 				    vr->vvr_conf.vvc_name, strerror(errno));
36801cb875aeSCathy Zhou 				err = VRRP_ESYS;
36811cb875aeSCathy Zhou 				goto done;
36821cb875aeSCathy Zhou 			}
36831cb875aeSCathy Zhou 		}
36841cb875aeSCathy Zhou 
36851cb875aeSCathy Zhou 		if ((pif->vvi_eid = iu_register_event(vrrpd_eh,
36861cb875aeSCathy Zhou 		    pif->vvi_sockfd, POLLIN, vrrpd_sock_handler, pif)) == -1) {
36871cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_init_rxsock(%s): "
36881cb875aeSCathy Zhou 			    "iu_register_event() failed",
36891cb875aeSCathy Zhou 			    vr->vvr_conf.vvc_name);
36901cb875aeSCathy Zhou 			err = VRRP_ESYS;
36911cb875aeSCathy Zhou 			goto done;
36921cb875aeSCathy Zhou 		}
36931cb875aeSCathy Zhou 	} else {
36941cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_rxsock(%s) over %s already "
36951cb875aeSCathy Zhou 		    "done %d", vr->vvr_conf.vvc_name, pif->vvi_ifname,
36961cb875aeSCathy Zhou 		    pif->vvi_nvr);
36971cb875aeSCathy Zhou 		assert(IS_PRIMARY_INTF(pif));
36981cb875aeSCathy Zhou 	}
36991cb875aeSCathy Zhou 
37001cb875aeSCathy Zhou done:
37011cb875aeSCathy Zhou 	vr->vvr_pif = pif;
37021cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS)
37031cb875aeSCathy Zhou 		vrrpd_fini_rxsock(vr);
37041cb875aeSCathy Zhou 
37051cb875aeSCathy Zhou 	return (err);
37061cb875aeSCathy Zhou }
37071cb875aeSCathy Zhou 
37081cb875aeSCathy Zhou /*
37091cb875aeSCathy Zhou  * Delete the socket which is used to receive VRRP packets for the given
37101cb875aeSCathy Zhou  * VRRP router. Since all virtual routers that configured on the same
37111cb875aeSCathy Zhou  * physical interface share the same socket, the socket is only closed
37121cb875aeSCathy Zhou  * when the last VRRP router share this socket is deleted.
37131cb875aeSCathy Zhou  */
37141cb875aeSCathy Zhou static void
vrrpd_fini_rxsock(vrrp_vr_t * vr)37151cb875aeSCathy Zhou vrrpd_fini_rxsock(vrrp_vr_t *vr)
37161cb875aeSCathy Zhou {
37171cb875aeSCathy Zhou 	vrrp_intf_t	*pif = vr->vvr_pif;
37181cb875aeSCathy Zhou 
37191cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_fini_rxsock(%s)", vr->vvr_conf.vvc_name);
37201cb875aeSCathy Zhou 
37211cb875aeSCathy Zhou 	if (pif == NULL)
37221cb875aeSCathy Zhou 		return;
37231cb875aeSCathy Zhou 
37241cb875aeSCathy Zhou 	if (--pif->vvi_nvr == 0) {
37251cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_fini_rxsock(%s) over %s",
37261cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name, pif->vvi_ifname);
37271cb875aeSCathy Zhou 		(void) iu_unregister_event(vrrpd_eh, pif->vvi_eid, NULL);
37281cb875aeSCathy Zhou 		(void) close(pif->vvi_sockfd);
37291cb875aeSCathy Zhou 		pif->vvi_pip = NULL;
37301cb875aeSCathy Zhou 		pif->vvi_sockfd = -1;
37311cb875aeSCathy Zhou 		pif->vvi_eid = -1;
37321cb875aeSCathy Zhou 	} else {
37331cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_fini_rxsock(%s) over %s %d",
37341cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name, pif->vvi_ifname, pif->vvi_nvr);
37351cb875aeSCathy Zhou 	}
37361cb875aeSCathy Zhou 	vr->vvr_pif = NULL;
37371cb875aeSCathy Zhou }
37381cb875aeSCathy Zhou 
37391cb875aeSCathy Zhou /*
37401cb875aeSCathy Zhou  * Create the socket which is used to send VRRP packets. Further, set
37411cb875aeSCathy Zhou  * the IFF_NOACCEPT flag based on the VRRP router's accept mode.
37421cb875aeSCathy Zhou  */
37431cb875aeSCathy Zhou static vrrp_err_t
vrrpd_init_txsock(vrrp_vr_t * vr)37441cb875aeSCathy Zhou vrrpd_init_txsock(vrrp_vr_t *vr)
37451cb875aeSCathy Zhou {
37461cb875aeSCathy Zhou 	int		af;
37471cb875aeSCathy Zhou 	vrrp_intf_t	*vif;
37481cb875aeSCathy Zhou 	vrrp_err_t	err;
37491cb875aeSCathy Zhou 
37501cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_init_txsock(%s)", vr->vvr_conf.vvc_name);
37511cb875aeSCathy Zhou 
37521cb875aeSCathy Zhou 	if (vr->vvr_vif != NULL) {
37531cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock(%s) already done on %s",
37541cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name, vr->vvr_vif->vvi_ifname);
37551cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
37561cb875aeSCathy Zhou 	}
37571cb875aeSCathy Zhou 
37581cb875aeSCathy Zhou 	af = vr->vvr_conf.vvc_af;
37591cb875aeSCathy Zhou 	if ((vif = vrrpd_lookup_if(vr->vvr_vnic, af)) == NULL) {
37601cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock(%s) no IP address over "
37611cb875aeSCathy Zhou 		    "%s/%s", vr->vvr_conf.vvc_name, vr->vvr_vnic, af_str(af));
37621cb875aeSCathy Zhou 		return (VRRP_ENOVIRT);
37631cb875aeSCathy Zhou 	}
37641cb875aeSCathy Zhou 
37651cb875aeSCathy Zhou 	vr->vvr_vif = vif;
37661cb875aeSCathy Zhou 	if (vr->vvr_conf.vvc_af == AF_INET)
37671cb875aeSCathy Zhou 		err = vrrpd_init_txsock_v4(vr);
37681cb875aeSCathy Zhou 	else
37691cb875aeSCathy Zhou 		err = vrrpd_init_txsock_v6(vr);
37701cb875aeSCathy Zhou 
37711cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS)
37721cb875aeSCathy Zhou 		goto done;
37731cb875aeSCathy Zhou 
37741cb875aeSCathy Zhou 	/*
37751cb875aeSCathy Zhou 	 * The interface should start with IFF_NOACCEPT flag not set, only
37761cb875aeSCathy Zhou 	 * call this function when the VRRP router requires IFF_NOACCEPT.
37771cb875aeSCathy Zhou 	 */
37781cb875aeSCathy Zhou 	if (!vr->vvr_conf.vvc_accept)
37791cb875aeSCathy Zhou 		err = vrrpd_set_noaccept(vr, _B_TRUE);
37801cb875aeSCathy Zhou 
37811cb875aeSCathy Zhou done:
37821cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
37831cb875aeSCathy Zhou 		(void) close(vif->vvi_sockfd);
37841cb875aeSCathy Zhou 		vif->vvi_sockfd = -1;
37851cb875aeSCathy Zhou 		vr->vvr_vif = NULL;
37861cb875aeSCathy Zhou 	}
37871cb875aeSCathy Zhou 
37881cb875aeSCathy Zhou 	return (err);
37891cb875aeSCathy Zhou }
37901cb875aeSCathy Zhou 
37911cb875aeSCathy Zhou /*
37921cb875aeSCathy Zhou  * Create the IPv4 socket which is used to send VRRP packets. Note that
37931cb875aeSCathy Zhou  * the destination MAC address of VRRP advertisement must be the virtual
37941cb875aeSCathy Zhou  * MAC address, so we specify the output interface to be the specific VNIC.
37951cb875aeSCathy Zhou  */
37961cb875aeSCathy Zhou static vrrp_err_t
vrrpd_init_txsock_v4(vrrp_vr_t * vr)37971cb875aeSCathy Zhou vrrpd_init_txsock_v4(vrrp_vr_t *vr)
37981cb875aeSCathy Zhou {
37991cb875aeSCathy Zhou 	vrrp_intf_t *vif;	/* VNIC interface used to send packets */
38001cb875aeSCathy Zhou 	vrrp_ip_t *vip;		/* The first IP over the VNIC */
38011cb875aeSCathy Zhou 	int on = 1;
38021cb875aeSCathy Zhou 	char off = 0;
38031cb875aeSCathy Zhou 	vrrp_err_t err = VRRP_SUCCESS;
38041cb875aeSCathy Zhou 	char abuf[INET6_ADDRSTRLEN];
38051cb875aeSCathy Zhou 
38061cb875aeSCathy Zhou 	vif = vr->vvr_vif;
38071cb875aeSCathy Zhou 	assert(vr->vvr_conf.vvc_af == AF_INET);
38081cb875aeSCathy Zhou 	assert(vif != NULL);
38091cb875aeSCathy Zhou 
38101cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v4(%s) over %s",
38111cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name, vif->vvi_ifname);
38121cb875aeSCathy Zhou 
38131cb875aeSCathy Zhou 	if (vif->vvi_sockfd != -1) {
38141cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v4(%s) already done "
38151cb875aeSCathy Zhou 		    "over %s", vr->vvr_conf.vvc_name, vif->vvi_ifname);
38161cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
38171cb875aeSCathy Zhou 	}
38181cb875aeSCathy Zhou 
38191cb875aeSCathy Zhou 	vif->vvi_sockfd = socket(vif->vvi_af, SOCK_RAW, IPPROTO_VRRP);
38201cb875aeSCathy Zhou 	if (vif->vvi_sockfd < 0) {
38211cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): socket() "
38221cb875aeSCathy Zhou 		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
38231cb875aeSCathy Zhou 		err = VRRP_ESYS;
38241cb875aeSCathy Zhou 		goto done;
38251cb875aeSCathy Zhou 	}
38261cb875aeSCathy Zhou 
38271cb875aeSCathy Zhou 	/*
38281cb875aeSCathy Zhou 	 * Include the IP header, so that we can specify the IP address/ttl.
38291cb875aeSCathy Zhou 	 */
38301cb875aeSCathy Zhou 	if (setsockopt(vif->vvi_sockfd, IPPROTO_IP, IP_HDRINCL, (char *)&on,
38311cb875aeSCathy Zhou 	    sizeof (on)) < 0) {
38321cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): ip_hdrincl "
38331cb875aeSCathy Zhou 		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
38341cb875aeSCathy Zhou 		err = VRRP_ESYS;
38351cb875aeSCathy Zhou 		goto done;
38361cb875aeSCathy Zhou 	}
38371cb875aeSCathy Zhou 
38381cb875aeSCathy Zhou 	/*
38391cb875aeSCathy Zhou 	 * Disable multicast loopback.
38401cb875aeSCathy Zhou 	 */
38411cb875aeSCathy Zhou 	if (setsockopt(vif->vvi_sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &off,
38421cb875aeSCathy Zhou 	    sizeof (char)) == -1) {
38431cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): disable "
38441cb875aeSCathy Zhou 		    "multicast_loop failed: %s", vr->vvr_conf.vvc_name,
38451cb875aeSCathy Zhou 		    strerror(errno));
38461cb875aeSCathy Zhou 		err = VRRP_ESYS;
38471cb875aeSCathy Zhou 		goto done;
38481cb875aeSCathy Zhou 	}
38491cb875aeSCathy Zhou 
38501cb875aeSCathy Zhou 	vip = TAILQ_FIRST(&vif->vvi_iplist);
38511cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
38521cb875aeSCathy Zhou 	VRRPADDR2STR(vif->vvi_af, &vip->vip_addr, abuf, INET6_ADDRSTRLEN,
38531cb875aeSCathy Zhou 	    _B_FALSE);
38541cb875aeSCathy Zhou 
38551cb875aeSCathy Zhou 	/*
38561cb875aeSCathy Zhou 	 * Set the output interface to send the VRRP packet.
38571cb875aeSCathy Zhou 	 */
38581cb875aeSCathy Zhou 	if (setsockopt(vif->vvi_sockfd, IPPROTO_IP, IP_MULTICAST_IF,
38591cb875aeSCathy Zhou 	    &vip->vip_addr.in4.sin_addr, sizeof (struct in_addr)) < 0) {
38601cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v4(%s): multcast_if(%s) "
38611cb875aeSCathy Zhou 		    "failed: %s", vr->vvr_conf.vvc_name, abuf, strerror(errno));
38621cb875aeSCathy Zhou 		err = VRRP_ESYS;
38631cb875aeSCathy Zhou 	} else {
38641cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_init_txsock_v4(%s): multcast_if(%s) "
38651cb875aeSCathy Zhou 		    "succeed", vr->vvr_conf.vvc_name, abuf);
38661cb875aeSCathy Zhou 	}
38671cb875aeSCathy Zhou 
38681cb875aeSCathy Zhou done:
38691cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
38701cb875aeSCathy Zhou 		(void) close(vif->vvi_sockfd);
38711cb875aeSCathy Zhou 		vif->vvi_sockfd = -1;
38721cb875aeSCathy Zhou 	}
38731cb875aeSCathy Zhou 
38741cb875aeSCathy Zhou 	return (err);
38751cb875aeSCathy Zhou }
38761cb875aeSCathy Zhou 
38771cb875aeSCathy Zhou /*
38781cb875aeSCathy Zhou  * Create the IPv6 socket which is used to send VRRP packets. Note that
38791cb875aeSCathy Zhou  * the destination must be the virtual MAC address, so we specify the output
38801cb875aeSCathy Zhou  * interface to be the specific VNIC.
38811cb875aeSCathy Zhou  */
38821cb875aeSCathy Zhou static vrrp_err_t
vrrpd_init_txsock_v6(vrrp_vr_t * vr)38831cb875aeSCathy Zhou vrrpd_init_txsock_v6(vrrp_vr_t *vr)
38841cb875aeSCathy Zhou {
38851cb875aeSCathy Zhou 	vrrp_intf_t *vif;	/* VNIC interface used to send packets */
38861cb875aeSCathy Zhou 	int off = 0, ttl = VRRP_IP_TTL;
38871cb875aeSCathy Zhou 	vrrp_err_t err = VRRP_SUCCESS;
38881cb875aeSCathy Zhou 
38891cb875aeSCathy Zhou 	vif = vr->vvr_vif;
38901cb875aeSCathy Zhou 	assert(vr->vvr_conf.vvc_af == AF_INET6);
38911cb875aeSCathy Zhou 	assert(vif != NULL);
38921cb875aeSCathy Zhou 
38931cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v6(%s) over %s",
38941cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name, vif->vvi_ifname);
38951cb875aeSCathy Zhou 
38961cb875aeSCathy Zhou 	if (vif->vvi_sockfd != -1) {
38971cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v6(%s) already done "
38981cb875aeSCathy Zhou 		    "over %s", vr->vvr_conf.vvc_name, vif->vvi_ifname);
38991cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
39001cb875aeSCathy Zhou 	}
39011cb875aeSCathy Zhou 
39021cb875aeSCathy Zhou 	vif->vvi_sockfd = socket(vif->vvi_af, SOCK_RAW, IPPROTO_VRRP);
39031cb875aeSCathy Zhou 	if (vif->vvi_sockfd < 0) {
39041cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): socket() "
39051cb875aeSCathy Zhou 		    "failed: %s", vr->vvr_conf.vvc_name, strerror(errno));
39061cb875aeSCathy Zhou 		err = VRRP_ESYS;
39071cb875aeSCathy Zhou 		goto done;
39081cb875aeSCathy Zhou 	}
39091cb875aeSCathy Zhou 
39101cb875aeSCathy Zhou 	/*
39111cb875aeSCathy Zhou 	 * Disable multicast loopback.
39121cb875aeSCathy Zhou 	 */
39131cb875aeSCathy Zhou 	if (setsockopt(vif->vvi_sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
39141cb875aeSCathy Zhou 	    &off, sizeof (int)) == -1) {
39151cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): disable "
39161cb875aeSCathy Zhou 		    "multicast_loop failed: %s", vr->vvr_conf.vvc_name,
39171cb875aeSCathy Zhou 		    strerror(errno));
39181cb875aeSCathy Zhou 		err = VRRP_ESYS;
39191cb875aeSCathy Zhou 		goto done;
39201cb875aeSCathy Zhou 	}
39211cb875aeSCathy Zhou 
39221cb875aeSCathy Zhou 	/*
39231cb875aeSCathy Zhou 	 * Set the multicast TTL.
39241cb875aeSCathy Zhou 	 */
39251cb875aeSCathy Zhou 	if (setsockopt(vif->vvi_sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
39261cb875aeSCathy Zhou 	    &ttl, sizeof (int)) == -1) {
39271cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): enable "
39281cb875aeSCathy Zhou 		    "multicast_hops %d failed: %s", vr->vvr_conf.vvc_name,
39291cb875aeSCathy Zhou 		    ttl, strerror(errno));
39301cb875aeSCathy Zhou 		err = VRRP_ESYS;
39311cb875aeSCathy Zhou 		goto done;
39321cb875aeSCathy Zhou 	}
39331cb875aeSCathy Zhou 
39341cb875aeSCathy Zhou 	/*
39351cb875aeSCathy Zhou 	 * Set the output interface to send the VRRP packet.
39361cb875aeSCathy Zhou 	 */
39371cb875aeSCathy Zhou 	if (setsockopt(vif->vvi_sockfd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
39381cb875aeSCathy Zhou 	    &vif->vvi_ifindex, sizeof (uint32_t)) < 0) {
39391cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_init_txsock_v6(%s): multicast_if(%d) "
39401cb875aeSCathy Zhou 		    "failed: %s", vr->vvr_conf.vvc_name, vif->vvi_ifindex,
39411cb875aeSCathy Zhou 		    strerror(errno));
39421cb875aeSCathy Zhou 		err = VRRP_ESYS;
39431cb875aeSCathy Zhou 	} else {
39441cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_init_txsock_v6(%s): multicast_if(%d)"
39451cb875aeSCathy Zhou 		    " succeed", vr->vvr_conf.vvc_name, vif->vvi_ifindex);
39461cb875aeSCathy Zhou 	}
39471cb875aeSCathy Zhou 
39481cb875aeSCathy Zhou done:
39491cb875aeSCathy Zhou 	if (err != VRRP_SUCCESS) {
39501cb875aeSCathy Zhou 		(void) close(vif->vvi_sockfd);
39511cb875aeSCathy Zhou 		vif->vvi_sockfd = -1;
39521cb875aeSCathy Zhou 	}
39531cb875aeSCathy Zhou 
39541cb875aeSCathy Zhou 	return (err);
39551cb875aeSCathy Zhou }
39561cb875aeSCathy Zhou 
39571cb875aeSCathy Zhou /*
39581cb875aeSCathy Zhou  * Delete the socket which is used to send VRRP packets. Further, clear
39591cb875aeSCathy Zhou  * the IFF_NOACCEPT flag based on the VRRP router's accept mode.
39601cb875aeSCathy Zhou  */
39611cb875aeSCathy Zhou static void
vrrpd_fini_txsock(vrrp_vr_t * vr)39621cb875aeSCathy Zhou vrrpd_fini_txsock(vrrp_vr_t *vr)
39631cb875aeSCathy Zhou {
39641cb875aeSCathy Zhou 	vrrp_intf_t *vif = vr->vvr_vif;
39651cb875aeSCathy Zhou 
39661cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_fini_txsock(%s)", vr->vvr_conf.vvc_name);
39671cb875aeSCathy Zhou 
39681cb875aeSCathy Zhou 	if (vif != NULL) {
39691cb875aeSCathy Zhou 		if (!vr->vvr_conf.vvc_accept)
39701cb875aeSCathy Zhou 			(void) vrrpd_set_noaccept(vr, _B_FALSE);
39711cb875aeSCathy Zhou 		(void) close(vif->vvi_sockfd);
39721cb875aeSCathy Zhou 		vif->vvi_sockfd = -1;
39731cb875aeSCathy Zhou 		vr->vvr_vif = NULL;
39741cb875aeSCathy Zhou 	}
39751cb875aeSCathy Zhou }
39761cb875aeSCathy Zhou 
39771cb875aeSCathy Zhou /*
39781cb875aeSCathy Zhou  * Given the the pseudo header cksum value (sum), caculate the cksum with
39791cb875aeSCathy Zhou  * the rest of VRRP packet.
39801cb875aeSCathy Zhou  */
39811cb875aeSCathy Zhou static uint16_t
in_cksum(int sum,uint16_t plen,void * p)39821cb875aeSCathy Zhou in_cksum(int sum, uint16_t plen, void *p)
39831cb875aeSCathy Zhou {
39841cb875aeSCathy Zhou 	int nleft;
39851cb875aeSCathy Zhou 	uint16_t *w;
39861cb875aeSCathy Zhou 	uint16_t answer;
39871cb875aeSCathy Zhou 	uint16_t odd_byte = 0;
39881cb875aeSCathy Zhou 
39891cb875aeSCathy Zhou 	nleft = plen;
39901cb875aeSCathy Zhou 	w = (uint16_t *)p;
39911cb875aeSCathy Zhou 	while (nleft > 1) {
39921cb875aeSCathy Zhou 		sum += *w++;
39931cb875aeSCathy Zhou 		nleft -= 2;
39941cb875aeSCathy Zhou 	}
39951cb875aeSCathy Zhou 
39961cb875aeSCathy Zhou 	/* mop up an odd byte, if necessary */
39971cb875aeSCathy Zhou 	if (nleft == 1) {
39981cb875aeSCathy Zhou 		*(uchar_t *)(&odd_byte) = *(uchar_t *)w;
39991cb875aeSCathy Zhou 		sum += odd_byte;
40001cb875aeSCathy Zhou 	}
40011cb875aeSCathy Zhou 
40021cb875aeSCathy Zhou 	/*
40031cb875aeSCathy Zhou 	 * add back carry outs from top 16 bits to low 16 bits
40041cb875aeSCathy Zhou 	 */
40051cb875aeSCathy Zhou 	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
40061cb875aeSCathy Zhou 	sum += (sum >> 16);			/* add carry */
40071cb875aeSCathy Zhou 	answer = ~sum;				/* truncate to 16 bits */
40081cb875aeSCathy Zhou 	return (answer == 0 ? ~0 : answer);
40091cb875aeSCathy Zhou }
40101cb875aeSCathy Zhou 
40111cb875aeSCathy Zhou /* Pseudo header for v4 */
40121cb875aeSCathy Zhou struct pshv4 {
40131cb875aeSCathy Zhou 	struct in_addr	ph4_src;
40141cb875aeSCathy Zhou 	struct in_addr	ph4_dst;
40151cb875aeSCathy Zhou 	uint8_t		ph4_zero;	/* always zero */
40161cb875aeSCathy Zhou 	uint8_t		ph4_protocol;	/* protocol used, IPPROTO_VRRP */
40171cb875aeSCathy Zhou 	uint16_t	ph4_len;	/* VRRP payload len */
40181cb875aeSCathy Zhou };
40191cb875aeSCathy Zhou 
40201cb875aeSCathy Zhou /*
40211cb875aeSCathy Zhou  * Checksum routine for VRRP checksum. Note that plen is the upper-layer
40221cb875aeSCathy Zhou  * packet length (in the host byte order), and both IP source and destination
40231cb875aeSCathy Zhou  * addresses are in the network byte order.
40241cb875aeSCathy Zhou  */
40251cb875aeSCathy Zhou static uint16_t
vrrp_cksum4(struct in_addr * src,struct in_addr * dst,uint16_t plen,vrrp_pkt_t * vp)40261cb875aeSCathy Zhou vrrp_cksum4(struct in_addr *src, struct in_addr *dst, uint16_t plen,
40271cb875aeSCathy Zhou     vrrp_pkt_t *vp)
40281cb875aeSCathy Zhou {
40291cb875aeSCathy Zhou 	struct pshv4 ph4;
40301cb875aeSCathy Zhou 	int nleft;
40311cb875aeSCathy Zhou 	uint16_t *w;
40321cb875aeSCathy Zhou 	int sum = 0;
40331cb875aeSCathy Zhou 
40341cb875aeSCathy Zhou 	ph4.ph4_src = *src;
40351cb875aeSCathy Zhou 	ph4.ph4_dst = *dst;
40361cb875aeSCathy Zhou 	ph4.ph4_zero = 0;
40371cb875aeSCathy Zhou 	ph4.ph4_protocol = IPPROTO_VRRP;
40381cb875aeSCathy Zhou 	ph4.ph4_len = htons(plen);
40391cb875aeSCathy Zhou 
40401cb875aeSCathy Zhou 	/*
40411cb875aeSCathy Zhou 	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
40421cb875aeSCathy Zhou 	 *  we add sequential 16 bit words to it, and at the end, fold
40431cb875aeSCathy Zhou 	 *  back all the carry bits from the top 16 bits into the lower
40441cb875aeSCathy Zhou 	 *  16 bits.
40451cb875aeSCathy Zhou 	 */
40461cb875aeSCathy Zhou 	nleft = sizeof (struct pshv4);
40471cb875aeSCathy Zhou 	w = (uint16_t *)&ph4;
40481cb875aeSCathy Zhou 	while (nleft > 0) {
40491cb875aeSCathy Zhou 		sum += *w++;
40501cb875aeSCathy Zhou 		nleft -= 2;
40511cb875aeSCathy Zhou 	}
40521cb875aeSCathy Zhou 
40531cb875aeSCathy Zhou 	return (in_cksum(sum, plen, vp));
40541cb875aeSCathy Zhou }
40551cb875aeSCathy Zhou 
40561cb875aeSCathy Zhou /* Pseudo header for v6 */
40571cb875aeSCathy Zhou struct pshv6 {
40581cb875aeSCathy Zhou 	struct in6_addr	ph6_src;
40591cb875aeSCathy Zhou 	struct in6_addr	ph6_dst;
40601cb875aeSCathy Zhou 	uint32_t	ph6_len;	/* VRRP payload len */
40611cb875aeSCathy Zhou 	uint32_t	ph6_zero : 24,
40621cb875aeSCathy Zhou 			ph6_protocol : 8; /* protocol used, IPPROTO_VRRP */
40631cb875aeSCathy Zhou };
40641cb875aeSCathy Zhou 
40651cb875aeSCathy Zhou /*
40661cb875aeSCathy Zhou  * Checksum routine for VRRP checksum. Note that plen is the upper-layer
40671cb875aeSCathy Zhou  * packet length (in the host byte order), and both IP source and destination
40681cb875aeSCathy Zhou  * addresses are in the network byte order.
40691cb875aeSCathy Zhou  */
40701cb875aeSCathy Zhou static uint16_t
vrrp_cksum6(struct in6_addr * src,struct in6_addr * dst,uint16_t plen,vrrp_pkt_t * vp)40711cb875aeSCathy Zhou vrrp_cksum6(struct in6_addr *src, struct in6_addr *dst, uint16_t plen,
40721cb875aeSCathy Zhou     vrrp_pkt_t *vp)
40731cb875aeSCathy Zhou {
40741cb875aeSCathy Zhou 	struct pshv6 ph6;
40751cb875aeSCathy Zhou 	int nleft;
40761cb875aeSCathy Zhou 	uint16_t *w;
40771cb875aeSCathy Zhou 	int sum = 0;
40781cb875aeSCathy Zhou 
40791cb875aeSCathy Zhou 	ph6.ph6_src = *src;
40801cb875aeSCathy Zhou 	ph6.ph6_dst = *dst;
40811cb875aeSCathy Zhou 	ph6.ph6_zero = 0;
40821cb875aeSCathy Zhou 	ph6.ph6_protocol = IPPROTO_VRRP;
40831cb875aeSCathy Zhou 	ph6.ph6_len = htonl((uint32_t)plen);
40841cb875aeSCathy Zhou 
40851cb875aeSCathy Zhou 	/*
40861cb875aeSCathy Zhou 	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
40871cb875aeSCathy Zhou 	 *  we add sequential 16 bit words to it, and at the end, fold
40881cb875aeSCathy Zhou 	 *  back all the carry bits from the top 16 bits into the lower
40891cb875aeSCathy Zhou 	 *  16 bits.
40901cb875aeSCathy Zhou 	 */
40911cb875aeSCathy Zhou 	nleft = sizeof (struct pshv6);
40921cb875aeSCathy Zhou 	w = (uint16_t *)&ph6;
40931cb875aeSCathy Zhou 	while (nleft > 0) {
40941cb875aeSCathy Zhou 		sum += *w++;
40951cb875aeSCathy Zhou 		nleft -= 2;
40961cb875aeSCathy Zhou 	}
40971cb875aeSCathy Zhou 
40981cb875aeSCathy Zhou 	return (in_cksum(sum, plen, vp));
40991cb875aeSCathy Zhou }
41001cb875aeSCathy Zhou 
41011cb875aeSCathy Zhou vrrp_err_t
vrrpd_state_i2m(vrrp_vr_t * vr)41021cb875aeSCathy Zhou vrrpd_state_i2m(vrrp_vr_t *vr)
41031cb875aeSCathy Zhou {
41041cb875aeSCathy Zhou 	vrrp_err_t	err;
41051cb875aeSCathy Zhou 
41061cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_state_i2m(%s)", vr->vvr_conf.vvc_name);
41071cb875aeSCathy Zhou 
41081cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_INIT, VRRP_STATE_MASTER, vr);
41091cb875aeSCathy Zhou 	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
41101cb875aeSCathy Zhou 		return (err);
41111cb875aeSCathy Zhou 
41121cb875aeSCathy Zhou 	(void) vrrpd_send_adv(vr, _B_FALSE);
41131cb875aeSCathy Zhou 
41141cb875aeSCathy Zhou 	vr->vvr_err = VRRP_SUCCESS;
41151cb875aeSCathy Zhou 	vr->vvr_timeout = vr->vvr_conf.vvc_adver_int;
41161cb875aeSCathy Zhou 	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
41171cb875aeSCathy Zhou 	    vr->vvr_timeout, vrrp_adv_timeout, vr)) == -1) {
41181cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_state_i2m(): unable to start timer");
41191cb875aeSCathy Zhou 		return (VRRP_ESYS);
41201cb875aeSCathy Zhou 	} else {
41211cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_state_i2m(%s): start "
41221cb875aeSCathy Zhou 		    "vrrp_adv_timeout(%d)", vr->vvr_conf.vvc_name,
41231cb875aeSCathy Zhou 		    vr->vvr_timeout);
41241cb875aeSCathy Zhou 	}
41251cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
41261cb875aeSCathy Zhou }
41271cb875aeSCathy Zhou 
41281cb875aeSCathy Zhou vrrp_err_t
vrrpd_state_i2b(vrrp_vr_t * vr)41291cb875aeSCathy Zhou vrrpd_state_i2b(vrrp_vr_t *vr)
41301cb875aeSCathy Zhou {
41311cb875aeSCathy Zhou 	vrrp_err_t	err;
41321cb875aeSCathy Zhou 
41331cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_state_i2b(%s)", vr->vvr_conf.vvc_name);
41341cb875aeSCathy Zhou 
41351cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_INIT, VRRP_STATE_BACKUP, vr);
41361cb875aeSCathy Zhou 	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
41371cb875aeSCathy Zhou 		return (err);
41381cb875aeSCathy Zhou 
41391cb875aeSCathy Zhou 	/*
41401cb875aeSCathy Zhou 	 * Reinitialize the Master advertisement interval to be the configured
41411cb875aeSCathy Zhou 	 * value.
41421cb875aeSCathy Zhou 	 */
41431cb875aeSCathy Zhou 	vr->vvr_err = VRRP_SUCCESS;
41441cb875aeSCathy Zhou 	vr->vvr_master_adver_int = vr->vvr_conf.vvc_adver_int;
41451cb875aeSCathy Zhou 	vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
41461cb875aeSCathy Zhou 	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
41471cb875aeSCathy Zhou 	    vr->vvr_timeout, vrrp_b2m_timeout, vr)) == -1) {
41481cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_state_i2b(): unable to set timer");
41491cb875aeSCathy Zhou 		return (VRRP_ESYS);
41501cb875aeSCathy Zhou 	} else {
41511cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_state_i2b(%s): start "
41521cb875aeSCathy Zhou 		    "vrrp_b2m_timeout(%d)", vr->vvr_conf.vvc_name,
41531cb875aeSCathy Zhou 		    vr->vvr_timeout);
41541cb875aeSCathy Zhou 	}
41551cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
41561cb875aeSCathy Zhou }
41571cb875aeSCathy Zhou 
41581cb875aeSCathy Zhou void
vrrpd_state_m2i(vrrp_vr_t * vr)41591cb875aeSCathy Zhou vrrpd_state_m2i(vrrp_vr_t *vr)
41601cb875aeSCathy Zhou {
41611cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_state_m2i(%s)", vr->vvr_conf.vvc_name);
41621cb875aeSCathy Zhou 
41631cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_MASTER, VRRP_STATE_INIT, vr);
41641cb875aeSCathy Zhou 	(void) vrrpd_virtualip_update(vr, _B_TRUE);
41651cb875aeSCathy Zhou 	bzero(&vr->vvr_peer, sizeof (vrrp_peer_t));
41661cb875aeSCathy Zhou 	(void) iu_cancel_timer(vrrpd_timerq, vr->vvr_timer_id, NULL);
41671cb875aeSCathy Zhou }
41681cb875aeSCathy Zhou 
41691cb875aeSCathy Zhou void
vrrpd_state_b2i(vrrp_vr_t * vr)41701cb875aeSCathy Zhou vrrpd_state_b2i(vrrp_vr_t *vr)
41711cb875aeSCathy Zhou {
41721cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_state_b2i(%s)", vr->vvr_conf.vvc_name);
41731cb875aeSCathy Zhou 
41741cb875aeSCathy Zhou 	bzero(&vr->vvr_peer, sizeof (vrrp_peer_t));
41751cb875aeSCathy Zhou 	(void) iu_cancel_timer(vrrpd_timerq, vr->vvr_timer_id, NULL);
41761cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_BACKUP, VRRP_STATE_INIT, vr);
41771cb875aeSCathy Zhou 	(void) vrrpd_virtualip_update(vr, _B_TRUE);
41781cb875aeSCathy Zhou }
41791cb875aeSCathy Zhou 
41801cb875aeSCathy Zhou /* ARGSUSED */
41811cb875aeSCathy Zhou static void
vrrp_b2m_timeout(iu_tq_t * tq,void * arg)41821cb875aeSCathy Zhou vrrp_b2m_timeout(iu_tq_t *tq, void *arg)
41831cb875aeSCathy Zhou {
41841cb875aeSCathy Zhou 	vrrp_vr_t *vr = (vrrp_vr_t *)arg;
41851cb875aeSCathy Zhou 
41861cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrp_b2m_timeout(%s)", vr->vvr_conf.vvc_name);
41871cb875aeSCathy Zhou 	(void) vrrpd_state_b2m(vr);
41881cb875aeSCathy Zhou }
41891cb875aeSCathy Zhou 
41901cb875aeSCathy Zhou /* ARGSUSED */
41911cb875aeSCathy Zhou static void
vrrp_adv_timeout(iu_tq_t * tq,void * arg)41921cb875aeSCathy Zhou vrrp_adv_timeout(iu_tq_t *tq, void *arg)
41931cb875aeSCathy Zhou {
41941cb875aeSCathy Zhou 	vrrp_vr_t *vr = (vrrp_vr_t *)arg;
41951cb875aeSCathy Zhou 
41961cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrp_adv_timeout(%s)", vr->vvr_conf.vvc_name);
41971cb875aeSCathy Zhou 
41981cb875aeSCathy Zhou 	(void) vrrpd_send_adv(vr, _B_FALSE);
41991cb875aeSCathy Zhou 	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
42001cb875aeSCathy Zhou 	    vr->vvr_timeout, vrrp_adv_timeout, vr)) == -1) {
42011cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrp_adv_timeout(%s): start timer failed",
42021cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name);
42031cb875aeSCathy Zhou 	} else {
42041cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrp_adv_timeout(%s): start "
42051cb875aeSCathy Zhou 		    "vrrp_adv_timeout(%d)", vr->vvr_conf.vvc_name,
42061cb875aeSCathy Zhou 		    vr->vvr_timeout);
42071cb875aeSCathy Zhou 	}
42081cb875aeSCathy Zhou }
42091cb875aeSCathy Zhou 
42101cb875aeSCathy Zhou vrrp_err_t
vrrpd_state_b2m(vrrp_vr_t * vr)42111cb875aeSCathy Zhou vrrpd_state_b2m(vrrp_vr_t *vr)
42121cb875aeSCathy Zhou {
42131cb875aeSCathy Zhou 	vrrp_err_t	err;
42141cb875aeSCathy Zhou 
42151cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_state_b2m(%s)", vr->vvr_conf.vvc_name);
42161cb875aeSCathy Zhou 
42171cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_BACKUP, VRRP_STATE_MASTER, vr);
42181cb875aeSCathy Zhou 	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
42191cb875aeSCathy Zhou 		return (err);
42201cb875aeSCathy Zhou 	(void) vrrpd_send_adv(vr, _B_FALSE);
42211cb875aeSCathy Zhou 
42221cb875aeSCathy Zhou 	vr->vvr_timeout = vr->vvr_conf.vvc_adver_int;
42231cb875aeSCathy Zhou 	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
42241cb875aeSCathy Zhou 	    vr->vvr_timeout, vrrp_adv_timeout, vr)) == -1) {
42251cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_state_b2m(%s): start timer failed",
42261cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name);
42271cb875aeSCathy Zhou 		return (VRRP_ESYS);
42281cb875aeSCathy Zhou 	} else {
42291cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_state_b2m(%s): start "
42301cb875aeSCathy Zhou 		    "vrrp_adv_timeout(%d)", vr->vvr_conf.vvc_name,
42311cb875aeSCathy Zhou 		    vr->vvr_timeout);
42321cb875aeSCathy Zhou 	}
42331cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
42341cb875aeSCathy Zhou }
42351cb875aeSCathy Zhou 
42361cb875aeSCathy Zhou vrrp_err_t
vrrpd_state_m2b(vrrp_vr_t * vr)42371cb875aeSCathy Zhou vrrpd_state_m2b(vrrp_vr_t *vr)
42381cb875aeSCathy Zhou {
42391cb875aeSCathy Zhou 	vrrp_err_t	err;
42401cb875aeSCathy Zhou 
42411cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_state_m2b(%s)", vr->vvr_conf.vvc_name);
42421cb875aeSCathy Zhou 
42431cb875aeSCathy Zhou 	vrrpd_state_trans(VRRP_STATE_MASTER, VRRP_STATE_BACKUP, vr);
42441cb875aeSCathy Zhou 	if ((err = vrrpd_virtualip_update(vr, _B_FALSE)) != VRRP_SUCCESS)
42451cb875aeSCathy Zhou 		return (err);
42461cb875aeSCathy Zhou 
42471cb875aeSCathy Zhou 	/*
42481cb875aeSCathy Zhou 	 * Cancel the adver_timer.
42491cb875aeSCathy Zhou 	 */
42501cb875aeSCathy Zhou 	vr->vvr_master_adver_int = vr->vvr_peer_adver_int;
42511cb875aeSCathy Zhou 	(void) iu_cancel_timer(vrrpd_timerq, vr->vvr_timer_id, NULL);
42521cb875aeSCathy Zhou 	vr->vvr_timeout = MASTER_DOWN_INTERVAL_VR(vr);
42531cb875aeSCathy Zhou 	if ((vr->vvr_timer_id = iu_schedule_timer_ms(vrrpd_timerq,
42541cb875aeSCathy Zhou 	    vr->vvr_timeout, vrrp_b2m_timeout, vr)) == -1) {
42551cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "vrrpd_state_m2b(%s): start timer failed",
42561cb875aeSCathy Zhou 		    vr->vvr_conf.vvc_name);
42571cb875aeSCathy Zhou 	} else {
42581cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG1, "vrrpd_state_m2b(%s) start "
42591cb875aeSCathy Zhou 		    "vrrp_b2m_timeout(%d)", vr->vvr_conf.vvc_name,
42601cb875aeSCathy Zhou 		    vr->vvr_timeout);
42611cb875aeSCathy Zhou 	}
42621cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
42631cb875aeSCathy Zhou }
42641cb875aeSCathy Zhou 
42651cb875aeSCathy Zhou /*
42661cb875aeSCathy Zhou  * Set the IFF_NOACCESS flag on the VNIC interface of the VRRP router
42671cb875aeSCathy Zhou  * based on its access mode.
42681cb875aeSCathy Zhou  */
42691cb875aeSCathy Zhou static vrrp_err_t
vrrpd_set_noaccept(vrrp_vr_t * vr,boolean_t on)42701cb875aeSCathy Zhou vrrpd_set_noaccept(vrrp_vr_t *vr, boolean_t on)
42711cb875aeSCathy Zhou {
42721cb875aeSCathy Zhou 	vrrp_intf_t *vif = vr->vvr_vif;
42731cb875aeSCathy Zhou 	uint64_t curr_flags;
42741cb875aeSCathy Zhou 	struct lifreq lifr;
42751cb875aeSCathy Zhou 	int s;
42761cb875aeSCathy Zhou 
42771cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_set_noaccept(%s, %s)",
42781cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name, on ? "on" : "off");
42791cb875aeSCathy Zhou 
42801cb875aeSCathy Zhou 	/*
42811cb875aeSCathy Zhou 	 * Possibly no virtual address exists on this VRRP router yet.
42821cb875aeSCathy Zhou 	 */
42831cb875aeSCathy Zhou 	if (vif == NULL)
42841cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
42851cb875aeSCathy Zhou 
42861cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_set_noaccept(%s, %s)",
42871cb875aeSCathy Zhou 	    vif->vvi_ifname, vrrp_state2str(vr->vvr_state));
42881cb875aeSCathy Zhou 
42891cb875aeSCathy Zhou 	s = (vif->vvi_af == AF_INET) ? vrrpd_ctlsock_fd : vrrpd_ctlsock6_fd;
42901cb875aeSCathy Zhou 	(void) strncpy(lifr.lifr_name, vif->vvi_ifname,
42911cb875aeSCathy Zhou 	    sizeof (lifr.lifr_name));
42921cb875aeSCathy Zhou 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
42931cb875aeSCathy Zhou 		if (errno != ENXIO && errno != ENOENT) {
42941cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_set_noaccept(): "
42951cb875aeSCathy Zhou 			    "SIOCGLIFFLAGS on %s failed: %s",
42961cb875aeSCathy Zhou 			    vif->vvi_ifname, strerror(errno));
42971cb875aeSCathy Zhou 		}
42981cb875aeSCathy Zhou 		return (VRRP_ESYS);
42991cb875aeSCathy Zhou 	}
43001cb875aeSCathy Zhou 
43011cb875aeSCathy Zhou 	curr_flags = lifr.lifr_flags;
43021cb875aeSCathy Zhou 	if (on)
43031cb875aeSCathy Zhou 		lifr.lifr_flags |= IFF_NOACCEPT;
43041cb875aeSCathy Zhou 	else
43051cb875aeSCathy Zhou 		lifr.lifr_flags &= ~IFF_NOACCEPT;
43061cb875aeSCathy Zhou 
43071cb875aeSCathy Zhou 	if (lifr.lifr_flags != curr_flags) {
43081cb875aeSCathy Zhou 		if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
43091cb875aeSCathy Zhou 			if (errno != ENXIO && errno != ENOENT) {
43101cb875aeSCathy Zhou 				vrrp_log(VRRP_ERR, "vrrpd_set_noaccept(%s): "
43111cb875aeSCathy Zhou 				    "SIOCSLIFFLAGS 0x%llx on %s failed: %s",
43121cb875aeSCathy Zhou 				    on ? "no_accept" : "accept",
43131cb875aeSCathy Zhou 				    lifr.lifr_flags, vif->vvi_ifname,
43141cb875aeSCathy Zhou 				    strerror(errno));
43151cb875aeSCathy Zhou 			}
43161cb875aeSCathy Zhou 			return (VRRP_ESYS);
43171cb875aeSCathy Zhou 		}
43181cb875aeSCathy Zhou 	}
43191cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
43201cb875aeSCathy Zhou }
43211cb875aeSCathy Zhou 
43221cb875aeSCathy Zhou static vrrp_err_t
vrrpd_virtualip_updateone(vrrp_intf_t * vif,vrrp_ip_t * ip,boolean_t checkonly)43231cb875aeSCathy Zhou vrrpd_virtualip_updateone(vrrp_intf_t *vif, vrrp_ip_t *ip, boolean_t checkonly)
43241cb875aeSCathy Zhou {
43251cb875aeSCathy Zhou 	vrrp_state_t	state = vif->vvi_vr_state;
43261cb875aeSCathy Zhou 	struct lifreq	lifr;
43271cb875aeSCathy Zhou 	char		abuf[INET6_ADDRSTRLEN];
43281cb875aeSCathy Zhou 	int		af = vif->vvi_af;
43291cb875aeSCathy Zhou 	uint64_t	curr_flags;
43301cb875aeSCathy Zhou 	int		s;
43311cb875aeSCathy Zhou 
43321cb875aeSCathy Zhou 	assert(IS_VIRTUAL_INTF(vif));
43331cb875aeSCathy Zhou 
43341cb875aeSCathy Zhou 	/* LINTED E_CONSTANT_CONDITION */
43351cb875aeSCathy Zhou 	VRRPADDR2STR(af, &ip->vip_addr, abuf, INET6_ADDRSTRLEN, _B_FALSE);
43361cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_virtualip_updateone(%s, %s%s)",
43371cb875aeSCathy Zhou 	    vif->vvi_ifname, abuf, checkonly ? ", checkonly" : "");
43381cb875aeSCathy Zhou 
43391cb875aeSCathy Zhou 	s = (af == AF_INET) ? vrrpd_ctlsock_fd : vrrpd_ctlsock6_fd;
43401cb875aeSCathy Zhou 	(void) strncpy(lifr.lifr_name, ip->vip_lifname,
43411cb875aeSCathy Zhou 	    sizeof (lifr.lifr_name));
43421cb875aeSCathy Zhou 	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
43431cb875aeSCathy Zhou 		if (errno != ENXIO && errno != ENOENT) {
43441cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_virtualip_updateone(%s): "
43451cb875aeSCathy Zhou 			    "SIOCGLIFFLAGS on %s/%s failed: %s",
43461cb875aeSCathy Zhou 			    vif->vvi_ifname, lifr.lifr_name, abuf,
43471cb875aeSCathy Zhou 			    strerror(errno));
43481cb875aeSCathy Zhou 		}
43491cb875aeSCathy Zhou 		return (VRRP_ESYS);
43501cb875aeSCathy Zhou 	}
43511cb875aeSCathy Zhou 
43521cb875aeSCathy Zhou 	curr_flags = lifr.lifr_flags;
43531cb875aeSCathy Zhou 	if (state == VRRP_STATE_MASTER)
43541cb875aeSCathy Zhou 		lifr.lifr_flags |= IFF_UP;
43551cb875aeSCathy Zhou 	else
43561cb875aeSCathy Zhou 		lifr.lifr_flags &= ~IFF_UP;
43571cb875aeSCathy Zhou 
43581cb875aeSCathy Zhou 	if (lifr.lifr_flags == curr_flags)
43591cb875aeSCathy Zhou 		return (VRRP_SUCCESS);
43601cb875aeSCathy Zhou 
43611cb875aeSCathy Zhou 	if (checkonly) {
43621cb875aeSCathy Zhou 		vrrp_log(VRRP_ERR, "VRRP virtual IP %s/%s was brought %s",
43631cb875aeSCathy Zhou 		    ip->vip_lifname, abuf,
43641cb875aeSCathy Zhou 		    state == VRRP_STATE_MASTER ? "down" : "up");
43651cb875aeSCathy Zhou 		return (VRRP_ESYS);
43661cb875aeSCathy Zhou 	} else if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
43671cb875aeSCathy Zhou 		if (errno != ENXIO && errno != ENOENT) {
43681cb875aeSCathy Zhou 			vrrp_log(VRRP_ERR, "vrrpd_virtualip_updateone(%s, %s): "
43691cb875aeSCathy Zhou 			    "bring %s %s/%s failed: %s",
43701cb875aeSCathy Zhou 			    vif->vvi_ifname, vrrp_state2str(state),
43711cb875aeSCathy Zhou 			    state == VRRP_STATE_MASTER ? "up" : "down",
43721cb875aeSCathy Zhou 			    ip->vip_lifname, abuf, strerror(errno));
43731cb875aeSCathy Zhou 		}
43741cb875aeSCathy Zhou 		return (VRRP_ESYS);
43751cb875aeSCathy Zhou 	}
43761cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
43771cb875aeSCathy Zhou }
43781cb875aeSCathy Zhou 
43791cb875aeSCathy Zhou static vrrp_err_t
vrrpd_virtualip_update(vrrp_vr_t * vr,boolean_t checkonly)43801cb875aeSCathy Zhou vrrpd_virtualip_update(vrrp_vr_t *vr, boolean_t checkonly)
43811cb875aeSCathy Zhou {
43821cb875aeSCathy Zhou 	vrrp_state_t		state;
43831cb875aeSCathy Zhou 	vrrp_intf_t		*vif = vr->vvr_vif;
43841cb875aeSCathy Zhou 	vrrp_ip_t		*ip, *nextip;
43851cb875aeSCathy Zhou 	char			abuf[INET6_ADDRSTRLEN];
43861cb875aeSCathy Zhou 	vrrp_err_t		err;
43871cb875aeSCathy Zhou 
43881cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_virtualip_update(%s, %s, %s)%s",
43891cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name, vrrp_state2str(vr->vvr_state),
43901cb875aeSCathy Zhou 	    vif->vvi_ifname, checkonly ? " checkonly" : "");
43911cb875aeSCathy Zhou 
43921cb875aeSCathy Zhou 	state = vr->vvr_state;
43931cb875aeSCathy Zhou 	assert(vif != NULL);
43941cb875aeSCathy Zhou 	assert(IS_VIRTUAL_INTF(vif));
43951cb875aeSCathy Zhou 	assert(vif->vvi_vr_state != state);
43961cb875aeSCathy Zhou 	vif->vvi_vr_state = state;
43971cb875aeSCathy Zhou 	for (ip = TAILQ_FIRST(&vif->vvi_iplist); ip != NULL; ip = nextip) {
43981cb875aeSCathy Zhou 		nextip = TAILQ_NEXT(ip, vip_next);
43991cb875aeSCathy Zhou 		err = vrrpd_virtualip_updateone(vif, ip, _B_FALSE);
44001cb875aeSCathy Zhou 		if (!checkonly && err != VRRP_SUCCESS) {
44011cb875aeSCathy Zhou 			/* LINTED E_CONSTANT_CONDITION */
44021cb875aeSCathy Zhou 			VRRPADDR2STR(vif->vvi_af, &ip->vip_addr, abuf,
44031cb875aeSCathy Zhou 			    INET6_ADDRSTRLEN, _B_FALSE);
44041cb875aeSCathy Zhou 			vrrp_log(VRRP_DBG1, "vrrpd_virtualip_update() update "
44051cb875aeSCathy Zhou 			    "%s over %s failed", abuf, vif->vvi_ifname);
44061cb875aeSCathy Zhou 			vrrpd_delete_ip(vif, ip);
44071cb875aeSCathy Zhou 		}
44081cb875aeSCathy Zhou 	}
44091cb875aeSCathy Zhou 
44101cb875aeSCathy Zhou 	/*
44111cb875aeSCathy Zhou 	 * The IP address is deleted when it is failed to be brought
44121cb875aeSCathy Zhou 	 * up. If no IP addresses are left, delete this interface.
44131cb875aeSCathy Zhou 	 */
44141cb875aeSCathy Zhou 	if (!checkonly && TAILQ_EMPTY(&vif->vvi_iplist)) {
44151cb875aeSCathy Zhou 		vrrp_log(VRRP_DBG0, "vrrpd_virtualip_update(): "
44161cb875aeSCathy Zhou 		    "no IP left over %s", vif->vvi_ifname);
44171cb875aeSCathy Zhou 		vrrpd_delete_if(vif, _B_TRUE);
44181cb875aeSCathy Zhou 		return (VRRP_ENOVIRT);
44191cb875aeSCathy Zhou 	}
44201cb875aeSCathy Zhou 	return (VRRP_SUCCESS);
44211cb875aeSCathy Zhou }
44221cb875aeSCathy Zhou 
44231cb875aeSCathy Zhou void
vrrpd_state_trans(vrrp_state_t prev_s,vrrp_state_t s,vrrp_vr_t * vr)44241cb875aeSCathy Zhou vrrpd_state_trans(vrrp_state_t prev_s, vrrp_state_t s, vrrp_vr_t *vr)
44251cb875aeSCathy Zhou {
44261cb875aeSCathy Zhou 	vrrp_log(VRRP_DBG1, "vrrpd_state_trans(%s): %s --> %s",
44271cb875aeSCathy Zhou 	    vr->vvr_conf.vvc_name, vrrp_state2str(prev_s), vrrp_state2str(s));
44281cb875aeSCathy Zhou 
44291cb875aeSCathy Zhou 	assert(vr->vvr_state == prev_s);
44301cb875aeSCathy Zhou 	vr->vvr_state = s;
44311cb875aeSCathy Zhou 	vr->vvr_prev_state = prev_s;
44321cb875aeSCathy Zhou 	(void) gettimeofday(&vr->vvr_st_time, NULL);
44331cb875aeSCathy Zhou 	(void) vrrpd_post_event(vr->vvr_conf.vvc_name, prev_s, s);
44341cb875aeSCathy Zhou }
44351cb875aeSCathy Zhou 
44361cb875aeSCathy Zhou static int
vrrpd_post_event(const char * name,vrrp_state_t prev_st,vrrp_state_t st)44371cb875aeSCathy Zhou vrrpd_post_event(const char *name, vrrp_state_t prev_st, vrrp_state_t st)
44381cb875aeSCathy Zhou {
44391cb875aeSCathy Zhou 	sysevent_id_t	eid;
44401cb875aeSCathy Zhou 	nvlist_t	*nvl = NULL;
44411cb875aeSCathy Zhou 
44421cb875aeSCathy Zhou 	/*
44431cb875aeSCathy Zhou 	 * sysevent is not supported in the non-global zone
44441cb875aeSCathy Zhou 	 */
44451cb875aeSCathy Zhou 	if (getzoneid() != GLOBAL_ZONEID)
44461cb875aeSCathy Zhou 		return (0);
44471cb875aeSCathy Zhou 
44481cb875aeSCathy Zhou 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
44491cb875aeSCathy Zhou 		goto failed;
44501cb875aeSCathy Zhou 
44511cb875aeSCathy Zhou 	if (nvlist_add_uint8(nvl, VRRP_EVENT_VERSION,
44521cb875aeSCathy Zhou 	    VRRP_EVENT_CUR_VERSION) != 0)
44531cb875aeSCathy Zhou 		goto failed;
44541cb875aeSCathy Zhou 
44551cb875aeSCathy Zhou 	if (nvlist_add_string(nvl, VRRP_EVENT_ROUTER_NAME, name) != 0)
44561cb875aeSCathy Zhou 		goto failed;
44571cb875aeSCathy Zhou 
44581cb875aeSCathy Zhou 	if (nvlist_add_uint8(nvl, VRRP_EVENT_STATE, st) != 0)
44591cb875aeSCathy Zhou 		goto failed;
44601cb875aeSCathy Zhou 
44611cb875aeSCathy Zhou 	if (nvlist_add_uint8(nvl, VRRP_EVENT_PREV_STATE, prev_st) != 0)
44621cb875aeSCathy Zhou 		goto failed;
44631cb875aeSCathy Zhou 
44641cb875aeSCathy Zhou 	if (sysevent_post_event(EC_VRRP, ESC_VRRP_STATE_CHANGE,
44651cb875aeSCathy Zhou 	    SUNW_VENDOR, VRRP_EVENT_PUBLISHER, nvl, &eid) == 0) {
44661cb875aeSCathy Zhou 		nvlist_free(nvl);
44671cb875aeSCathy Zhou 		return (0);
44681cb875aeSCathy Zhou 	}
44691cb875aeSCathy Zhou 
44701cb875aeSCathy Zhou failed:
44711cb875aeSCathy Zhou 	vrrp_log(VRRP_ERR, "vrrpd_post_event(): `state change (%s --> %s)' "
44721cb875aeSCathy Zhou 	    "sysevent posting failed: %s", vrrp_state2str(prev_st),
44731cb875aeSCathy Zhou 	    vrrp_state2str(st), strerror(errno));
44741cb875aeSCathy Zhou 
44751cb875aeSCathy Zhou 	nvlist_free(nvl);
44761cb875aeSCathy Zhou 	return (-1);
44771cb875aeSCathy Zhou }
44781cb875aeSCathy Zhou 
44791cb875aeSCathy Zhou /*
44801cb875aeSCathy Zhou  * timeval processing functions
44811cb875aeSCathy Zhou  */
44821cb875aeSCathy Zhou static int
timeval_to_milli(struct timeval tv)44831cb875aeSCathy Zhou timeval_to_milli(struct timeval tv)
44841cb875aeSCathy Zhou {
44851cb875aeSCathy Zhou 	return ((int)(tv.tv_sec * 1000 + tv.tv_usec / 1000 + 0.5));
44861cb875aeSCathy Zhou }
44871cb875aeSCathy Zhou 
44881cb875aeSCathy Zhou static struct timeval
timeval_delta(struct timeval t1,struct timeval t2)44891cb875aeSCathy Zhou timeval_delta(struct timeval t1, struct timeval t2)
44901cb875aeSCathy Zhou {
44911cb875aeSCathy Zhou 	struct timeval t;
44921cb875aeSCathy Zhou 	t.tv_sec = t1.tv_sec - t2.tv_sec;
44931cb875aeSCathy Zhou 	t.tv_usec = t1.tv_usec - t2.tv_usec;
44941cb875aeSCathy Zhou 
44951cb875aeSCathy Zhou 	if (t.tv_usec < 0) {
44961cb875aeSCathy Zhou 		t.tv_usec += 1000000;
44971cb875aeSCathy Zhou 		t.tv_sec--;
44981cb875aeSCathy Zhou 	}
44991cb875aeSCathy Zhou 	return (t);
45001cb875aeSCathy Zhou }
45011cb875aeSCathy Zhou 
45021cb875aeSCathy Zhou /*
45031cb875aeSCathy Zhou  * print error messages to the terminal or to syslog
45041cb875aeSCathy Zhou  */
45051cb875aeSCathy Zhou static void
vrrp_log(int level,char * message,...)45061cb875aeSCathy Zhou vrrp_log(int level, char *message, ...)
45071cb875aeSCathy Zhou {
45081cb875aeSCathy Zhou 	va_list ap;
45091cb875aeSCathy Zhou 	int log_level = -1;
45101cb875aeSCathy Zhou 
45111cb875aeSCathy Zhou 	va_start(ap, message);
45121cb875aeSCathy Zhou 
45131cb875aeSCathy Zhou 	if (vrrp_logflag == 0) {
45141cb875aeSCathy Zhou 		if (level <= vrrp_debug_level) {
45151cb875aeSCathy Zhou 			/*
45161cb875aeSCathy Zhou 			 * VRRP_ERR goes to stderr, others go to stdout
45171cb875aeSCathy Zhou 			 */
45181cb875aeSCathy Zhou 			FILE *out = (level <= VRRP_ERR) ? stderr : stdout;
4519c5e0ece0SCathy Zhou 			(void) fprintf(out, "vrrpd: ");
45201cb875aeSCathy Zhou 			/* LINTED: E_SEC_PRINTF_VAR_FMT */
45211cb875aeSCathy Zhou 			(void) vfprintf(out, message, ap);
45221cb875aeSCathy Zhou 			(void) fprintf(out, "\n");
45231cb875aeSCathy Zhou 			(void) fflush(out);
45241cb875aeSCathy Zhou 		}
45251cb875aeSCathy Zhou 		va_end(ap);
45261cb875aeSCathy Zhou 		return;
45271cb875aeSCathy Zhou 	}
45281cb875aeSCathy Zhou 
45291cb875aeSCathy Zhou 	/*
45301cb875aeSCathy Zhou 	 * translate VRRP_* to LOG_*
45311cb875aeSCathy Zhou 	 */
45321cb875aeSCathy Zhou 	switch (level) {
45331cb875aeSCathy Zhou 	case VRRP_ERR:
45341cb875aeSCathy Zhou 		log_level = LOG_ERR;
45351cb875aeSCathy Zhou 		break;
45361cb875aeSCathy Zhou 	case VRRP_WARNING:
45371cb875aeSCathy Zhou 		log_level = LOG_WARNING;
45381cb875aeSCathy Zhou 		break;
45391cb875aeSCathy Zhou 	case VRRP_NOTICE:
45401cb875aeSCathy Zhou 		log_level = LOG_NOTICE;
45411cb875aeSCathy Zhou 		break;
45421cb875aeSCathy Zhou 	case VRRP_DBG0:
45431cb875aeSCathy Zhou 		log_level = LOG_INFO;
45441cb875aeSCathy Zhou 		break;
45451cb875aeSCathy Zhou 	default:
45461cb875aeSCathy Zhou 		log_level = LOG_DEBUG;
45471cb875aeSCathy Zhou 		break;
45481cb875aeSCathy Zhou 	}
45491cb875aeSCathy Zhou 
45501cb875aeSCathy Zhou 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
45511cb875aeSCathy Zhou 	(void) vsyslog(log_level, message, ap);
45521cb875aeSCathy Zhou 	va_end(ap);
45531cb875aeSCathy Zhou }
4554