/*
* Soft: Keepalived is a failover program for the LVS project
* <www.linuxvirtualserver.org>. It monitor & manipulate
* a loadbalanced server pool using multi-layer checks.
*
* Part: Interface tracking framework.
*
* Author: Alexandre Cassen, <acassen@linux-vs.org>
*
* This program 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.
*
* This program 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
* 2 of the License, or (at your option) any later version.
*
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
*/
#include "config.h"
#include <net/if.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
/* local include */
#include "vrrp_data.h"
#include "vrrp.h"
#include "vrrp_track.h"
#include "vrrp_sync.h"
#include "logger.h"
#include "memory.h"
#include "vrrp_scheduler.h"
#include "scheduler.h"
#include "parser.h"
static int inotify_fd = -1;
static thread_t *inotify_thread;
/* Track interface dump */
void
dump_track_if(FILE *fp, void *track_data)
{
tracked_if_t *tip = track_data;
conf_write(fp, " %s weight %d", IF_NAME(tip->ifp), tip->weight);
}
void
free_track_if(void *tip)
{
FREE(tip);
}
void
alloc_track_if(vrrp_t *vrrp, vector_t *strvec)
{
interface_t *ifp = NULL;
tracked_if_t *tip = NULL;
int weight = 0;
char *tracked = strvec_slot(strvec, 0);
element e;
ifp = if_get_by_ifname(tracked, IF_CREATE_IF_DYNAMIC);
if (!ifp) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) tracked interface %s doesn't exist", vrrp->iname, tracked);
return;
}
/* Check this vrrp isn't already tracking the i/f */
LIST_FOREACH(vrrp->track_ifp, tip, e) {
if (tip->ifp == ifp) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_interface %s - ignoring", vrrp->iname, tracked);
return;
}
}
if (vector_size(strvec) >= 3 &&
!strcmp(strvec_slot(strvec, 1), "weight")) {
if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight %s for %s must be between "
"[-253..253] inclusive. Ignoring...", vrrp->iname, FMT_STR_VSLOT(strvec, 2), tracked);
weight = 0;
}
else if (weight == -254 || weight == 254) {
/* This check can be removed once users have migrated away from +/-254 */
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for %s cannot be +/-254. Setting to +/-253", vrrp->iname, tracked);
weight = weight == -254 ? -253 : 253;
}
}
tip = (tracked_if_t *) MALLOC(sizeof(tracked_if_t));
tip->ifp = ifp;
tip->weight = weight;
list_add(vrrp->track_ifp, tip);
}
void
alloc_group_track_if(vrrp_sgroup_t *sgroup, vector_t *strvec)
{
interface_t *ifp;
tracked_if_t *tip;
int weight = 0;
char *tracked = strvec_slot(strvec, 0);
element e;
ifp = if_get_by_ifname(tracked, IF_CREATE_IF_DYNAMIC);
if (!ifp) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) tracked interface %s doesn't exist", sgroup->gname, tracked);
return;
}
/* Check this sgroup isn't already tracking the i/f */
LIST_FOREACH(sgroup->track_ifp, tip, e) {
if (tip->ifp == ifp) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_interface %s - ignoring", sgroup->gname, tracked);
return;
}
}
if (vector_size(strvec) >= 3 &&
!strcmp(strvec_slot(strvec, 1), "weight")) {
if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for %s must be between "
"[-253..253] inclusive. Ignoring...", sgroup->gname, tracked);
weight = 0;
}
else if (weight == -254 || weight == 254) {
/* This check can be removed once users have migrated away from +/-254 */
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for %s cannot be +/-254. Setting to +/-253", sgroup->gname, tracked);
weight = weight == -254 ? -253 : 253;
}
}
tip = (tracked_if_t *) MALLOC(sizeof(tracked_if_t));
tip->ifp = ifp;
tip->weight = weight;
list_add(sgroup->track_ifp, tip);
}
vrrp_script_t *
find_script_by_name(char *name)
{
element e;
vrrp_script_t *scr;
if (LIST_ISEMPTY(vrrp_data->vrrp_script))
return NULL;
for (e = LIST_HEAD(vrrp_data->vrrp_script); e; ELEMENT_NEXT(e)) {
scr = ELEMENT_DATA(e);
if (!strcmp(scr->sname, name))
return scr;
}
return NULL;
}
/* Track script dump */
void
dump_track_script(FILE *fp, void *track_data)
{
tracked_sc_t *tsc = track_data;
conf_write(fp, " %s weight %d", tsc->scr->sname, tsc->weight);
}
void
free_track_script(void *tsc)
{
FREE(tsc);
}
void
alloc_track_script(vrrp_t *vrrp, vector_t *strvec)
{
vrrp_script_t *vsc;
tracked_sc_t *tsc;
int weight;
char *tracked = strvec_slot(strvec, 0);
element e;
tracked_sc_t *etsc;
vsc = find_script_by_name(tracked);
/* Ignoring if no script found */
if (!vsc) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track script %s not found, ignoring...", vrrp->iname, tracked);
return;
}
if (!LIST_ISEMPTY(vrrp->track_script)) {
/* Check this vrrp isn't already tracking the script */
for (e = LIST_HEAD(vrrp->track_script); e; ELEMENT_NEXT(e)) {
etsc = ELEMENT_DATA(e);
if (etsc->scr == vsc) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_script %s - ignoring", vrrp->iname, tracked);
return;
}
}
}
/* default weight */
weight = vsc->weight;
if (vector_size(strvec) >= 3 &&
!strcmp(strvec_slot(strvec, 1), "weight")) {
if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) {
weight = vsc->weight;
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track script %s: weight must be between [-253..253]"
" inclusive, ignoring...",
vrrp->iname, tracked);
}
else if (weight == -254 || weight == 254) {
/* This check can be removed once users have migrated away from +/-254 */
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for %s cannot be +/-254. Setting to +/-253", vrrp->iname, tracked);
weight = weight == -254 ? -253 : 253;
}
}
tsc = (tracked_sc_t *) MALLOC(sizeof(tracked_sc_t));
tsc->scr = vsc;
tsc->weight = weight;
vsc->init_state = SCRIPT_INIT_STATE_INIT;
list_add(vrrp->track_script, tsc);
}
void
alloc_group_track_script(vrrp_sgroup_t *sgroup, vector_t *strvec)
{
vrrp_script_t *vsc = NULL;
tracked_sc_t *tsc = NULL;
int weight = 0;
char *tracked = strvec_slot(strvec, 0);
tracked_sc_t *etsc = NULL;
element e;
vsc = find_script_by_name(tracked);
/* Ignoring if no script found */
if (!vsc) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track script %s not found, ignoring...", sgroup->gname, tracked);
return;
}
if (!LIST_ISEMPTY(sgroup->track_script)) {
/* Check this sync group isn't already tracking the script */
for (e = LIST_HEAD(sgroup->track_script); e; ELEMENT_NEXT(e)) {
etsc = ELEMENT_DATA(e);
if (etsc->scr == vsc) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_script %s - ignoring", sgroup->gname, tracked);
return;
}
}
}
/* default weight */
weight = vsc->weight;
if (vector_size(strvec) >= 3 &&
!strcmp(strvec_slot(strvec, 1), "weight")) {
if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) {
weight = vsc->weight;
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track script %s: weight must be between [-253..253]"
" inclusive, ignoring...",
sgroup->gname, tracked);
}
else if (weight == -254 || weight == 254) {
/* This check can be removed once users have migrated away from +/-254 */
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for %s cannot be +/-254. Setting to +/-253", sgroup->gname, tracked);
weight = weight == -254 ? -253 : 253;
}
}
tsc = (tracked_sc_t *) MALLOC(sizeof(tracked_sc_t));
tsc->scr = vsc;
tsc->weight = weight;
vsc->init_state = SCRIPT_INIT_STATE_INIT;
list_add(sgroup->track_script, tsc);
}
vrrp_tracked_file_t *
find_tracked_file_by_name(const char *name)
{
element e;
vrrp_tracked_file_t *file;
if (LIST_ISEMPTY(vrrp_data->vrrp_track_files))
return NULL;
for (e = LIST_HEAD(vrrp_data->vrrp_track_files); e; ELEMENT_NEXT(e)) {
file = ELEMENT_DATA(e);
if (!strcmp(file->fname, name))
return file;
}
return NULL;
}
/* Track file dump */
void
dump_track_file(FILE *fp, void *track_data)
{
tracked_file_t *tfile = track_data;
conf_write(fp, " %s, weight %d", tfile->file->fname, tfile->weight);
}
void
free_track_file(void *tsf)
{
FREE(tsf);
}
void
alloc_track_file(vrrp_t *vrrp, vector_t *strvec)
{
vrrp_tracked_file_t *vsf;
tracked_file_t *tfile;
char *tracked = strvec_slot(strvec, 0);
tracked_file_t *etfile;
element e;
int weight;
vsf = find_tracked_file_by_name(tracked);
/* Ignoring if no file found */
if (!vsf) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track file %s not found, ignoring...", vrrp->iname, tracked);
return;
}
if (!LIST_ISEMPTY(vrrp->track_file)) {
/* Check this vrrp isn't already tracking the script */
for (e = LIST_HEAD(vrrp->track_file); e; ELEMENT_NEXT(e)) {
etfile = ELEMENT_DATA(e);
if (etfile->file == vsf) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_file %s - ignoring", vrrp->iname, tracked);
return;
}
}
}
weight = vsf->weight;
if (vector_size(strvec) >= 2) {
if (strcmp(strvec_slot(strvec, 1), "weight")) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track file option %s - ignoring",
vrrp->iname, FMT_STR_VSLOT(strvec, 1));
return;
}
if (vector_size(strvec) >= 3) {
if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for track file %s must be in "
"[-254..254] inclusive. Ignoring...", vrrp->iname, tracked);
weight = vsf->weight;
}
} else {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified for track file %s - ignoring",
vrrp->iname, tracked);
return;
}
}
tfile = (tracked_file_t *) MALLOC(sizeof(tracked_file_t));
tfile->file = vsf;
tfile->weight = weight;
list_add(vrrp->track_file, tfile);
}
void
alloc_group_track_file(vrrp_sgroup_t *sgroup, vector_t *strvec)
{
vrrp_tracked_file_t *vsf;
tracked_file_t *tfile;
char *tracked = strvec_slot(strvec, 0);
tracked_file_t *etfile;
element e;
int weight;
vsf = find_tracked_file_by_name(tracked);
/* Ignoring if no file found */
if (!vsf) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track file %s not found, ignoring...", sgroup->gname, tracked);
return;
}
if (!LIST_ISEMPTY(sgroup->track_file)) {
/* Check this vrrp isn't already tracking the script */
for (e = LIST_HEAD(sgroup->track_file); e; ELEMENT_NEXT(e)) {
etfile = ELEMENT_DATA(e);
if (etfile->file == vsf) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_file %s - ignoring", sgroup->gname, tracked);
return;
}
}
}
weight = vsf->weight;
if (vector_size(strvec) >= 2) {
if (strcmp(strvec_slot(strvec, 1), "weight")) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track file option %s - ignoring",
sgroup->gname, FMT_STR_VSLOT(strvec, 1));
return;
}
if (vector_size(strvec) >= 3) {
if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for track file %s must be in "
"[-254..254] inclusive. Ignoring...", sgroup->gname, tracked);
weight = vsf->weight;
}
} else {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified for track file %s - ignoring",
sgroup->gname, tracked);
return;
}
}
tfile = (tracked_file_t *) MALLOC(sizeof(tracked_file_t));
tfile->file = vsf;
tfile->weight = weight;
list_add(sgroup->track_file, tfile);
}
#ifdef _WITH_BFD_
vrrp_tracked_bfd_t *
find_vrrp_tracked_bfd_by_name(const char *name)
{
element e;
vrrp_tracked_bfd_t *bfd;
LIST_FOREACH(vrrp_data->vrrp_track_bfds, bfd, e) {
if (!strcmp(bfd->bname, name))
return bfd;
}
return NULL;
}
/* Track bfd dump */
void
dump_vrrp_tracked_bfd(FILE *fp, void *track_data)
{
tracked_bfd_t *tbfd = track_data;
conf_write(fp, " %s: weight %d", tbfd->bfd->bname, tbfd->weight);
}
void
free_vrrp_tracked_bfd(void *bfd)
{
FREE(bfd);
}
void
alloc_track_bfd(vrrp_t *vrrp, vector_t *strvec)
{
vrrp_tracked_bfd_t *vtb;
tracked_bfd_t *tbfd;
char *tracked = strvec_slot(strvec, 0);
tracked_bfd_t *etbfd;
element e;
int weight;
vtb = find_vrrp_tracked_bfd_by_name(tracked);
/* Ignoring if no bfd found */
if (!vtb) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track bfd %s not found, ignoring...", vrrp->iname, tracked);
return;
}
/* Check this vrrp isn't already tracking the bfd */
LIST_FOREACH(vrrp->track_bfd, etbfd, e) {
if (etbfd->bfd == vtb) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_bfd %s - ignoring", vrrp->iname, tracked);
return;
}
}
weight = vtb->weight;
if (vector_size(strvec) >= 2) {
if (strcmp(strvec_slot(strvec, 1), "weight")) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track bfd option %s - ignoring",
vrrp->iname, FMT_STR_VSLOT(strvec, 1));
return;
}
if (vector_size(strvec) >= 3) {
if (!read_int_strvec(strvec, 2, &weight, -253, 253, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for track bfd %s must be in "
"[-253..253] inclusive. Ignoring...", vrrp->iname, tracked);
weight = vtb->weight;
}
} else {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified for track bfd %s - ignoring",
vrrp->iname, tracked);
return;
}
}
tbfd = (tracked_bfd_t *) MALLOC(sizeof(tracked_bfd_t));
tbfd->bfd = vtb;
tbfd->weight = weight;
list_add(vrrp->track_bfd, tbfd);
}
void
alloc_group_track_bfd(vrrp_sgroup_t *sgroup, vector_t *strvec)
{
vrrp_tracked_bfd_t *vtb;
tracked_bfd_t *tbfd;
char *tracked = strvec_slot(strvec, 0);
tracked_bfd_t *etbfd;
element e;
int weight;
vtb = find_vrrp_tracked_bfd_by_name(tracked);
/* Ignoring if no bfd found */
if (!vtb) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) track bfd %s not found, ignoring...", sgroup->gname, tracked);
return;
}
/* Check this vrrp isn't already tracking the script */
LIST_FOREACH(sgroup->track_bfd, etbfd, e) {
if (etbfd->bfd == vtb) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_bfd %s - ignoring", sgroup->gname, tracked);
return;
}
}
weight = vtb->weight;
if (vector_size(strvec) >= 2) {
if (strcmp(strvec_slot(strvec, 1), "weight")) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track bfd option %s - ignoring",
sgroup->gname, FMT_STR_VSLOT(strvec, 1));
return;
}
if (vector_size(strvec) >= 3) {
if (!read_int_strvec(strvec, 2, &weight, -253, 253, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for track bfd %s must be in "
"[-253..253] inclusive. Ignoring...", sgroup->gname, tracked);
weight = vtb->weight;
}
} else {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified for track bfd %s - ignoring",
sgroup->gname, tracked);
return;
}
}
tbfd = (tracked_bfd_t *) MALLOC(sizeof(tracked_bfd_t));
tbfd->bfd = vtb;
tbfd->weight = weight;
list_add(sgroup->track_bfd, tbfd);
}
#endif
void
down_instance(vrrp_t *vrrp)
{
if (vrrp->num_script_if_fault++ == 0 || vrrp->state == VRRP_STATE_INIT) {
vrrp->wantstate = VRRP_STATE_FAULT;
if (vrrp->state == VRRP_STATE_MAST)
vrrp_state_leave_master(vrrp, true);
else
vrrp_state_leave_fault(vrrp);
if (vrrp->sync && vrrp->sync->num_member_fault++ == 0)
vrrp_sync_fault(vrrp);
}
}
/* Set effective priorty, issue message on changes */
void
vrrp_set_effective_priority(vrrp_t *vrrp)
{
uint8_t new_prio;
uint32_t old_down_timer;
/* Don't change priority if address owner */
if (vrrp->base_priority == VRRP_PRIO_OWNER)
return;
if (vrrp->total_priority < 1)
new_prio = 1;
else if (vrrp->total_priority >= VRRP_PRIO_OWNER)
new_prio = VRRP_PRIO_OWNER - 1;
else
new_prio = (uint8_t)vrrp->total_priority;
if (vrrp->effective_priority == new_prio)
return;
log_message(LOG_INFO, "(%s) Changing effective priority from %d to %d",
vrrp->iname, vrrp->effective_priority, new_prio);
vrrp->effective_priority = new_prio;
old_down_timer = vrrp->ms_down_timer;
vrrp->ms_down_timer = 3 * vrrp->master_adver_int + VRRP_TIMER_SKEW(vrrp);
if (vrrp->state == VRRP_STATE_BACK) {
if (old_down_timer < vrrp->ms_down_timer)
vrrp->sands = timer_add_long(vrrp->sands, vrrp->ms_down_timer - old_down_timer);
else
vrrp->sands = timer_sub_long(vrrp->sands, old_down_timer - vrrp->ms_down_timer);
vrrp_thread_requeue_read(vrrp);
}
}
static void
process_script_update_priority(int weight, vrrp_script_t *vscript, bool script_ok, vrrp_t *vrrp)
{
bool instance_left_init = false;
if (!weight) {
if (vscript->init_state == SCRIPT_INIT_STATE_INIT) {
/* We need to adjust the number of scripts in init state */
if (!--vrrp->num_script_init) {
instance_left_init = true;
if (vrrp->sync)
vrrp->sync->num_member_init--;
}
}
if (!script_ok) {
/* The instance needs to go down */
down_instance(vrrp);
} else if (!vrrp->num_script_init &&
(!vrrp->sync || !vrrp->sync->num_member_init)) {
/* The instance can come up */
try_up_instance(vrrp, instance_left_init); // Set want_state = BACKUP/MASTER, and check i/fs and sync groups
}
return;
}
if (vscript->init_state == SCRIPT_INIT_STATE_INIT) {
/* If the script hasn't previously exited, we need
to only adjust the priority if the state the script
is now in causes an adjustment to the priority */
if (script_ok) {
if (weight > 0)
vrrp->total_priority += weight;
} else {
if (weight < 0)
vrrp->total_priority += weight;
}
} else {
if (script_ok)
vrrp->total_priority += abs(weight);
else
vrrp->total_priority -= abs(weight);
}
vrrp_set_effective_priority(vrrp);
}
void
update_script_priorities(vrrp_script_t *vscript, bool script_ok)
{
element e;
vrrp_t *vrrp;
tracking_vrrp_t* tvp;
/* First process the vrrp instances tracking the script */
if (!LIST_ISEMPTY(vscript->tracking_vrrp)) {
for (e = LIST_HEAD(vscript->tracking_vrrp); e; ELEMENT_NEXT(e)) {
tvp = ELEMENT_DATA(e);
vrrp = tvp->vrrp;
process_script_update_priority(tvp->weight, vscript, script_ok, vrrp);
}
}
}
static void
initialise_track_script_state(tracked_sc_t *tsc, vrrp_t *vrrp)
{
if (!tsc->weight) {
if (tsc->scr->init_state == SCRIPT_INIT_STATE_INIT)
vrrp->num_script_init++;
else if (tsc->scr->init_state == SCRIPT_INIT_STATE_FAILED ||
(tsc->scr->result >= 0 && tsc->scr->result < tsc->scr->rise)) {
/* The script is in fault state */
vrrp->num_script_if_fault++;
vrrp->state = VRRP_STATE_FAULT;
}
return;
}
/* Don't change effective priority if address owner */
if (vrrp->base_priority == VRRP_PRIO_OWNER)
return;
if (tsc->scr->init_state != SCRIPT_INIT_STATE_INIT)
{
if (tsc->scr->result >= tsc->scr->rise) {
if (tsc->weight > 0)
vrrp->total_priority += tsc->weight;
} else {
if (tsc->weight < 0)
vrrp->total_priority += tsc->weight;
}
}
}
#ifdef _WITH_BFD_
static void
initialise_track_bfd_state(tracked_bfd_t *tbfd, vrrp_t *vrrp)
{
if (tbfd->bfd->bfd_up) {
if (tbfd->weight > 0)
vrrp->total_priority += tbfd->weight;
} else {
if (tbfd->weight < 0)
vrrp->total_priority += tbfd->weight;
else if (!tbfd->weight) {
vrrp->num_script_if_fault++;
vrrp->state = VRRP_STATE_FAULT;
}
}
}
#endif
static void
initialise_interface_tracking_priorities(void)
{
tracking_vrrp_t *tvp;
interface_t *ifp;
element e, e1;
LIST_FOREACH(get_if_list(), ifp, e) {
LIST_FOREACH(ifp->tracking_vrrp, tvp, e1) {
if (tvp->weight == VRRP_NOT_TRACK_IF)
continue;
if (!tvp->weight) {
if (!IF_ISUP(ifp)) {
/* The instance is down */
tvp->vrrp->state = VRRP_STATE_FAULT;
tvp->vrrp->num_script_if_fault++;
}
}
else if (IF_ISUP(ifp)) {
if (tvp->weight > 0)
tvp->vrrp->total_priority += tvp->weight;
}
else {
if (tvp->weight < 0)
tvp->vrrp->total_priority += tvp->weight;
}
}
}
}
static void
initialise_file_tracking_priorities(void)
{
vrrp_tracked_file_t *tfile;
tracking_vrrp_t *tvp;
int status;
element e, e1;
LIST_FOREACH(vrrp_data->vrrp_track_files, tfile, e) {
LIST_FOREACH(tfile->tracking_vrrp, tvp, e1) {
status = !tvp->weight ? (tfile->last_status ? -254 : 0 ) : tfile->last_status * tvp->weight;
if (status <= -254) {
/* The instance is down */
tvp->vrrp->state = VRRP_STATE_FAULT;
tvp->vrrp->num_script_if_fault++;
}
else
tvp->vrrp->total_priority += (status > 253 ? 253 : status);
}
}
}
static void
initialise_vrrp_tracking_priorities(vrrp_t *vrrp)
{
element e;
tracked_sc_t *tsc;
#ifdef _WITH_BFD_
tracked_bfd_t *tbfd;
#endif
/* If no src address has been specified, and the interface doesn't have
* an appropriate address, put the interface into fault state */
if (vrrp->saddr.ss_family == AF_UNSPEC) {
vrrp->num_script_if_fault++;
vrrp->state = VRRP_STATE_FAULT;
}
/* Initialise the vrrp instance's tracked scripts */
LIST_FOREACH(vrrp->track_script, tsc, e)
initialise_track_script_state(tsc, vrrp);
#ifdef _WITH_BFD_
/* Initialise the vrrp instance's tracked scripts */
LIST_FOREACH(vrrp->track_bfd, tbfd, e)
initialise_track_bfd_state(tbfd, vrrp);
#endif
/* If have a sync group, initialise it's tracked scripts and bfds */
if (vrrp->sync) {
LIST_FOREACH(vrrp->sync->track_script, tsc, e)
initialise_track_script_state(tsc, vrrp);
}
vrrp_set_effective_priority(vrrp);
}
void
initialise_tracking_priorities(void)
{
vrrp_t *vrrp;
element e;
/* Check for instance down due to an interface */
initialise_interface_tracking_priorities();
initialise_file_tracking_priorities();
/* Now check for tracking scripts, files, bfd etc. */
LIST_FOREACH(vrrp_data->vrrp, vrrp, e) {
/* Set effective priority and fault state */
initialise_vrrp_tracking_priorities(vrrp);
if (vrrp->sync) {
if (vrrp->state == VRRP_STATE_FAULT) {
if (vrrp->sync->state != VRRP_STATE_FAULT) {
vrrp->sync->state = VRRP_STATE_FAULT;
log_message(LOG_INFO, "VRRP_Group(%s): Syncing instances to FAULT state", vrrp->sync->gname);
}
vrrp->sync->num_member_fault++;
}
if (vrrp->num_script_init) {
/* Update init count on sync group if needed */
vrrp->sync->num_member_init++;
if (vrrp->sync->state != VRRP_STATE_FAULT)
vrrp->sync->state = VRRP_STATE_INIT;
}
}
}
}
static void
remove_track_file(list track_files, element e)
{
vrrp_tracked_file_t *tfile = ELEMENT_DATA(e);
element e1;
element e2, next2;
tracking_vrrp_t *tvp;
tracked_file_t *tft;
/* Search through the vrrp instances tracking this file */
LIST_FOREACH(tfile->tracking_vrrp, tvp, e1) {
/* Search for the matching track file */
LIST_FOREACH_NEXT(tvp->vrrp->track_file, tft, e2, next2) {
if (tft->file == tfile)
free_list_element(tvp->vrrp->track_file, e2);
}
}
free_list_element(track_files, e);
}
static void
process_update_track_file_status(vrrp_tracked_file_t *tfile, int new_status, tracking_vrrp_t *tvp)
{
int previous_status;
previous_status = !tvp->weight ? (tfile->last_status ? -254 : 0 ) : tfile->last_status * tvp->weight;
if (previous_status < -254)
previous_status = -254;
else if (previous_status > 253)
previous_status = 253;
if (previous_status == new_status)
return;
if (new_status == -254)
down_instance(tvp->vrrp);
else {
if (previous_status == -254)
try_up_instance(tvp->vrrp, false);
else if (tvp->vrrp->base_priority != VRRP_PRIO_OWNER) {
tvp->vrrp->total_priority += new_status - previous_status;
vrrp_set_effective_priority(tvp->vrrp);
}
}
}
static void
update_track_file_status(vrrp_tracked_file_t* tfile, int new_status)
{
element e;
tracking_vrrp_t *tvp;
int status;
if (new_status == tfile->last_status)
return;
/* Process the VRRP instances tracking the file */
LIST_FOREACH(tfile->tracking_vrrp, tvp, e) {
/* If the tracking weight is 0, a non-zero value means
* failure, a 0 status means success */
if (!tvp->weight)
status = new_status ? -254 : 0;
else {
status = new_status * tvp->weight;
if (status < -254)
status = -254;
else if (status > 253)
status = 253;
}
process_update_track_file_status(tfile, status, tvp);
}
}
static void
process_track_file(vrrp_tracked_file_t *tfile, bool init)
{
long new_status = 0;
char buf[128];
int fd;
ssize_t len;
if ((fd = open(tfile->file_path, O_RDONLY | O_NONBLOCK)) != -1) {
if ((len = read(fd, buf, sizeof(buf) - 1)) > 0) {
buf[len] = '\0';
/* If there is an error, we want to use 0,
* so we don't really mind if there is an error */
new_status = strtol(buf, NULL, 0);
}
close(fd);
}
if (new_status > 254)
new_status = 254;
else if (new_status < -254)
new_status = -254;
if (!init)
update_track_file_status(tfile, (int)new_status);
tfile->last_status = new_status;
}
static int
process_inotify(thread_t *thread)
{
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
char *buf_ptr;
ssize_t len;
struct inotify_event* event;
vrrp_tracked_file_t *tfile;
element e;
int fd = thread->u.fd;
inotify_thread = thread_add_read(master, process_inotify, NULL, fd, TIMER_NEVER);
while (true) {
if ((len = read(fd, buf, sizeof(buf))) < (ssize_t)sizeof(struct inotify_event)) {
if (len == -1) {
if (errno == EAGAIN)
return 0;
if (errno == EINTR)
continue;
log_message(LOG_INFO, "inotify read() returned error %d - %m", errno);
return 0;
}
log_message(LOG_INFO, "inotify read() returned short length %zd", len);
return 0;
}
for (buf_ptr = buf; buf_ptr < buf + len; buf_ptr += event->len + sizeof(struct inotify_event)) {
event = (struct inotify_event*)buf_ptr;
/* We are not interested in directories */
if (event->mask & IN_ISDIR)
continue;
if (!(event->mask & (IN_DELETE | IN_CLOSE_WRITE | IN_MOVE))) {
log_message(LOG_INFO, "Unknown inotify event 0x%x", event->mask);
continue;
}
LIST_FOREACH(vrrp_data->vrrp_track_files, tfile, e) {
/* Is this event for our file */
if (tfile->wd != event->wd ||
strcmp(tfile->file_part, event->name))
continue;
if (event->mask & (IN_MOVED_FROM | IN_DELETE)) {
/* The file has disappeared. Treat as though the value is 0 */
update_track_file_status(tfile, 0);
}
else { /* event->mask & (IN_MOVED_TO | IN_CLOSE_WRITE) */
/* The file has been writted/moved in */
process_track_file(tfile, false);
}
}
}
}
/* NOT REACHED */
}
void
init_track_files(list track_files)
{
vrrp_tracked_file_t *tfile;
char *resolved_path;
char *dir_end = NULL;
char *new_path;
struct stat stat_buf;
char sav_ch;
element e, next;
inotify_fd = -1;
if (LIST_ISEMPTY(track_files))
return;
#ifdef HAVE_INOTIFY_INIT1
inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
#else
inotify_fd = inotify_init();
if (inotify_fd != -1) {
fcntl(inotify_fd, F_SETFD, FD_CLOEXEC);
fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
}
#endif
if (inotify_fd == -1) {
log_message(LOG_INFO, "Unable to monitor vrrp track files");
return ;
}
LIST_FOREACH_NEXT(track_files, tfile, e, next) {
if (LIST_ISEMPTY(tfile->tracking_vrrp)) {
/* No vrrp instance is tracking this file, so forget it */
report_config_error(CONFIG_GENERAL_ERROR, "Track file %s is not being used - removing", tfile->fname);
remove_track_file(track_files, e);
continue;
}
resolved_path = realpath(tfile->file_path, NULL);
if (resolved_path) {
if (strcmp(tfile->file_path, resolved_path)) {
FREE(tfile->file_path);
tfile->file_path = MALLOC(strlen(resolved_path) + 1);
strcpy(tfile->file_path, resolved_path);
}
/* The file exists, so read it now */
process_track_file(tfile, true);
}
else if (errno == ENOENT) {
/* Resolve the directory */
if (!(dir_end = strrchr(tfile->file_path, '/')))
resolved_path = realpath(".", NULL);
else {
*dir_end = '\0';
resolved_path = realpath(tfile->file_path, NULL);
/* Check it is a directory */
if (resolved_path &&
(stat(resolved_path, &stat_buf) ||
!S_ISDIR(stat_buf.st_mode))) {
free(resolved_path);
resolved_path = NULL;
}
}
if (!resolved_path) {
report_config_error(CONFIG_GENERAL_ERROR, "Track file directory for %s does not exist - removing", tfile->fname);
remove_track_file(track_files, e);
continue;
}
if (strcmp(tfile->file_path, resolved_path)) {
new_path = MALLOC(strlen(resolved_path) + strlen((!dir_end) ? tfile->file_path : dir_end + 1) + 2);
strcpy(new_path, resolved_path);
strcat(new_path, "/");
strcat(new_path, dir_end ? dir_end + 1 : tfile->file_path);
FREE(tfile->file_path);
tfile->file_path = new_path;
}
else if (dir_end)
*dir_end = '/';
}
else {
report_config_error(CONFIG_GENERAL_ERROR, "track file %s is not accessible - ignoring", tfile->fname);
remove_track_file(track_files, e);
continue;
}
if (resolved_path)
free(resolved_path);
tfile->file_part = strrchr(tfile->file_path, '/') + 1;
sav_ch = *tfile->file_part;
*tfile->file_part = '\0';
tfile->wd = inotify_add_watch(inotify_fd, tfile->file_path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVE);
*tfile->file_part = sav_ch;
}
inotify_thread = thread_add_read(master, process_inotify, NULL, inotify_fd, TIMER_NEVER);
}
void
stop_track_files(void)
{
if (inotify_thread) {
thread_cancel(inotify_thread);
inotify_thread = NULL;
}
if (inotify_fd != -1) {
close(inotify_fd);
inotify_fd = -1;
}
}
#ifdef THREAD_DUMP
void
register_vrrp_inotify_addresses(void)
{
register_thread_address("process_inotify", process_inotify);
}
#endif