vrrpd.c revision f6da83d4178694e7113b71d1e452f15b296f73d8
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <auth_attr.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <zone.h>
#include <libsysevent.h>
#include <limits.h>
#include <locale.h>
#include <signal.h>
#include <assert.h>
#include <ucred.h>
#include <bsm/adt_event.h>
#include <priv_utils.h>
#include <libdllink.h>
#include <libdlvnic.h>
#include <libipadm.h>
#include <pwd.h>
#include <libvrrpadm.h>
#include "vrrpd_impl.h"
/*
* A VRRP router can be only start participating the VRRP protocol of a virtual
* router when all the following conditions are met:
*
* - The VRRP router is enabled (vr->vvr_conf.vvc_enabled is _B_TRUE)
* - The RX socket is successfully created over the physical interface to
* receive the VRRP multicast advertisement. Note that one RX socket can
* be shared by several VRRP routers configured over the same physical
* interface. (See vrrpd_init_rxsock())
* - The TX socket is successfully created over the VNIC interface to send
* the VRRP advertisment. (See vrrpd_init_txsock())
* - The primary IP address has been successfully selected over the physical
* interface. (See vrrpd_select_primary())
*
* If a VRRP router is enabled but the other conditions haven't be satisfied,
* the router will be stay at the VRRP_STATE_INIT state. If all the above
* conditions are met, the VRRP router will be transit to either
* the VRRP_STATE_MASTER or the VRRP_STATE_BACKUP state, depends on the VRRP
* protocol.
*/
#define skip_whitespace(p) while (isspace(*(p))) ++(p)
#define BUFFSIZE 65536
typedef struct vrrpd_rtsock_s {
int vrt_af; /* address family */
int vrt_fd; /* socket for the PF_ROUTE msg */
static int vrrp_logflag = 0;
/* between vrrpd/libvrrpadm */
/* the virtual IP addresses */
static int vrrpd_ctlsock6_fd = -1;
};
static struct vrrp_vr_list_s vrrp_vr_list;
static struct vrrp_intf_list_s vrrp_intf_list;
static char vrrpd_conffile[MAXPATHLEN];
/*
* Multicast address of VRRP advertisement in network byte order
*/
static vrrp_addr_t vrrp_muladdr4;
static vrrp_addr_t vrrp_muladdr6;
static int pfds[2];
/*
* macros to calculate skew_time and master_down_timer
*
* Note that the input is in centisecs and output are in msecs
*/
#define SKEW_TIME_VR(vr) \
#define MASTER_DOWN_INTERVAL_VR(vr) \
#define VRRP_CONF_UPDATE 0x01
#define VRRP_CONF_DELETE 0x02
static char *af_str(int);
static iu_tq_callback_t vrrp_adv_timeout;
static iu_tq_callback_t vrrp_b2m_timeout;
static iu_eh_callback_t vrrpd_sock_handler;
static iu_eh_callback_t vrrpd_rtsock_handler;
static int daemon_init();
static vrrp_err_t vrrpd_init();
static void vrrpd_fini();
static vrrp_err_t vrrpd_cmdsock_create();
static void vrrpd_cmdsock_destroy();
static vrrp_err_t vrrpd_rtsock_create();
static void vrrpd_rtsock_destroy();
static vrrp_err_t vrrpd_ctlsock_create();
static void vrrpd_ctlsock_destroy();
static void vrrpd_scan_timer(iu_tq_t *, void *);
static void vrrpd_scan(int);
static void vrrpd_fini_rxsock(vrrp_vr_t *);
static void vrrpd_fini_txsock(vrrp_vr_t *);
static void vrrpd_delete_vr(vrrp_vr_t *);
static vrrp_err_t vrrpd_delete(const char *);
static vrrp_err_t vrrpd_disable(const char *);
static void vrrpd_cmd_create(void *, void *, size_t *);
static void vrrpd_cmd_delete(void *, void *, size_t *);
static void vrrpd_cmd_enable(void *, void *, size_t *);
static void vrrpd_cmd_disable(void *, void *, size_t *);
static void vrrpd_cmd_modify(void *, void *, size_t *);
static void vrrpd_cmd_list(void *, void *, size_t *);
static void vrrpd_cmd_query(void *, void *, size_t *);
static vrrp_vr_t *vrrpd_lookup_vr_by_name(const char *);
static vrrp_intf_t *vrrpd_lookup_if(const char *, int);
static void vrrpd_init_ipcache(int);
static void vrrpd_update_ipcache(int);
static ipadm_status_t vrrpd_walk_addr_info(int);
int, uint64_t);
static void vrrpd_reselect_primary(vrrp_intf_t *);
static void vrrpd_reenable_all_vr();
uint16_t, vrrp_pkt_t *);
uint16_t, vrrp_pkt_t *);
/* state transition functions */
static void vrrpd_state_m2i(vrrp_vr_t *);
static void vrrpd_state_b2i(vrrp_vr_t *);
static void vrrpd_initconf();
static void vrrpd_cleanup();
static void vrrp_log(int, char *, ...);
static int timeval_to_milli(struct timeval);
typedef struct vrrpd_prop_s {
char *vs_propname;
} vrrp_prop_t;
/*
* persistent VRRP properties array
*/
static vrrp_prop_t vrrp_prop_info_tbl[] = {
};
#define VRRP_PROP_INFO_TABSIZE \
(sizeof (vrrp_prop_info_tbl) / sizeof (vrrp_prop_t))
typedef void vrrp_cmd_func_t(void *, void *, size_t *);
typedef struct vrrp_cmd_info_s {
static vrrp_cmd_info_t vrrp_cmd_info_tbl[] = {
{VRRP_CMD_CREATE, sizeof (vrrp_cmd_create_t),
{VRRP_CMD_DELETE, sizeof (vrrp_cmd_delete_t),
{VRRP_CMD_ENABLE, sizeof (vrrp_cmd_enable_t),
{VRRP_CMD_DISABLE, sizeof (vrrp_cmd_disable_t),
{VRRP_CMD_MODIFY, sizeof (vrrp_cmd_modify_t),
{VRRP_CMD_QUERY, sizeof (vrrp_cmd_query_t), 0,
{VRRP_CMD_LIST, sizeof (vrrp_cmd_list_t), 0,
};
#define VRRP_DOOR_INFO_TABLE_SIZE \
(sizeof (vrrp_cmd_info_tbl) / sizeof (vrrp_cmd_info_t))
static int
{
} else {
}
}
static vrrp_vr_t *
{
break;
}
}
return (vr);
}
static vrrp_vr_t *
vrrpd_lookup_vr_by_name(const char *name)
{
break;
}
return (vr);
}
static vrrp_intf_t *
{
break;
}
}
return (intf);
}
static vrrp_err_t
vrrp_intf_t **intfp)
{
return (VRRP_ENOMEM);
}
return (VRRP_SUCCESS);
}
/*
* An interface is deleted. If update_vr is true, the deletion of the interface
* may cause the state transition of assoicated VRRP router (if this interface
* is either the primary or the VNIC interface of the VRRP router); otherwise,
* simply delete the interface without updating the VRRP router.
*/
static void
{
if (update_vr) {
/*
* If a this interface is the physical interface or the VNIC
* of a VRRP router, the deletion of the interface (no IP
* address exists on this interface) may cause the state
* transition of the VRRP router. call vrrpd_remove_if()
* to find all corresponding VRRP router and update their
* states.
*/
}
/*
* First remove and delete all the IP addresses on the interface
*/
}
/*
* Then remove and delete the interface
*/
}
static vrrp_err_t
{
char abuf[INET6_ADDRSTRLEN];
/* LINTED E_CONSTANT_CONDITION */
return (VRRP_ENOMEM);
}
/*
* Make sure link-local IPv6 IP addresses are at the head of the list
*/
} else {
}
return (VRRP_SUCCESS);
}
static void
{
char abuf[INET6_ADDRSTRLEN];
/* LINTED E_CONSTANT_CONDITION */
}
static char *
{
switch (event) {
case RTM_NEWADDR:
return ("RTM_NEWADDR");
case RTM_DELADDR:
return ("RTM_DELADDR");
case RTM_IFINFO:
return ("RTM_IFINFO");
case RTM_ADD:
return ("RTM_ADD");
case RTM_DELETE:
return ("RTM_DELETE");
case RTM_CHANGE:
return ("RTM_CHANGE");
case RTM_OLDADD:
return ("RTM_OLDADD");
case RTM_OLDDEL:
return ("RTM_OLDDEL");
case RTM_CHGADDR:
return ("RTM_CHGADDR");
case RTM_FREEADDR:
return ("RTM_FREEADDR");
default:
return ("RTM_OTHER");
}
}
/*
* This is called by the child process to inform the parent process to
* exit with the given return value. Note that the child process
* (the daemon process) informs the parent process to exit when anything
* goes wrong or when all the intialization is done.
*/
static int
{
int err = 0;
/*
* If vrrp_debug_level is none-zero, vrrpd is not running as
* a daemon. Return directly.
*/
if (vrrp_debug_level != 0)
return (0);
return (err);
}
return (0);
}
int
{
int c, err;
(void) textdomain(TEXT_DOMAIN);
/*
* We need PRIV_SYS_CONFIG to post VRRP sysevent, PRIV_NET_RAWACESS
* and PRIV_NET_ICMPACCESS to open the raw socket, PRIV_SYS_IP_CONFIG
* setrlimit().
*
* Note that sysevent is not supported in non-global zones.
*/
if (getzoneid() == GLOBAL_ZONEID) {
} else {
}
if (err == -1) {
return (EXIT_FAILURE);
}
/*
* If vrrpd is started by other process, it will inherit the
* signal block mask. We unblock all signals to make sure the
* signal handling will work normally.
*/
(void) sigfillset(&mask);
vrrp_debug_level = 0;
switch (c) {
case 'd':
break;
case 'f':
sizeof (vrrpd_conffile));
break;
default:
break;
}
}
closefrom(3);
if (vrrp_debug_level == 0 && (daemon_init() != 0)) {
return (EXIT_FAILURE);
}
goto child_out;
}
if (vrrpd_init() != VRRP_SUCCESS) {
goto child_out;
}
/*
* Get rid of unneeded privileges.
*/
/*
* Read the configuration and initialize the existing VRRP
* configuration
*/
/*
* Inform the parent process that it can successfully exit.
*/
return (EXIT_FAILURE);
}
/*
* Start the loop to handle the timer and the IO events.
*/
case -1:
"abnormally");
break;
default:
break;
}
return (EXIT_SUCCESS);
(void) vrrpd_inform_parent_exit(EXIT_FAILURE);
return (EXIT_FAILURE);
}
static int
{
int rv;
"service and should not be run from the command line.");
return (-1);
}
/*
* Create the pipe used for the child process to inform the parent
* process to exit after all initialization is done.
*/
return (-1);
}
return (-1);
}
if (pid != 0) { /* Parent */
/*
* Read the child process's return value from the pfds.
* If the child process exits unexpectedly, read() returns -1.
*/
rv = EXIT_FAILURE;
}
}
/*
* in child process, became a daemon, and return to main() to continue.
*/
(void) chdir("/");
(void) setsid();
(void) close(0);
(void) close(1);
(void) close(2);
(void) dup2(0, 1);
(void) dup2(0, 2);
vrrp_logflag = 1;
return (0);
}
static vrrp_err_t
{
goto fail;
}
goto fail;
}
goto fail;
}
/*
* Create the AF_UNIX socket used to communicate with libvrrpadm.
*
* This socket is used to receive the administrative requests and
* send back the results.
*/
if (vrrpd_cmdsock_create() != VRRP_SUCCESS) {
"failed");
goto fail;
}
/*
* IP addresses. It is also used to set the IFF_NOACCEPT flag of
* the virtual IP addresses.
*/
if (vrrpd_ctlsock_create() != VRRP_SUCCESS) {
"failed");
goto fail;
}
/*
* Create the PF_ROUTER socket used to listen to the routing socket
*/
if (vrrpd_rtsock_create() != VRRP_SUCCESS) {
"failed");
goto fail;
}
/* Open the libipadm handle */
goto fail;
}
/*
* Build the list of interfaces and IP addresses. Also, start the time
* to scan the interfaces/IP addresses periodically.
*/
goto fail;
}
/*
* Initialize the VRRP multicast address.
*/
return (VRRP_SUCCESS);
fail:
vrrpd_fini();
return (err);
}
static void
{
vrrp_scan_timer_id = -1;
}
if (vrrpd_timerq != NULL) {
vrrpd_timerq = NULL;
}
}
static void
vrrpd_cleanup(void)
{
while (!TAILQ_EMPTY(&vrrp_vr_list)) {
}
while (!TAILQ_EMPTY(&vrrp_intf_list)) {
}
vrrpd_fini();
closelog();
exit(1);
}
/*
* Read the configuration file and initialize all the existing VRRP routers.
*/
static void
{
int linenum = 0;
return;
}
linenum++;
continue;
}
/*
* Blank or comment line
*/
continue;
/*
* No need to update the configuration since the VRRP router
*/
continue;
}
if (conf.vvc_enabled &&
VRRP_SUCCESS)) {
}
}
}
/*
* Create the AF_UNIX socket used to communicate with libvrrpadm.
*
* This socket is used to receive the administrative request and
* send back the results.
*/
static vrrp_err_t
{
struct sockaddr_un laddr;
return (VRRP_ESYS);
}
/*
* Set it to be non-blocking.
*/
/*
* Unlink first in case a previous daemon instance exited ungracefully.
*/
(void) unlink(VRRPD_SOCKET);
return (VRRP_ESYS);
}
return (VRRP_ESYS);
}
" failed");
return (VRRP_ESYS);
}
return (VRRP_SUCCESS);
}
static void
{
(void) close(vrrpd_cmdsock_fd);
vrrpd_cmdsock_fd = -1;
vrrpd_cmdsock_eid = -1;
}
/*
* Create the PF_ROUTER sockets used to listen to the routing socket
* each address family (IPv4 and IPv6).
*/
static vrrp_err_t
{
for (i = 0; i < 2; i++) {
if (sock == -1) {
break;
}
/*
* Set it to be non-blocking.
*/
break;
}
break;
}
"rtsock %d(%s) failed", sock,
break;
}
}
if (i != 2) {
return (VRRP_ESYS);
}
return (VRRP_SUCCESS);
}
static void
{
int i;
for (i = 0; i < 2; i++) {
NULL);
}
}
/*
* IP addresses. It is also used to set the IFF_NOACCEPT flag of
* the virtual IP addresses.
*/
static vrrp_err_t
{
int s, s6;
return (VRRP_ESYS);
}
(void) close(s);
return (VRRP_ESYS);
}
(void) close(s);
return (VRRP_ESYS);
}
(void) close(s);
return (VRRP_ESYS);
}
vrrpd_ctlsock_fd = s;
return (VRRP_SUCCESS);
}
static void
{
(void) close(vrrpd_ctlsock_fd);
vrrpd_ctlsock_fd = -1;
(void) close(vrrpd_ctlsock6_fd);
vrrpd_ctlsock6_fd = -1;
}
/*ARGSUSED*/
static void
{
/*
* No need to update the configuration since it is already
* done in the above vrrpd_create() call
*/
if (err != VRRP_SUCCESS)
}
}
/*ARGSUSED*/
static void
{
}
/*ARGSUSED*/
static void
{
}
/*ARGSUSED*/
static void
{
}
/*ARGSUSED*/
static void
{
}
static void
{
}
static void
{
}
/*
* Write-type requeset must have the solaris.network.vrrp authorization.
*/
static boolean_t
{
return (_B_TRUE);
/*
* Validate the credential
*/
return (_B_FALSE);
}
goto done;
}
goto done;
}
done:
return (success);
}
/*
* Process the administrative request from libvrrpadm
*/
/* ARGSUSED */
static void
void *arg)
{
int connfd, i;
struct sockaddr_in from;
return;
}
/*
* First get the type of the request
*/
cursize = 0;
continue;
} else if (len > 0) {
continue;
}
"length");
return;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
for (i = 0; i < VRRP_DOOR_INFO_TABLE_SIZE; i++) {
cinfo = vrrp_cmd_info_tbl + i;
break;
}
}
"type %d", cmd);
err = VRRP_EINVAL;
goto done;
}
/*
* Get the rest of the request.
*/
continue;
} else if (len > 0) {
continue;
}
"length");
err = VRRP_EINVAL;
goto done;
}
/*
* Validate the authorization
*/
"not sufficient authorization");
err = VRRP_EPERM;
}
done:
/*
* Ack the request
*/
if (err != 0) {
/* LINTED E_BAD_PTR_CAST_ALIGN */
acksize = sizeof (vrrp_ret_t);
} else {
/*
* If the size of ack is varied, the cmdfunc callback
* will set the right size.
*/
/* LINTED E_BAD_PTR_CAST_ALIGN */
}
/*
* Send the ack back.
*/
cursize = 0;
continue;
} else if (len > 0) {
continue;
} else {
break;
}
}
}
/*
* Process the routing socket messages and update the interfaces/IP addresses
* list
*/
/* ARGSUSED */
static void
{
struct ifa_msghdr *ifam;
int nbytes;
for (;;) {
if (nbytes <= 0) {
/* No more messages */
break;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
break;
}
case RTM_FREEADDR:
case RTM_CHGADDR:
case RTM_NEWADDR:
case RTM_DELADDR:
/*
* address list.
*/
break;
default:
/* Not interesting */
break;
}
}
if (scanif)
vrrpd_scan(af);
}
/*
*/
/* ARGSUSED */
static void
{
}
/*
* family.
*/
static void
vrrpd_scan(int af)
{
/* If interface index changes, walk again. */
goto again;
}
/*
* First mark all IP addresses of the specific address family to be removed.
* This flag will then be cleared when we walk up all the IP addresses.
*/
static void
vrrpd_init_ipcache(int af)
{
char abuf[INET6_ADDRSTRLEN];
continue;
/*
* If the interface is still marked as new, it means that this
* vrrpd_init_ipcache() call is a result of ifindex change,
* which causes the re-walk of all the interfaces (see
* vrrpd_add_ipaddr()), and some interfaces are still marked
* as new during the last walk. In this case, delete this
* interface with the "update_vr" argument to be _B_FALSE,
* since no VRRP router has been assoicated with this
* interface yet (the association is done in
* vrrpd_update_ipcache()).
*
* This interface will be re-added later if it still exists.
*/
intf->vvi_ifindex);
continue;
}
/* LINTED E_CONSTANT_CONDITION */
continue;
}
/*
* If the IP is still marked as new, it means that
* this vrrpd_init_ipcache() call is a result of
* ifindex change, which causes the re-walk of all
* the IP addresses (see vrrpd_add_ipaddr()).
* Delete this IP.
*
* This IP will be readded later if it still exists.
*/
}
}
}
/*
* Walk all the IP addresses of the given family and update its
* addresses list. Return IPADM_FAILURE if it is required to walk
* all the interfaces again (one of the interface index changes in between).
*/
static ipadm_status_t
vrrpd_walk_addr_info(int af)
{
char *lifname;
int ifindex;
if (ipstatus != IPADM_SUCCESS) {
"ipadm_addr_info() failed: %s",
return (IPADM_SUCCESS);
}
continue;
continue;
}
/* Filter out the all-zero IP address */
continue;
"if_nametoindex() failed for %s: %s",
}
break;
}
/*
* to walk the IP addresses one more time.
*/
== VRRP_EAGAIN) {
break;
}
}
return (ipstatus);
}
/*
* Given the information of each IP address, update the interface and
* IP addresses list
*/
static vrrp_err_t
{
char abuf[INET6_ADDRSTRLEN];
/* LINTED E_CONSTANT_CONDITION */
/*
* Get the physical interface name from the logical interface name.
*/
*c = '\0';
if (err != VRRP_SUCCESS)
return (err);
/*
* If index changes, it means that this interface is
* interface is not used by any VRRP router, just
* update its ifindex, and the IP addresses list will
* be updated later. Otherwise, return EAGAIN to rewalk
* all the IP addresses from the beginning.
*/
} else {
/*
* delete this interface from the list if this
* interface has already been assoicated with
* any VRRP routers.
*/
return (VRRP_EAGAIN);
}
}
/*
* Does this IP address already exist?
*/
break;
}
/*
* Address has been changed, mark it as new
* If this address is already selected as the
* primary IP address, the new IP will be checked
* to see whether it is still qualified as the
* primary IP address. If not, the primary IP
* address will be reselected.
*/
sizeof (vrrp_addr_t));
}
} else {
if (err != VRRP_SUCCESS)
return (err);
}
return (VRRP_SUCCESS);
}
/*
* Update the interface and IP addresses list. Remove the ones that have been
* staled since last time we walk the IP addresses and updated the ones that
* have been changed.
*/
static void
vrrpd_update_ipcache(int af)
{
char abuf[INET6_ADDRSTRLEN];
continue;
/*
* Does the interface already select its primary IP address?
*/
/*
* Removed the IP addresses that have been unconfigured.
*/
continue;
/* LINTED E_CONSTANT_CONDITION */
_B_FALSE);
}
/*
* No IP addresses left, delete this interface.
*/
continue;
}
/*
* If this is selected ss the physical interface for any
* VRRP router, reselect the primary address if needed.
*/
if (IS_PRIMARY_INTF(intf)) {
/*
* Cannot find the new primary IP address.
*/
if (primary_selected && !primary_now_selected) {
"reselect primary IP on %s failed",
intf->vvi_ifname);
} else if (!primary_selected && primary_now_selected) {
/*
* The primary IP address is successfully
* selected on the physical interfacew we
* need to walk through all the VRRP routers
* that is created on this physical interface
* and see whether they can now be enabled.
*/
}
}
/*
* on the state of VRRP router.
*
* Note that it is fine to not update the IP's vip_flags field
* even if vrrpd_virtualip_updateone() changed the address's
* select primary IP address over a physical interface, and
* vrrpd_virtualip_updateone() only affects the virtual IP
* address's status.
*/
/* LINTED E_CONSTANT_CONDITION */
_B_FALSE);
if (IS_VIRTUAL_INTF(intf)) {
/*
* based on the virtual interface's state
* (which is determined by the VRRP router's
* state). Otherwise, check only and prompt
* changed.
*/
VRRP_SUCCESS) {
"vrrpd_update_ipcache(): "
"IP %s over %s update failed", abuf,
intf->vvi_ifname);
continue;
}
}
}
/*
* The IP address is deleted when it is failed to be brought
* up. If no IP addresses are left, delete this interface.
*/
continue;
}
/*
* A new interface is found. This interface can be
* the primary interface or the virtual VNIC
* interface. Again, we need to walk throught all
* the VRRP routers to see whether some of them can
* now be enabled because of the new primary IP
* address or the new virtual IP addresses.
*/
}
}
if (need_reenable)
}
/*
* Reselect primary IP if:
* - The existing primary IP is no longer qualified (removed or it is down or
* not a link-local IP for IPv6 VRRP router);
* - This is a physical interface but no primary IP is chosen;
*/
static void
{
char abuf[INET6_ADDRSTRLEN];
/*
* If the interface's old primary IP address is still valid, return
*/
return;
/* LINTED E_CONSTANT_CONDITION */
}
/* LINTED E_CONSTANT_CONDITION */
}
}
/*
* Select the primary IP address. Since the link-local IP address is always
* at the head of the IP address list, try to find the first UP IP address
* and see whether it qualify.
*/
static vrrp_ip_t *
{
char abuf[INET6_ADDRSTRLEN];
/* LINTED E_CONSTANT_CONDITION */
break;
}
/*
* Is this valid primary IP address?
*/
return (NULL);
}
return (pip);
}
/*
* This is a new interface. Check whether any VRRP router is waiting for it
*/
static void
{
(void) vrrpd_enable_vr(vr);
}
}
/*
* If primary_addr_gone is _B_TRUE, it means that we failed to select
* the primary IP address on this (physical) interface; otherwise,
* it means the interface is no longer available.
*/
static void
{
}
}
/*
* Update the VRRP configuration file based on the given configuration.
* op is either VRRP_CONF_UPDATE or VRRP_CONF_DELETE
*/
static vrrp_err_t
{
int nfd;
char newfile[MAXPATHLEN];
return (VRRP_EDB);
}
return (VRRP_EDB);
}
goto done;
}
VRRP_SUCCESS) {
"configuration format: %s", line);
goto done;
}
/*
* Write this line out if:
* - this is a comment line; or
* - if the name of the VR read from this line does not match
*/
continue;
"write line %s", line);
goto done;
}
/*
*/
if (op == VRRP_CONF_DELETE)
continue;
newconf)) != VRRP_SUCCESS) {
goto done;
}
"write line %s", line);
goto done;
}
}
/*
* If we get to the end of the file and have not seen the router that
* we are about to update, write it out.
*/
"write line %s", line);
}
}
if (err != VRRP_SUCCESS)
goto done;
"rename file %s", newfile);
}
done:
return (err);
}
static vrrp_err_t
{
int n, i;
for (i = 0; i < VRRP_PROP_INFO_TABSIZE; i++) {
prop = &vrrp_prop_info_tbl[i];
prop->vs_propname);
if (n < 0 || n >= len)
break;
len -= n;
line += n;
if (n < 0 || n >= len)
break;
len -= n;
line += n;
}
if (i != VRRP_PROP_INFO_TABSIZE) {
return (VRRP_EDB);
}
if (n < 0 || n >= len) {
return (VRRP_EDB);
}
return (VRRP_SUCCESS);
}
static vrrp_err_t
{
char *next;
char tmpbuf[MAXLINELEN];
/*
* Skip leading spaces, blank lines, and comments.
*/
return (VRRP_SUCCESS);
}
/*
* Read each VR properties.
*/
break;
}
/* All properties read but no VRID defined */
err = VRRP_EINVAL;
return (err);
}
static vrrp_err_t
{
char *pstr;
int i;
return (VRRP_EINVAL);
}
*pstr++ = '\0';
for (i = 0; i < VRRP_PROP_INFO_TABSIZE; i++) {
prop = &vrrp_prop_info_tbl[i];
break;
}
}
if (i == VRRP_PROP_INFO_TABSIZE) {
return (VRRP_EINVAL);
}
return (VRRP_SUCCESS);
}
static boolean_t
{
}
static boolean_t
{
}
static boolean_t
{
else
return (_B_FALSE);
return (_B_TRUE);
}
static boolean_t
{
}
static boolean_t
{
}
static boolean_t
{
else
return (_B_FALSE);
return (_B_TRUE);
}
static boolean_t
{
else
return (_B_FALSE);
return (_B_TRUE);
}
static boolean_t
{
else
return (_B_FALSE);
return (_B_TRUE);
}
static boolean_t
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static int
{
}
static char *
{
return ("AF_INET");
return ("AF_INET6");
return ("AF_UNSPEC");
else
return ("AF_error");
}
static vrrp_err_t
{
return (VRRP_ENOMEM);
}
return (VRRP_SUCCESS);
}
static void
{
}
static vrrp_err_t
{
/*
* This VRRP router has been successfully enabled and start
* participating.
*/
return (VRRP_SUCCESS);
/*
* Select the primary IP address. Even if this time
* primary IP selection failed, we will reselect the
* primary IP address when new IP address comes up.
*/
"select_primary over %s failed",
}
}
/*
* Initialize the TX socket used for this vrrp_vr_t to send the
* multicast packets.
*/
/*
* Only start the state transition if sockets for both RX and TX are
* initialized correctly.
*/
/*
* Record the error information for diagnose purpose.
*/
return (err);
}
else
if (err != VRRP_SUCCESS) {
}
return (err);
}
/*
* Given the removed interface, see whether the given VRRP router would
* be affected and stop participating the VRRP protocol.
*
* If intf is NULL, VR disabling request is coming from the admin.
*/
static void
{
"interface deleted"));
/*
* An interface is deleted, see whether this interface is the
* physical interface or the VNIC of the given VRRP router.
* If so, continue to disable the VRRP router.
*/
return;
}
/*
* If this is the case that the primary IP address is gone,
* and we failed to reselect another primary IP address,
* continue to disable the VRRP router.
*/
return;
/*
* If this router is disabled by the administrator, send
* the zero-priority advertisement to indicate the Master
* stops participating VRRP.
*/
}
/*
* If no primary IP address can be selected, the VRRP router
* stays at the INIT state and will become BACKUP and MASTER when
* a primary IP address is reselected.
*/
if (primary_addr_gone) {
/*
* The VRRP router is disable by the administrator
*/
}
}
{
/*
* Sanity check
*/
return (VRRP_EINVAL);
}
return (VRRP_EINVALVRNAME);
}
return (VRRP_EINSTEXIST);
}
return (VRRP_EVREXIST);
}
VRRP_CONF_UPDATE)) != VRRP_SUCCESS) {
return (err);
}
return (err);
}
static vrrp_err_t
vrrpd_delete(const char *vn)
{
return (VRRP_ENOTFOUND);
}
if (err != VRRP_SUCCESS) {
return (err);
}
return (VRRP_SUCCESS);
}
static vrrp_err_t
{
return (VRRP_ENOTFOUND);
}
/*
* The VR is already enabled.
*/
if (conf->vvc_enabled) {
"enabled", vn);
return (VRRP_EALREADY);
}
/*
* Check whether the link exists.
*/
return (VRRP_EINVALLINK);
}
/*
* address famitly.
*/
if (err != VRRP_SUCCESS) {
err = VRRP_ENOVNIC;
goto fail;
}
/*
* Find the right VNIC, primary interface and get the list of the
* protected IP adressses and primary IP address. Note that if
* either interface is NULL (no IP addresses configured over the
* interface), we will still continue and mark this VRRP router
* as "enabled".
*/
VRRP_CONF_UPDATE)) != VRRP_SUCCESS) {
goto fail;
}
/*
* If vrrpd_setup_vr() fails, it is possible that there is no IP
* addresses over ether the primary interface or the VNIC yet,
* return success in this case, the VRRP router will stay in
* the initialized state and start to work when the IP address is
* configured.
*/
(void) vrrpd_enable_vr(vr);
return (VRRP_SUCCESS);
fail:
return (err);
}
static vrrp_err_t
vrrpd_disable(const char *vn)
{
return (VRRP_ENOTFOUND);
}
/*
* The VR is already disable.
*/
return (VRRP_EALREADY);
}
if (err != VRRP_SUCCESS) {
return (err);
}
return (VRRP_SUCCESS);
}
static vrrp_err_t
{
int pri;
if (mask == 0)
return (VRRP_SUCCESS);
return (VRRP_ENOTFOUND);
}
if (mask & VRRP_CONF_INTERVAL) {
return (VRRP_EINVAL);
}
}
if (mask & VRRP_CONF_PRIORITY) {
return (VRRP_EINVAL);
}
}
if (mask & VRRP_CONF_ACCEPT)
return (VRRP_EINVAL);
}
if (err != VRRP_SUCCESS) {
vrrp_err2str(err));
return (err);
}
}
/*
* Save the current configuration, so it can be restored if the
* following fails.
*/
if (mask & VRRP_CONF_PREEMPT)
if (mask & VRRP_CONF_ACCEPT)
if (mask & VRRP_CONF_PRIORITY)
if (mask & VRRP_CONF_INTERVAL)
if (err != VRRP_SUCCESS) {
if (set_accept)
return (err);
}
return (VRRP_SUCCESS);
}
static void
{
char *p = (char *)ret + sizeof (vrrp_ret_list_t);
continue;
continue;
}
continue;
if (size < VRRP_NAME_MAX) {
*sizep = sizeof (vrrp_ret_list_t);
return;
}
size -= VRRP_NAME_MAX;
}
}
static void
{
*sizep = sizeof (vrrp_ret_query_t);
return;
}
/*
* Get the virtual IP list if the router is not in the INIT state.
*/
vipcnt++;
}
}
*sizep = sizeof (vrrp_ret_query_t);
"%d virtual IPs", vipcnt);
*sizep = sizeof (vrrp_ret_query_t);
return;
}
return;
}
vipcnt = 0;
}
/*
* Check whether there is a peer.
*/
}
}
}
/*
* Build the VRRP packet (not including the IP header). Return the
* payload length.
*
* If zero_pri is set to be B_TRUE, then this is the specical zero-priority
* advertisement which is sent by the Master to indicate that it has been
* stopped participating in VRRP.
*/
static size_t
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* LINTED E_BAD_PTR_CAST_ALIGN */
/* LINTED E_BAD_PTR_CAST_ALIGN */
int nip = 0;
return (0);
}
else
}
if (nip == 0) {
return (0);
}
/*
* Set the checksum to 0 first, then caculate it.
*/
} else {
}
return (size);
}
/*
* We need to build the IPv4 header on our own.
*/
static vrrp_err_t
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
return (VRRP_ETOOSMALL);
}
/*
* The kernel will set the IP cksum and the IPv4 identification.
*/
(const struct sockaddr *)&vrrp_muladdr4,
sizeof (struct sockaddr_in))) != plen) {
"(vrid:%d, %s, %s) failed: %s sent:%d expect:%d",
return (VRRP_ESYS);
}
return (VRRP_SUCCESS);
}
static vrrp_err_t
{
size_t hoplimit_space = 0;
size_t pktinfo_space = 0;
struct in6_pktinfo *pktinfop;
return (VRRP_ETOOSMALL);
msg6.msg_controllen = 0;
hoplimit_space = sizeof (int);
pktinfo_space = sizeof (struct in6_pktinfo);
/*
* We need to temporarily set the msg6.msg_controllen to bufspace
* (we will later trim it to actual length used). This is needed because
* CMSG_NXTHDR() uses it to check we have not exceeded the bounds.
*/
return (VRRP_ENOMEM);
}
/* LINTED */
*(int *)cmsg_datap = VRRP_IP_TTL;
/* LINTED */
/*
* We don't know if pktinfop->ipi6_addr is aligned properly,
* therefore let's use bcopy, instead of assignment.
*/
/*
* We can assume pktinfop->ipi6_ifindex is 32 bit aligned.
*/
return (VRRP_ESYS);
}
return (VRRP_SUCCESS);
}
/*
* Send the VRRP advertisement packets.
*/
static vrrp_err_t
{
return (VRRP_EINVAL);
}
} else {
}
}
static void
{
char peer[INET6_ADDRSTRLEN];
char local[INET6_ADDRSTRLEN];
int addr_cmp;
/* LINTED E_CONSTANT_CONDITION */
_B_FALSE);
peer);
return;
}
/* LINTED E_CONSTANT_CONDITION */
if (addr_cmp == 0) {
return;
}
peer);
return;
}
(void) iu_cancel_timer(vrrpd_timerq,
/* the master stops participating in VRRP */
} else {
}
vr)) == -1) {
"start vrrp_b2m_timeout(%d) failed",
} else {
"start vrrp_b2m_timeout(%d)",
}
}
(void) iu_cancel_timer(vrrpd_timerq,
vr)) == -1) {
"start vrrp_adv_timeout(%d) failed",
} else {
"start vrrp_adv_timeout(%d)",
}
(void) vrrpd_state_m2b(vr);
}
} else {
}
}
static vrrp_err_t
{
char peer[INET6_ADDRSTRLEN];
/* LINTED E_CONSTANT_CONDITION */
peer);
if (len < sizeof (vrrp_pkt_t)) {
"length %d", len);
return (VRRP_EINVAL);
}
/*
* Verify: VRRP version number and packet type.
*/
if (vers_type != VRRP_VERSION) {
return (VRRP_EINVAL);
}
pif->vvi_ifname);
return (VRRP_EINVAL);
}
if (len - sizeof (vrrp_pkt_t) !=
sizeof (struct in6_addr))) {
return (VRRP_EINVAL);
}
/*
* verify: VRRP checksum. Note that vrrp_cksum returns network byte
* order checksum value;
*/
} else {
}
if (cksum != saved_cksum) {
cksum, saved_cksum);
return (VRRP_EINVAL);
}
} else {
}
return (VRRP_SUCCESS);
}
/*
* IPv4 socket, the IPv4 header is included.
*/
static vrrp_err_t
{
char abuf[INET6_ADDRSTRLEN];
/* Sanity check */
return (VRRP_EINVAL);
}
return (VRRP_EINVAL);
}
return (VRRP_EINVAL);
}
/*
* Note that the ip_len contains only the IP payload length.
*/
return (vrrpd_process_vrrp(pif,
/* LINTED E_BAD_PTR_CAST_ALIGN */
}
/*
* IPv6 socket, check the ancillary_data.
*/
static vrrp_err_t
{
struct in6_pktinfo *pktinfop;
char abuf[INET6_ADDRSTRLEN];
int ttl;
/* Sanity check */
if (len < sizeof (vrrp_pkt_t)) {
return (VRRP_EINVAL);
}
case IPV6_HOPLIMIT:
/* LINTED E_BAD_PTR_CAST_ALIGN */
break;
return (VRRP_EINVAL);
case IPV6_PKTINFO:
/* LINTED E_BAD_PTR_CAST_ALIGN */
break;
}
sizeof (abuf)));
return (VRRP_EINVAL);
}
}
}
/* ARGSUSED */
static void
void *arg)
{
int len;
sizeof (struct sockaddr_in6);
return;
}
/*
* Ignore packets whose control buffers that don't fit
*/
return;
}
else
}
/*
* Create the socket which is used to receive VRRP packets. Virtual routers
* that configured on the same physical interface share the same socket.
*/
static vrrp_err_t
{
struct sockaddr_storage *muladdr;
int on = 1;
/*
* The RX sockets may already been initialized.
*/
return (VRRP_SUCCESS);
}
/*
* If no IP addresses configured on the primary interface,
* return failure.
*/
return (VRRP_ENOPRIM);
}
if (pif->vvi_sockfd < 0) {
goto done;
}
/*
* Join the multicast group to receive VRRP packets.
*/
muladdr = (struct sockaddr_storage *)
(void *)&vrrp_muladdr4;
} else {
muladdr = (struct sockaddr_storage *)
(void *)&vrrp_muladdr6;
}
sizeof (struct sockaddr_storage));
sizeof (struct group_req)) < 0) {
goto done;
} else {
pif->vvi_ifindex);
}
/*
* Unlike IPv4, the IPv6 raw socket does not pass the IP header
* when a packet is received. Call setsockopt() to receive such
* information.
*/
/*
* Enable receipt of destination address info
*/
"enable recvpktinfo failed: %s",
goto done;
}
/*
* Enable receipt of hoplimit info
*/
"enable recvhoplimit failed: %s",
goto done;
}
}
"iu_register_event() failed",
goto done;
}
} else {
}
done:
if (err != VRRP_SUCCESS)
return (err);
}
/*
* Delete the socket which is used to receive VRRP packets for the given
* VRRP router. Since all virtual routers that configured on the same
* physical interface share the same socket, the socket is only closed
* when the last VRRP router share this socket is deleted.
*/
static void
{
return;
} else {
}
}
/*
* Create the socket which is used to send VRRP packets. Further, set
* the IFF_NOACCEPT flag based on the VRRP router's accept mode.
*/
static vrrp_err_t
{
int af;
return (VRRP_SUCCESS);
}
return (VRRP_ENOVIRT);
}
else
if (err != VRRP_SUCCESS)
goto done;
/*
* The interface should start with IFF_NOACCEPT flag not set, only
* call this function when the VRRP router requires IFF_NOACCEPT.
*/
done:
if (err != VRRP_SUCCESS) {
}
return (err);
}
/*
* Create the IPv4 socket which is used to send VRRP packets. Note that
* the destination MAC address of VRRP advertisement must be the virtual
* MAC address, so we specify the output interface to be the specific VNIC.
*/
static vrrp_err_t
{
int on = 1;
char off = 0;
char abuf[INET6_ADDRSTRLEN];
return (VRRP_SUCCESS);
}
if (vif->vvi_sockfd < 0) {
goto done;
}
/*
*/
sizeof (on)) < 0) {
goto done;
}
/*
* Disable multicast loopback.
*/
sizeof (char)) == -1) {
goto done;
}
/* LINTED E_CONSTANT_CONDITION */
_B_FALSE);
/*
* Set the output interface to send the VRRP packet.
*/
} else {
}
done:
if (err != VRRP_SUCCESS) {
}
return (err);
}
/*
* Create the IPv6 socket which is used to send VRRP packets. Note that
* the destination must be the virtual MAC address, so we specify the output
* interface to be the specific VNIC.
*/
static vrrp_err_t
{
return (VRRP_SUCCESS);
}
if (vif->vvi_sockfd < 0) {
goto done;
}
/*
* Disable multicast loopback.
*/
&off, sizeof (int)) == -1) {
goto done;
}
/*
* Set the multicast TTL.
*/
&ttl, sizeof (int)) == -1) {
goto done;
}
/*
* Set the output interface to send the VRRP packet.
*/
} else {
}
done:
if (err != VRRP_SUCCESS) {
}
return (err);
}
/*
* Delete the socket which is used to send VRRP packets. Further, clear
* the IFF_NOACCEPT flag based on the VRRP router's accept mode.
*/
static void
{
}
}
/*
* Given the the pseudo header cksum value (sum), caculate the cksum with
* the rest of VRRP packet.
*/
static uint16_t
{
int nleft;
uint16_t *w;
w = (uint16_t *)p;
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if (nleft == 1) {
}
/*
* add back carry outs from top 16 bits to low 16 bits
*/
}
/* Pseudo header for v4 */
struct pshv4 {
};
/*
* Checksum routine for VRRP checksum. Note that plen is the upper-layer
* packet length (in the host byte order), and both IP source and destination
* addresses are in the network byte order.
*/
static uint16_t
vrrp_pkt_t *vp)
{
int nleft;
uint16_t *w;
int sum = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while (nleft > 0) {
sum += *w++;
nleft -= 2;
}
}
/* Pseudo header for v6 */
struct pshv6 {
};
/*
* Checksum routine for VRRP checksum. Note that plen is the upper-layer
* packet length (in the host byte order), and both IP source and destination
* addresses are in the network byte order.
*/
static uint16_t
vrrp_pkt_t *vp)
{
int nleft;
uint16_t *w;
int sum = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while (nleft > 0) {
sum += *w++;
nleft -= 2;
}
}
{
return (err);
return (VRRP_ESYS);
} else {
vr->vvr_timeout);
}
return (VRRP_SUCCESS);
}
{
return (err);
/*
* Reinitialize the Master advertisement interval to be the configured
* value.
*/
return (VRRP_ESYS);
} else {
vr->vvr_timeout);
}
return (VRRP_SUCCESS);
}
void
{
}
void
{
}
/* ARGSUSED */
static void
{
(void) vrrpd_state_b2m(vr);
}
/* ARGSUSED */
static void
{
} else {
vr->vvr_timeout);
}
}
{
return (err);
return (VRRP_ESYS);
} else {
vr->vvr_timeout);
}
return (VRRP_SUCCESS);
}
{
return (err);
/*
* Cancel the adver_timer.
*/
} else {
vr->vvr_timeout);
}
return (VRRP_SUCCESS);
}
/*
* Set the IFF_NOACCESS flag on the VNIC interface of the VRRP router
* based on its access mode.
*/
static vrrp_err_t
{
int s;
/*
* Possibly no virtual address exists on this VRRP router yet.
*/
return (VRRP_SUCCESS);
"SIOCGLIFFLAGS on %s failed: %s",
}
return (VRRP_ESYS);
}
if (on)
else
"SIOCSLIFFLAGS 0x%llx on %s failed: %s",
}
return (VRRP_ESYS);
}
}
return (VRRP_SUCCESS);
}
static vrrp_err_t
{
char abuf[INET6_ADDRSTRLEN];
int s;
/* LINTED E_CONSTANT_CONDITION */
"SIOCGLIFFLAGS on %s/%s failed: %s",
}
return (VRRP_ESYS);
}
if (state == VRRP_STATE_MASTER)
else
return (VRRP_SUCCESS);
if (checkonly) {
return (VRRP_ESYS);
"bring %s %s/%s failed: %s",
}
return (VRRP_ESYS);
}
return (VRRP_SUCCESS);
}
static vrrp_err_t
{
char abuf[INET6_ADDRSTRLEN];
/* LINTED E_CONSTANT_CONDITION */
}
}
/*
* The IP address is deleted when it is failed to be brought
* up. If no IP addresses are left, delete this interface.
*/
return (VRRP_ENOVIRT);
}
return (VRRP_SUCCESS);
}
void
{
}
static int
{
/*
* sysevent is not supported in the non-global zone
*/
if (getzoneid() != GLOBAL_ZONEID)
return (0);
goto failed;
VRRP_EVENT_CUR_VERSION) != 0)
goto failed;
goto failed;
goto failed;
goto failed;
return (0);
}
return (-1);
}
/*
* timeval processing functions
*/
static int
{
}
static struct timeval
{
struct timeval t;
if (t.tv_usec < 0) {
t.tv_usec += 1000000;
t.tv_sec--;
}
return (t);
}
/*
* print error messages to the terminal or to syslog
*/
static void
{
int log_level = -1;
if (vrrp_logflag == 0) {
if (level <= vrrp_debug_level) {
/*
* VRRP_ERR goes to stderr, others go to stdout
*/
/* LINTED: E_SEC_PRINTF_VAR_FMT */
}
return;
}
/*
* translate VRRP_* to LOG_*
*/
switch (level) {
case VRRP_ERR:
break;
case VRRP_WARNING:
break;
case VRRP_NOTICE:
break;
case VRRP_DBG0:
break;
default:
break;
}
/* LINTED: E_SEC_PRINTF_VAR_FMT */
}