Blame keepalived/bfd/bfd.c

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
}