uvrrpd/vrrp_state.c

353 lines
8.7 KiB
C

/*
* vrrp_state.c
*
* Copyright (C) 2014 Arnaud Andre
*
* This file is part of uvrrpd.
*
* uvrrpd is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* uvrrpd is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with uvrrpd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "vrrp.h"
#include "vrrp_net.h"
#include "vrrp_adv.h"
#include "vrrp_arp.h"
#include "vrrp_na.h"
#include "vrrp_exec.h"
#include "log.h"
#include "bits.h"
#include "uvrrpd.h"
extern unsigned long reg;
/**
* switching state functions
*/
static int vrrp_state_goto_master(struct vrrp *vrrp, struct vrrp_net *vnet);
static int vrrp_state_goto_backup(struct vrrp *vrrp, struct vrrp_net *vnet);
/**
* vrrp_state_init() - Initial state of VRRP instance
*
* Switch to master or backup state
*/
int vrrp_state_init(struct vrrp *vrrp, struct vrrp_net *vnet)
{
log_notice("vrid %d :: %s", vrrp->vrid, "init");
/* Hook init */
vrrp_exec(vrrp, vnet, INIT);
/* init Master_Adver_Interval */
vrrp->master_adv_int = vrrp->adv_int;
log_debug("%d", vrrp->master_adv_int);
/* router owns ip address(es) */
if (vrrp->priority == 255) {
log_debug("%s %d :%s", "priority", vrrp->priority,
"router owns VIP address(es)");
return vrrp_state_goto_master(vrrp, vnet);
}
return vrrp_state_goto_backup(vrrp, vnet);
}
/**
* vrrp_state_backup() - handle backup state
*/
int vrrp_state_backup(struct vrrp *vrrp, struct vrrp_net *vnet)
{
int event = vrrp_listen(vrrp, vnet);
char straddr[INET6_ADDRSTRLEN];
switch (event) {
case TIMER: /* TIMER expired */
log_notice("vrid %d :: %s", vrrp->vrid,
"masterdown_timer expired");
vrrp_state_goto_master(vrrp, vnet);
break;
case PKT: /* PKT received */
/* Must be impossible, invalid received adv buffer */
if (vrrp_adv_get_version(vnet) == 0) {
log_error("vrid %d :: %s", vrrp->vrid,
"recv buffer empty !?");
break;
}
log_debug("vrid %d :: %s", vrrp->vrid,
"pkt received from current master");
log_debug("vrid %d :: %s:%d %s:%d %s:%s", vrrp->vrid, "prio",
vrrp->priority, "prio recv",
vrrp_adv_get_priority(vnet), "preempt",
STR_PREEMPT(vrrp->preempt));
/* Priority of received pkt is 0
* => set Master_Down_Timer to skew_time
*/
if (vrrp_adv_get_priority(vnet) == 0) {
log_info("vrid %d :: %s", vrrp->vrid,
"receive packet with priority 0");
log_notice("vrid %d :: %s %d", vrrp->vrid,
"set masterdown_timer to skew_time",
SKEW_TIME(vrrp));
VRRP_SET_SKEW_TIME(vrrp);
break;
}
/* Master has send its adv pkt, or preemption mode is false
*/
if ((vrrp->preempt == FALSE) ||
(vrrp_adv_get_priority(vnet) >= vrrp->priority)) {
/* RFC5798: Set Master_Adver_Interval to
* Advertisement_Interval
*/
if (vrrp->version == RFC5798)
vrrp->master_adv_int =
vrrp_adv_get_advint(vnet);
VRRP_SET_MASTERDOWN_TIMER(vrrp);
break;
}
/* Our priority is greater and preemption mode is true
* So we discard advertisement.
* Maybe we'll become Master if Master_Down_Timer expire,
*/
log_info("vrid %d :: %s", vrrp->vrid, "discard advertisement");
break;
case SIGNAL:
log_debug("vrid %d :: signal", vrrp->vrid);
case CTRL_FIFO:
/* shutdown/reload event ? */
if (test_and_clear_bit(UVRRPD_RELOAD, &reg)) {
vrrp_timer_clear(&vrrp->masterdown_timer);
vrrp->state = INIT;
}
break;
case VRID_MISMATCH:
/* ignore VRRP pkt with different vrid */
break;
case INVALID:
log_warning("vrid %d :: %s %s, %s", vrrp->vrid,
"receive an invalid advertisement packet from",
vrrp_adv_addr_to_str(vnet, straddr), "ignore it");
break;
default:
log_error("vrid %d :: %s r:%d", vrrp->vrid, "unknown event",
event);
break;
}
return event;
}
/**
* vrrp_state_master() - handle master state
*/
int vrrp_state_master(struct vrrp *vrrp, struct vrrp_net *vnet)
{
int event = vrrp_listen(vrrp, vnet);
switch (event) {
case TIMER: /* TIMER expired */
/* adv_timer expired, time to send another */
log_info("vrid %d :: %s", vrrp->vrid, "adv_timer expired");
vrrp_adv_send(vnet);
VRRP_SET_ADV_TIMER(vrrp);
break;
case PKT: /* PKT received */
/* Must be impossible, invalid adv received buffer */
if (vrrp_adv_get_version(vnet) == 0) {
log_error("vrid %d :: %s", vrrp->vrid,
"recv buffer empty !?");
break;
}
log_debug("vrid %d :: %s:%d %s:%d %s:%s", vrrp->vrid, "prio",
vrrp->priority, "prio recv",
vrrp_adv_get_priority(vnet), "preempt",
STR_PREEMPT(vrrp->preempt));
/* Priority of received pkt is 0
* We send an advertisement
* and rearm Adv_timer
*/
if (vrrp_adv_get_priority(vnet) == 0) {
log_info("vrid %d :: %s", vrrp->vrid,
"receive packet with priority 0");
vrrp_adv_send(vnet);
VRRP_SET_ADV_TIMER(vrrp);
break;
}
/* Priority of received pkt is greater than our,
* switch to backup
*/
if (vrrp_adv_get_priority(vnet) > vrrp->priority) {
log_notice("vrid %d :: %s", vrrp->vrid,
"receive packet with higher priority");
vrrp_state_goto_backup(vrrp, vnet);
break;
}
/* Priority of received pkt is equal, but
* primary IP address of the sender is greater than
* the local primary IP address,
* switch to backup
*/
if (vrrp_adv_get_priority(vnet) == vrrp->priority) {
if (vrrp_adv_addr_cmp(vnet) > 0) {
log_notice("vrid %d :: %s", vrrp->vrid,
"Same priority !");
log_notice("vrid %d :: %s", vrrp->vrid,
"Primary IP address of the sender greater than the local primary address");
vrrp_state_goto_backup(vrrp, vnet);
break;
}
log_notice("vrid %d :: %s", vrrp->vrid,
"Same priority !");
log_notice("vrid %d :: %s", vrrp->vrid,
"Local primary address is greater than the primary IP address of the sender");
}
/* We have the greatest priority */
log_info("vrid %d :: %s", vrrp->vrid, "discard advertisement");
break;
case SIGNAL:
log_debug("vrid %d :: signal", vrrp->vrid);
case CTRL_FIFO:
/* shutdown/reload event ? */
if (test_and_clear_bit(UVRRPD_RELOAD, &reg)) {
vrrp_timer_clear(&vrrp->adv_timer);
vrrp_adv_send_zero(vnet);
/* berk */
vrrp_exec(vrrp, vnet, BACKUP);
vrrp->state = INIT;
}
break;
case VRID_MISMATCH:
/* ignore VRRP pkt with different vrid */
break;
case INVALID:
log_warning("vrid %d :: invalid event", vrrp->vrid);
break;
default:
log_error("vrid %d :: %s r:%d", vrrp->vrid, "unknown event",
event);
break;
}
return event;
}
/**
* vrrp_state_goto_master() - switch state to master
*/
static int vrrp_state_goto_master(struct vrrp *vrrp, struct vrrp_net *vnet)
{
log_notice("vrid %d :: %s -> %s", vrrp->vrid,
STR_STATE(vrrp->state), "master");
vrrp->state = MASTER;
vrrp_adv_send(vnet);
if (vnet->family == AF_INET)
vrrp_arp_send(vnet);
#ifdef HAVE_IP6
else if (vnet->family == AF_INET6)
vrrp_na_send(vnet);
#endif /* HAVE_IP6 */
/* script */
vrrp_exec(vrrp, vnet, vrrp->state);
/* reset masterdown_timer && set ADV timer */
vrrp_timer_clear(&vrrp->masterdown_timer);
VRRP_SET_ADV_TIMER(vrrp);
return 0;
}
/**
* vrrp_state_goto_backup() - switch state to backup
*/
static int vrrp_state_goto_backup(struct vrrp *vrrp, struct vrrp_net *vnet)
{
log_notice("vrid %d :: %s -> %s", vrrp->vrid,
STR_STATE(vrrp->state), "backup");
int previous_state = vrrp->state;
vrrp->state = BACKUP;
log_debug("%s:%s", STR_STATE(previous_state), STR_STATE(vrrp->state));
/* script */
if (previous_state != INIT)
vrrp_exec(vrrp, vnet, vrrp->state);
/* RFC5798, use of master_adv_int */
if (vrrp->version == RFC5798) {
if (previous_state == INIT)
vrrp->master_adv_int = vrrp->adv_int;
else if (previous_state == MASTER)
vrrp->master_adv_int = vrrp_adv_get_advint(vnet);
}
/* clear adv timer && set masterdown_timer */
vrrp_timer_clear(&vrrp->adv_timer);
if ((previous_state == INIT) && (vrrp->start_delay != 0)) {
log_notice("vrid %d :: applying start_delay %d%s",
vrrp->vrid, vrrp->start_delay,
(vrrp->version == 3 ? "cs":"s")
);
VRRP_SET_STARTDELAY_TIMER(vrrp);
}
else
VRRP_SET_MASTERDOWN_TIMER(vrrp);
log_debug("%d %d", vrrp->master_adv_int,
3 * vrrp->master_adv_int + SKEW_TIME(vrrp));
return 0;
}