|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Soft: Keepalived is a failover program for the LVS project
|
|
Packit |
c22fc9 |
* <www.linuxvirtualserver.org>. It monitor & manipulate
|
|
Packit |
c22fc9 |
* a loadbalanced server pool using multi-layer checks.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Part: BFD implementation as specified by RFC5880, RFC5881
|
|
Packit |
c22fc9 |
* Bidirectional Forwarding Detection (BFD) is a protocol
|
|
Packit |
c22fc9 |
* which can provide failure detection on bidirectional path
|
|
Packit |
c22fc9 |
* between two hosts. A pair of host creates BFD session for
|
|
Packit |
c22fc9 |
* the communications path. During the communication, hosts
|
|
Packit |
c22fc9 |
* transmit BFD packets periodically over the path between
|
|
Packit |
c22fc9 |
* them, and if one host stops receiving BFD packets for
|
|
Packit |
c22fc9 |
* long enough, some component in the path to the correspondent
|
|
Packit |
c22fc9 |
* peer is assumed to have failed
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Author: Ilya Voronin, <ivoronin@gmail.com>
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
c22fc9 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
c22fc9 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit |
c22fc9 |
* See the GNU General Public License for more details.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* This program is free software; you can redistribute it and/or
|
|
Packit |
c22fc9 |
* modify it under the terms of the GNU General Public License
|
|
Packit |
c22fc9 |
* as published by the Free Software Foundation; either version
|
|
Packit |
c22fc9 |
* 2 of the License, or (at your option) any later version.
|
|
Packit |
c22fc9 |
*
|
|
Packit |
c22fc9 |
* Copyright (C) 2015-2017 Alexandre Cassen, <acassen@gmail.com>
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "config.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include <assert.h>
|
|
Packit |
c22fc9 |
#include <string.h>
|
|
Packit |
c22fc9 |
#include <arpa/inet.h>
|
|
Packit |
c22fc9 |
#include <sys/time.h>
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
#include "bitops.h"
|
|
Packit |
c22fc9 |
#include "bfd.h"
|
|
Packit |
c22fc9 |
#include "bfd_data.h"
|
|
Packit |
c22fc9 |
#include "logger.h"
|
|
Packit |
c22fc9 |
#include "utils.h"
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Initial state */
|
|
Packit |
c22fc9 |
const bfd_t bfd0 = {
|
|
Packit |
c22fc9 |
.local_state = BFD_STATE_DOWN,
|
|
Packit |
c22fc9 |
.remote_state = BFD_STATE_DOWN,
|
|
Packit |
c22fc9 |
.local_discr = 0, /* ! */
|
|
Packit |
c22fc9 |
.remote_discr = 0,
|
|
Packit |
c22fc9 |
.local_diag = BFD_DIAG_NO_DIAG,
|
|
Packit |
c22fc9 |
.remote_diag = BFD_DIAG_NO_DIAG,
|
|
Packit |
c22fc9 |
.remote_min_tx_intv = 0,
|
|
Packit |
c22fc9 |
.remote_min_rx_intv = 0,
|
|
Packit |
c22fc9 |
.local_demand = 0,
|
|
Packit |
c22fc9 |
.remote_demand = 0,
|
|
Packit |
c22fc9 |
.remote_detect_mult = 0,
|
|
Packit |
c22fc9 |
.poll = 0,
|
|
Packit |
c22fc9 |
.final = 0,
|
|
Packit |
c22fc9 |
.local_tx_intv = 0,
|
|
Packit |
c22fc9 |
.remote_tx_intv = 0,
|
|
Packit |
c22fc9 |
.local_detect_time = 0,
|
|
Packit |
c22fc9 |
.remote_detect_time = 0,
|
|
Packit |
c22fc9 |
.last_seen = (struct timeval) {0},
|
|
Packit |
c22fc9 |
};
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_update_local_tx_intv(bfd_t *bfd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
bfd->local_tx_intv = bfd->local_min_tx_intv > bfd->remote_min_rx_intv ?
|
|
Packit |
c22fc9 |
bfd->local_min_tx_intv : bfd->remote_min_rx_intv;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_update_remote_tx_intv(bfd_t *bfd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
bfd->remote_tx_intv = bfd->local_min_rx_intv > bfd->remote_min_tx_intv ?
|
|
Packit |
c22fc9 |
bfd->local_min_rx_intv : bfd->remote_min_tx_intv;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_idle_local_tx_intv(bfd_t *bfd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
bfd->local_tx_intv = bfd->local_idle_tx_intv > bfd->remote_min_rx_intv ?
|
|
Packit |
c22fc9 |
bfd->local_idle_tx_intv : bfd->remote_min_rx_intv;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_set_poll(bfd_t *bfd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_INFO, "BFD_Instance(%s) Starting poll sequence",
|
|
Packit |
c22fc9 |
bfd->iname);
|
|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* RFC5880:
|
|
Packit |
c22fc9 |
* ... If the timing is such that a system receiving a Poll Sequence
|
|
Packit |
c22fc9 |
* wishes to change the parameters described in this paragraph, the
|
|
Packit |
c22fc9 |
* new parameter values MAY be carried in packets with the Final (F)
|
|
Packit |
c22fc9 |
* bit set, even if the Poll Sequence has not yet been sent.
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
if (bfd->final != 1)
|
|
Packit |
c22fc9 |
bfd->poll = 1;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Copies BFD state */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_copy_state(bfd_t *bfd, const bfd_t *bfd_old, bool all_fields)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
assert(bfd_old);
|
|
Packit |
c22fc9 |
assert(bfd);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Copy state variables */
|
|
Packit |
c22fc9 |
bfd->local_state = bfd_old->local_state;
|
|
Packit |
c22fc9 |
bfd->remote_state = bfd_old->remote_state;
|
|
Packit |
c22fc9 |
bfd->remote_discr = bfd_old->remote_discr;
|
|
Packit |
c22fc9 |
bfd->remote_diag = bfd_old->remote_diag;
|
|
Packit |
c22fc9 |
bfd->local_demand = bfd_old->local_demand;
|
|
Packit |
c22fc9 |
bfd->remote_demand = bfd_old->remote_demand;
|
|
Packit |
c22fc9 |
bfd->poll = bfd_old->poll;
|
|
Packit |
c22fc9 |
bfd->final = bfd_old->final;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* RFC5880:
|
|
Packit |
c22fc9 |
* When the text refers to initializing a state variable, this takes
|
|
Packit |
c22fc9 |
* place only at the time that the session (and the corresponding state
|
|
Packit |
c22fc9 |
* variables) is created. The state variables are subsequently
|
|
Packit |
c22fc9 |
* manipulated by the state machine and are never reinitialized, even if
|
|
Packit |
c22fc9 |
* the session fails and is reestablished.
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
if (all_fields) {
|
|
Packit |
c22fc9 |
bfd->local_diag = bfd_old->local_diag;
|
|
Packit |
c22fc9 |
bfd->local_discr = bfd_old->local_discr;
|
|
Packit |
c22fc9 |
bfd->remote_min_tx_intv = bfd_old->remote_min_tx_intv;
|
|
Packit |
c22fc9 |
bfd->remote_min_rx_intv = bfd_old->remote_min_rx_intv;
|
|
Packit |
c22fc9 |
bfd->remote_detect_mult = bfd_old->remote_detect_mult;
|
|
Packit |
c22fc9 |
bfd->local_tx_intv = bfd_old->local_tx_intv;
|
|
Packit |
c22fc9 |
bfd->remote_tx_intv = bfd_old->remote_tx_intv;
|
|
Packit |
c22fc9 |
bfd->local_detect_time = bfd_old->local_detect_time;
|
|
Packit |
c22fc9 |
bfd->remote_detect_time = bfd_old->remote_detect_time;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
bfd->last_seen = bfd_old->last_seen;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Copies thread sands */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_copy_sands(bfd_t *bfd, const bfd_t *bfd_old)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
bfd->sands_out = bfd_old->sands_out;
|
|
Packit |
c22fc9 |
bfd->sands_exp = bfd_old->sands_exp;
|
|
Packit |
c22fc9 |
bfd->sands_rst = bfd_old->sands_rst;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Resets BFD instance to initial state */
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_init_state(bfd_t *bfd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
assert(bfd);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
bfd_copy_state(bfd, &bfd0, true);
|
|
Packit |
c22fc9 |
bfd->local_discr = bfd_get_random_discr(bfd_data);
|
|
Packit |
c22fc9 |
bfd->local_tx_intv = bfd->local_idle_tx_intv;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_reset_state(bfd_t *bfd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
assert(bfd);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
bfd_copy_state(bfd, &bfd0, false);
|
|
Packit |
c22fc9 |
bfd_idle_local_tx_intv(bfd);
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Builds BFD packet
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
void
|
|
Packit |
c22fc9 |
bfd_build_packet(bfdpkt_t *pkt, bfd_t *bfd, char *buf,
|
|
Packit |
c22fc9 |
const ssize_t bufsz)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
ssize_t len = sizeof (bfdhdr_t);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
memset(buf, 0, bufsz);
|
|
Packit |
c22fc9 |
pkt->hdr = (bfdhdr_t *) buf;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* If we are responding to a poll, but also wanted
|
|
Packit |
c22fc9 |
* to send a poll, we can send the parameters now */
|
|
Packit |
c22fc9 |
if (bfd->poll && bfd->final)
|
|
Packit |
c22fc9 |
bfd->poll = false;
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
pkt->hdr->diag = bfd->local_diag;
|
|
Packit |
c22fc9 |
pkt->hdr->version = BFD_VERSION_1;
|
|
Packit |
c22fc9 |
pkt->hdr->state = bfd->local_state;
|
|
Packit |
c22fc9 |
pkt->hdr->poll = bfd->poll;
|
|
Packit |
c22fc9 |
pkt->hdr->final = bfd->final;
|
|
Packit |
c22fc9 |
pkt->hdr->cplane = 0;
|
|
Packit |
c22fc9 |
pkt->hdr->auth = 0; /* Auth is not supported */
|
|
Packit |
c22fc9 |
pkt->hdr->demand = bfd->local_demand;
|
|
Packit |
c22fc9 |
pkt->hdr->multipoint = 0;
|
|
Packit |
c22fc9 |
pkt->hdr->detect_mult = bfd->local_detect_mult;
|
|
Packit |
c22fc9 |
pkt->hdr->len = len;
|
|
Packit |
c22fc9 |
pkt->hdr->local_discr = htonl(bfd->local_discr);
|
|
Packit |
c22fc9 |
pkt->hdr->remote_discr = htonl(bfd->remote_discr);
|
|
Packit |
c22fc9 |
pkt->hdr->min_tx_intv = bfd->local_state == BFD_STATE_UP ? htonl(bfd->local_min_tx_intv) : htonl(bfd->local_idle_tx_intv);
|
|
Packit |
c22fc9 |
pkt->hdr->min_rx_intv = htonl(bfd->local_min_rx_intv);
|
|
Packit |
c22fc9 |
pkt->hdr->min_echo_rx_intv = 0; /* Echo function is not supported */
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
pkt->len = len;
|
|
Packit |
c22fc9 |
pkt->dst_addr = bfd->nbr_addr;
|
|
Packit |
c22fc9 |
pkt->buf = buf;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/*
|
|
Packit |
c22fc9 |
* Performs sanity checks on a packet
|
|
Packit |
c22fc9 |
*/
|
|
Packit |
c22fc9 |
bool
|
|
Packit |
c22fc9 |
bfd_check_packet(const bfdpkt_t *pkt)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
/* Preliminary sanity checks */
|
|
Packit |
c22fc9 |
if (sizeof (bfdhdr_t) > pkt->len) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet is too small: %u bytes",
|
|
Packit |
c22fc9 |
pkt->len);
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (pkt->hdr->len != pkt->len) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet size mismatch:"
|
|
Packit |
c22fc9 |
" length field: %u bytes"
|
|
Packit |
c22fc9 |
", buffer size: %u bytes",
|
|
Packit |
c22fc9 |
pkt->hdr->len, pkt->len);
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Main Checks (RFC5880) */
|
|
Packit |
c22fc9 |
if (pkt->hdr->version != BFD_VERSION_1) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet is of unsupported"
|
|
Packit |
c22fc9 |
" version: %i", pkt->hdr->version);
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!pkt->hdr->detect_mult) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet 'detection multiplier'"
|
|
Packit |
c22fc9 |
" field is zero");
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (pkt->hdr->multipoint) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet has 'multipoint' flag");
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!pkt->hdr->local_discr) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet 'my discriminator'"
|
|
Packit |
c22fc9 |
" field is zero");
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!pkt->hdr->remote_discr
|
|
Packit |
c22fc9 |
&& pkt->hdr->state != BFD_STATE_DOWN
|
|
Packit |
c22fc9 |
&& pkt->hdr->state != BFD_STATE_ADMINDOWN) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR,
|
|
Packit |
c22fc9 |
"Packet 'your discriminator' field is"
|
|
Packit |
c22fc9 |
" zero and 'state' field is not"
|
|
Packit |
c22fc9 |
" Down or AdminDown");
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
/* Additional sanity checks */
|
|
Packit |
c22fc9 |
if (pkt->hdr->poll && pkt->hdr->final) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet has both poll and final"
|
|
Packit |
c22fc9 |
" flags set");
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!BFD_VALID_STATE(pkt->hdr->state)) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet has invalid 'state'"
|
|
Packit |
c22fc9 |
" field: %u", pkt->hdr->state);
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
if (!BFD_VALID_DIAG(pkt->hdr->diag)) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet has invalid 'diag'"
|
|
Packit |
c22fc9 |
" field: %u", pkt->hdr->diag);
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
bool
|
|
Packit |
c22fc9 |
bfd_check_packet_ttl(const bfdpkt_t *pkt, const bfd_t *bfd)
|
|
Packit |
c22fc9 |
{
|
|
Packit |
c22fc9 |
/* Generalized TTL Security Mechanism Check (RFC5881)
|
|
Packit |
c22fc9 |
* - extended so we can specify a maximum number of hops */
|
|
Packit |
c22fc9 |
if (pkt->ttl && bfd->max_hops + pkt->ttl < bfd->ttl) {
|
|
Packit |
c22fc9 |
if (__test_bit(LOG_DETAIL_BIT, &debug))
|
|
Packit |
c22fc9 |
log_message(LOG_ERR, "Packet %s(%i) < %i - discarding",
|
|
Packit |
c22fc9 |
pkt->src_addr.ss_family == AF_INET ? "ttl" : "hop_limit",
|
|
Packit |
c22fc9 |
pkt->ttl,
|
|
Packit |
c22fc9 |
bfd->ttl - bfd->max_hops);
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return true;
|
|
Packit |
c22fc9 |
}
|
|
Packit |
c22fc9 |
|
|
Packit |
c22fc9 |
return false;
|
|
Packit |
c22fc9 |
}
|