Blame keepalived/bfd/bfd_scheduler.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:        Scheduling framework for bfd code
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 <sys/socket.h>
Packit c22fc9
#include <unistd.h>
Packit c22fc9
#include <assert.h>
Packit c22fc9
#include <netdb.h>
Packit c22fc9
#include <inttypes.h>
Packit c22fc9
#include <stdio.h>
Packit c22fc9
Packit c22fc9
#include "bfd.h"
Packit c22fc9
#include "bfd_data.h"
Packit c22fc9
#include "bfd_scheduler.h"
Packit c22fc9
#include "bfd_event.h"
Packit c22fc9
#include "parser.h"
Packit c22fc9
#include "logger.h"
Packit c22fc9
#include "memory.h"
Packit c22fc9
#include "main.h"
Packit c22fc9
#include "bitops.h"
Packit c22fc9
#include "utils.h"
Packit c22fc9
#include "signals.h"
Packit c22fc9
Packit c22fc9
static int bfd_send_packet(int, bfdpkt_t *, bool);
Packit c22fc9
static void bfd_sender_schedule(bfd_t *);
Packit c22fc9
Packit c22fc9
static void bfd_state_down(bfd_t *, char diag);
Packit c22fc9
static void bfd_state_admindown(bfd_t *);
Packit c22fc9
static void bfd_state_up(bfd_t *);
Packit c22fc9
static void bfd_dump_timers(FILE *fp, bfd_t *);
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * Session sender thread
Packit c22fc9
 *
Packit c22fc9
 * Runs every local_tx_intv, or after reception of a packet
Packit c22fc9
 * with Poll bit set
Packit c22fc9
 */
Packit c22fc9
Packit c22fc9
inline static long
Packit c22fc9
thread_time_to_wakeup(thread_t *thread)
Packit c22fc9
{
Packit c22fc9
	struct timeval tmp_time;
Packit c22fc9
Packit c22fc9
	timersub(&thread->sands, &time_now, &tmp_time);
Packit c22fc9
Packit c22fc9
	return timer_long(tmp_time);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Sends one BFD control packet and reschedules itself if needed */
Packit c22fc9
static int
Packit c22fc9
bfd_sender_thread(thread_t *thread)
Packit c22fc9
{
Packit c22fc9
	bfd_t *bfd;
Packit c22fc9
	bfdpkt_t pkt;
Packit c22fc9
Packit c22fc9
	assert(thread);
Packit c22fc9
	bfd = THREAD_ARG(thread);
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(!BFD_ISADMINDOWN(bfd));
Packit c22fc9
Packit c22fc9
	if (thread->type != THREAD_EVENT)
Packit c22fc9
		bfd->thread_out = NULL;
Packit c22fc9
Packit c22fc9
	bfd_build_packet(&pkt, bfd, bfd_buffer, BFD_BUFFER_SIZE);
Packit c22fc9
	if (bfd_send_packet(bfd->fd_out, &pkt, !bfd->send_error) == -1) {
Packit c22fc9
		if (!bfd->send_error) {
Packit c22fc9
			log_message(LOG_ERR, "BFD_Instance(%s) Error sending packet", bfd->iname);
Packit c22fc9
			bfd->send_error = true;
Packit c22fc9
		}
Packit c22fc9
	} else
Packit c22fc9
		bfd->send_error = false;
Packit c22fc9
Packit c22fc9
	/* Reset final flag if set */
Packit c22fc9
	bfd->final = 0;
Packit c22fc9
Packit c22fc9
	/* Schedule next run if not called as an event thread */
Packit c22fc9
	if (thread->type != THREAD_EVENT)
Packit c22fc9
		bfd_sender_schedule(bfd);
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Schedules bfd_sender_thread to run in local_tx_intv minus applied jitter */
Packit c22fc9
static uint32_t
Packit c22fc9
get_jitter(bfd_t * bfd)
Packit c22fc9
{
Packit c22fc9
	uint32_t min_jitter;
Packit c22fc9
Packit c22fc9
	/*
Packit c22fc9
	 * RFC5880:
Packit c22fc9
	 * The periodic transmission of BFD Control packets MUST be jittered
Packit c22fc9
	 * on a per-packet basis by up to 25%, that is, the interval MUST be
Packit c22fc9
	 * reduced by a random value of 0 to 25% <...>
Packit c22fc9
	 *
Packit c22fc9
	 * If bfd.DetectMult is equal to 1, the interval between transmitted
Packit c22fc9
	 * BFD Control packets MUST be no more than 90% of the negotiated
Packit c22fc9
	 * transmission interval, and MUST be no less than 75% of the
Packit c22fc9
	 * negotiated transmission interval.
Packit c22fc9
	 */
Packit c22fc9
	if (bfd->local_detect_mult)
Packit c22fc9
		min_jitter = bfd->local_tx_intv * 0.1;
Packit c22fc9
	else
Packit c22fc9
		min_jitter = 0;
Packit c22fc9
Packit c22fc9
	return rand_intv(min_jitter, bfd->local_tx_intv * 0.25);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Schedules bfd_sender_thread to run in local_tx_intv minus applied jitter */
Packit c22fc9
static void
Packit c22fc9
bfd_sender_schedule(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(!bfd->thread_out);
Packit c22fc9
Packit c22fc9
	bfd->thread_out =
Packit c22fc9
	    thread_add_timer(master, bfd_sender_thread, bfd,
Packit c22fc9
			     bfd->local_tx_intv - get_jitter(bfd));
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Cancels bfd_sender_thread run */
Packit c22fc9
static void
Packit c22fc9
bfd_sender_cancel(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_out);
Packit c22fc9
Packit c22fc9
	thread_cancel(bfd->thread_out);
Packit c22fc9
	bfd->thread_out = NULL;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Reschedules bfd_sender_thread run (usually after local_tx_intv change) */
Packit c22fc9
static void
Packit c22fc9
bfd_sender_reschedule(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_out);
Packit c22fc9
Packit c22fc9
	timer_thread_update_timeout(bfd->thread_out, bfd->local_tx_intv - get_jitter(bfd));
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Returns 1 if bfd_sender_thread is scheduled to run, 0 otherwise */
Packit c22fc9
static int
Packit c22fc9
bfd_sender_scheduled(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	return bfd->thread_out != NULL;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Suspends sender thread. Needs freshly updated time_now */
Packit c22fc9
static void
Packit c22fc9
bfd_sender_suspend(bfd_t * bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_out);
Packit c22fc9
	assert(bfd->sands_out == -1);
Packit c22fc9
Packit c22fc9
	bfd->sands_out = thread_time_to_wakeup(bfd->thread_out);
Packit c22fc9
	bfd_sender_cancel(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Resumes sender thread */
Packit c22fc9
static void
Packit c22fc9
bfd_sender_resume(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(!bfd->thread_out);
Packit c22fc9
	assert(bfd->sands_out != -1);
Packit c22fc9
Packit c22fc9
	if (!bfd->passive || bfd->local_state == BFD_STATE_UP)
Packit c22fc9
		bfd->thread_out =
Packit c22fc9
		    thread_add_timer(master, bfd_sender_thread, bfd, bfd->sands_out);
Packit c22fc9
	bfd->sands_out = -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Returns 1 if bfd_sender_thread is suspended, 0 otherwise */
Packit c22fc9
static int
Packit c22fc9
bfd_sender_suspended(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	return bfd->sands_out != -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
static void
Packit c22fc9
bfd_sender_discard(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->sands_out != -1);
Packit c22fc9
Packit c22fc9
	bfd->sands_out = -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * Session expiration thread
Packit c22fc9
 *
Packit c22fc9
 * Runs after local_detect_time has passed since receipt of last
Packit c22fc9
 * BFD control packet from neighbor
Packit c22fc9
 */
Packit c22fc9
Packit c22fc9
/* Marks session as down because of Control Detection Time Expiration */
Packit c22fc9
static int
Packit c22fc9
bfd_expire_thread(thread_t *thread)
Packit c22fc9
{
Packit c22fc9
	bfd_t *bfd;
Packit c22fc9
	uint32_t dead_time, overdue_time;
Packit c22fc9
	timeval_t dead_time_tv;
Packit c22fc9
Packit c22fc9
	assert(thread);
Packit c22fc9
Packit c22fc9
	bfd = THREAD_ARG(thread);
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	/* Session cannot expire while not in Up or Init states */
Packit c22fc9
	assert(BFD_ISUP(bfd) || BFD_ISINIT(bfd));
Packit c22fc9
Packit c22fc9
	bfd->thread_exp = NULL;
Packit c22fc9
Packit c22fc9
	/* Time since last received control packet */
Packit c22fc9
	timersub(&time_now, &bfd->last_seen, &dead_time_tv);
Packit c22fc9
	dead_time = timer_long(dead_time_tv);
Packit c22fc9
Packit c22fc9
	/* Difference between expected and actual failure detection time */
Packit c22fc9
	overdue_time = dead_time - bfd->local_detect_time;
Packit c22fc9
Packit c22fc9
	if (bfd->local_state == BFD_STATE_UP ||
Packit c22fc9
	    __test_bit(LOG_EXTRA_DETAIL_BIT, &debug))
Packit c22fc9
		log_message(LOG_WARNING, "BFD_Instance(%s) Expired after"
Packit c22fc9
			    " %i ms (%i usec overdue)",
Packit c22fc9
			    bfd->iname, dead_time / 1000, overdue_time);
Packit c22fc9
Packit c22fc9
	/*
Packit c22fc9
	 * RFC5880:
Packit c22fc9
	 * <...> If a period of a Detection Time passes without the
Packit c22fc9
	 * receipt of a valid, authenticated BFD packet from the remote
Packit c22fc9
	 * system, this <bfd.RemoteDiscr> variable MUST be set to zero.
Packit c22fc9
	 */
Packit c22fc9
	bfd->remote_discr = 0;
Packit c22fc9
	bfd_state_down(bfd, BFD_DIAG_EXPIRED);
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Schedules bfd_expire_thread to run in local_detect_time */
Packit c22fc9
static void
Packit c22fc9
bfd_expire_schedule(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(!bfd->thread_exp);
Packit c22fc9
Packit c22fc9
	bfd->thread_exp =
Packit c22fc9
	    thread_add_timer(master, bfd_expire_thread, bfd,
Packit c22fc9
			     bfd->local_detect_time);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Cancels bfd_expire_thread run */
Packit c22fc9
static void
Packit c22fc9
bfd_expire_cancel(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_exp);
Packit c22fc9
Packit c22fc9
	thread_cancel(bfd->thread_exp);
Packit c22fc9
	bfd->thread_exp = NULL;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Reschedules bfd_expire_thread run (usually after control packet receipt) */
Packit c22fc9
static void
Packit c22fc9
bfd_expire_reschedule(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_exp);
Packit c22fc9
Packit c22fc9
	timer_thread_update_timeout(bfd->thread_exp, bfd->local_detect_time);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Returns 1 if bfd_expire_thread is scheduled to run, 0 otherwise */
Packit c22fc9
static int
Packit c22fc9
bfd_expire_scheduled(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	return bfd->thread_exp != NULL;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Suspends expire thread. Needs freshly updated time_now */
Packit c22fc9
static void
Packit c22fc9
bfd_expire_suspend(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_exp);
Packit c22fc9
	assert(bfd->sands_exp == -1);
Packit c22fc9
Packit c22fc9
	bfd->sands_exp = thread_time_to_wakeup(bfd->thread_exp);
Packit c22fc9
	bfd_expire_cancel(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Resumes expire thread */
Packit c22fc9
static void
Packit c22fc9
bfd_expire_resume(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(!bfd->thread_exp);
Packit c22fc9
	assert(bfd->sands_exp != -1);
Packit c22fc9
Packit c22fc9
	bfd->thread_exp =
Packit c22fc9
	    thread_add_timer(master, bfd_expire_thread, bfd, bfd->sands_exp);
Packit c22fc9
	bfd->sands_exp = -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Returns 1 if bfd_expire_thread is suspended, 0 otherwise */
Packit c22fc9
static int
Packit c22fc9
bfd_expire_suspended(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	return bfd->sands_exp != -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
static void
Packit c22fc9
bfd_expire_discard(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->sands_exp != -1);
Packit c22fc9
Packit c22fc9
	bfd->sands_exp = -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * Session reset thread
Packit c22fc9
 *
Packit c22fc9
 * Runs after local_detect_time has passed after BFD session
Packit c22fc9
 * gone to Down state.
Packit c22fc9
 */
Packit c22fc9
Packit c22fc9
/* Resets BFD session to initial state */
Packit c22fc9
static int
Packit c22fc9
bfd_reset_thread(thread_t *thread)
Packit c22fc9
{
Packit c22fc9
	bfd_t *bfd;
Packit c22fc9
Packit c22fc9
	assert(thread);
Packit c22fc9
Packit c22fc9
	bfd = THREAD_ARG(thread);
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_rst);
Packit c22fc9
Packit c22fc9
	bfd->thread_rst = NULL;
Packit c22fc9
Packit c22fc9
	bfd_reset_state(bfd);
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Schedules bfd_reset_thread to run in local_detect_time */
Packit c22fc9
static void
Packit c22fc9
bfd_reset_schedule(bfd_t * bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(!bfd->thread_rst);
Packit c22fc9
Packit c22fc9
	bfd->thread_rst =
Packit c22fc9
	    thread_add_timer(master, bfd_reset_thread, bfd,
Packit c22fc9
			     bfd->local_detect_time);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Cancels bfd_reset_thread run */
Packit c22fc9
static void
Packit c22fc9
bfd_reset_cancel(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_rst);
Packit c22fc9
Packit c22fc9
	thread_cancel(bfd->thread_rst);
Packit c22fc9
	bfd->thread_rst = NULL;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Returns 1 if bfd_reset_thread is scheduled to run, 0 otherwise */
Packit c22fc9
static int
Packit c22fc9
bfd_reset_scheduled(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	return bfd->thread_rst != NULL;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Suspends reset thread. Needs freshly updated time_now */
Packit c22fc9
static void
Packit c22fc9
bfd_reset_suspend(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->thread_rst);
Packit c22fc9
	assert(bfd->sands_rst == -1);
Packit c22fc9
Packit c22fc9
	bfd->sands_rst = thread_time_to_wakeup(bfd->thread_rst);
Packit c22fc9
	bfd_reset_cancel(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Resumes reset thread */
Packit c22fc9
static void
Packit c22fc9
bfd_reset_resume(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(!bfd->thread_rst);
Packit c22fc9
	assert(bfd->sands_rst != -1);
Packit c22fc9
Packit c22fc9
	bfd->thread_rst =
Packit c22fc9
	    thread_add_timer(master, bfd_reset_thread, bfd, bfd->sands_rst);
Packit c22fc9
	bfd->sands_rst = -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Returns 1 if bfd_reset_thread is suspended, 0 otherwise */
Packit c22fc9
static int
Packit c22fc9
bfd_reset_suspended(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	return bfd->sands_rst != -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
static void
Packit c22fc9
bfd_reset_discard(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->sands_rst != -1);
Packit c22fc9
Packit c22fc9
	bfd->sands_rst = -1;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * State change handlers
Packit c22fc9
 */
Packit c22fc9
/* Common actions for Down and AdminDown states */
Packit c22fc9
static void
Packit c22fc9
bfd_state_fall(bfd_t *bfd, bool send_event)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	/*
Packit c22fc9
	 * RFC5880:
Packit c22fc9
	 * When bfd.SessionState is not Up, the system MUST set
Packit c22fc9
	 * bfd.DesiredMinTxInterval to a value of not less than
Packit c22fc9
	 * one second (1,000,000 microseconds)
Packit c22fc9
	 */
Packit c22fc9
	bfd_idle_local_tx_intv(bfd);
Packit c22fc9
Packit c22fc9
	if (bfd_expire_scheduled(bfd))
Packit c22fc9
		bfd_expire_cancel(bfd);
Packit c22fc9
Packit c22fc9
	if (send_event &&
Packit c22fc9
	    bfd->remote_state != BFD_STATE_ADMINDOWN)
Packit c22fc9
		bfd_event_send(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Runs when BFD session state goes Down */
Packit c22fc9
static void
Packit c22fc9
bfd_state_down(bfd_t *bfd, char diag)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(BFD_VALID_DIAG(diag));
Packit c22fc9
	int old_state = bfd->local_state;
Packit c22fc9
Packit c22fc9
	if (bfd->local_state == BFD_STATE_UP)
Packit c22fc9
		bfd->local_discr = bfd_get_random_discr(bfd_data);
Packit c22fc9
Packit c22fc9
	if (bfd->local_state == BFD_STATE_UP ||
Packit c22fc9
	    __test_bit(LOG_EXTRA_DETAIL_BIT, &debug))
Packit c22fc9
		log_message(LOG_WARNING, "BFD_Instance(%s) Entering %s state"
Packit c22fc9
			    " (Local diagnostic - %s, Remote diagnostic - %s)",
Packit c22fc9
			    bfd->iname, BFD_STATE_STR(BFD_STATE_DOWN),
Packit c22fc9
			    BFD_DIAG_STR(diag),
Packit c22fc9
			    BFD_DIAG_STR(bfd->remote_diag));
Packit c22fc9
Packit c22fc9
	bfd->local_state = BFD_STATE_DOWN;
Packit c22fc9
	bfd->local_diag = diag;
Packit c22fc9
Packit c22fc9
	bfd_reset_schedule(bfd);
Packit c22fc9
Packit c22fc9
	if (bfd->passive && bfd_sender_scheduled(bfd))
Packit c22fc9
		bfd_sender_cancel(bfd);
Packit c22fc9
Packit c22fc9
	bfd_state_fall(bfd, old_state == BFD_STATE_UP);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Runs when BFD session state goes AdminDown */
Packit c22fc9
static void
Packit c22fc9
bfd_state_admindown(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	bfd->local_state = BFD_STATE_ADMINDOWN;
Packit c22fc9
	bfd->local_diag = BFD_DIAG_ADMIN_DOWN;
Packit c22fc9
Packit c22fc9
	if (bfd_sender_scheduled(bfd))
Packit c22fc9
		bfd_sender_cancel(bfd);
Packit c22fc9
Packit c22fc9
	log_message(LOG_WARNING, "BFD_Instance(%s) Entering %s state",
Packit c22fc9
		    bfd->iname, BFD_STATE_STR(bfd->local_state));
Packit c22fc9
Packit c22fc9
	bfd_state_fall(bfd, false);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Common actions for Init and Up states */
Packit c22fc9
static void
Packit c22fc9
bfd_state_rise(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	/* RFC5880 doesn't state if this must be done or not */
Packit c22fc9
	bfd->local_diag = BFD_DIAG_NO_DIAG;
Packit c22fc9
Packit c22fc9
	if (bfd->local_state == BFD_STATE_UP ||
Packit c22fc9
	    __test_bit(LOG_EXTRA_DETAIL_BIT, &debug))
Packit c22fc9
		log_message(LOG_INFO, "BFD_Instance(%s) Entering %s state",
Packit c22fc9
			    bfd->iname, BFD_STATE_STR(bfd->local_state));
Packit c22fc9
Packit c22fc9
	if (bfd_reset_scheduled(bfd))
Packit c22fc9
		bfd_reset_cancel(bfd);
Packit c22fc9
Packit c22fc9
	if (!bfd_expire_scheduled(bfd))
Packit c22fc9
		bfd_expire_schedule(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Runs when BFD session state goes Up */
Packit c22fc9
static void
Packit c22fc9
bfd_state_up(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	bfd->local_state = BFD_STATE_UP;
Packit c22fc9
	bfd_state_rise(bfd);
Packit c22fc9
Packit c22fc9
	if (bfd->local_idle_tx_intv != bfd->local_min_tx_intv)
Packit c22fc9
		bfd_set_poll(bfd);
Packit c22fc9
Packit c22fc9
	bfd_event_send(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Runs when BFD session state goes Init */
Packit c22fc9
static void
Packit c22fc9
bfd_state_init(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	/* According to RFC5880 session cannot directly
Packit c22fc9
	   transition from Init to Up state */
Packit c22fc9
	assert(!BFD_ISUP(bfd));
Packit c22fc9
Packit c22fc9
	bfd->local_state = BFD_STATE_INIT;
Packit c22fc9
	bfd_state_rise(bfd);
Packit c22fc9
Packit c22fc9
	if (bfd->passive && !bfd_sender_scheduled(bfd))
Packit c22fc9
		bfd_sender_schedule(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Dumps current timers values */
Packit c22fc9
static void
Packit c22fc9
bfd_dump_timers(FILE *fp, bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	assert(bfd);
Packit c22fc9
Packit c22fc9
	conf_write(fp, "BFD_Instance(%s)"
Packit c22fc9
		    " --------------< Session parameters >-------------",
Packit c22fc9
		    bfd->iname);
Packit c22fc9
	conf_write(fp, "BFD_Instance(%s)"
Packit c22fc9
		    "        min_tx  min_rx  tx_intv  mult  detect_time",
Packit c22fc9
		    bfd->iname);
Packit c22fc9
	conf_write(fp, "BFD_Instance(%s)"
Packit c22fc9
		    " local %7u %7u %8u %5u %12" PRIu64,
Packit c22fc9
		    bfd->iname, (bfd->local_state == BFD_STATE_UP ? bfd->local_min_tx_intv : bfd->local_idle_tx_intv) / 1000,
Packit c22fc9
		    bfd->local_min_rx_intv / 1000,
Packit c22fc9
		    bfd->local_tx_intv / 1000, bfd->local_detect_mult,
Packit c22fc9
		    bfd->local_detect_time / 1000);
Packit c22fc9
	conf_write(fp, "BFD_Instance(%s)" " remote %6u %7u %8u %5u %12" PRIu64,
Packit c22fc9
		    bfd->iname, bfd->remote_min_tx_intv / 1000,
Packit c22fc9
		    bfd->remote_min_rx_intv / 1000,
Packit c22fc9
		    bfd->remote_tx_intv / 1000, bfd->remote_detect_mult,
Packit c22fc9
		    bfd->remote_detect_time / 1000);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * Packet handling functions
Packit c22fc9
 */
Packit c22fc9
Packit c22fc9
/* Sends a control packet to the neighbor (called from bfd_sender_thread)
Packit c22fc9
   returns -1 on error */
Packit c22fc9
static int
Packit c22fc9
bfd_send_packet(int fd, bfdpkt_t *pkt, bool log_error)
Packit c22fc9
{
Packit c22fc9
	int ret;
Packit c22fc9
	socklen_t dstlen;
Packit c22fc9
Packit c22fc9
	assert(fd >= 0);
Packit c22fc9
	assert(pkt);
Packit c22fc9
Packit c22fc9
	if (pkt->dst_addr.ss_family == AF_INET)
Packit c22fc9
		dstlen = sizeof (struct sockaddr_in);
Packit c22fc9
	else
Packit c22fc9
		dstlen = sizeof (struct sockaddr_in6);
Packit c22fc9
Packit c22fc9
	ret =
Packit c22fc9
	    sendto(fd, pkt->buf, pkt->len, 0,
Packit c22fc9
		   (struct sockaddr *) &pkt->dst_addr, dstlen);
Packit c22fc9
	if (ret == -1 && log_error)
Packit c22fc9
		log_message(LOG_ERR, "sendto() error (%m)");
Packit c22fc9
Packit c22fc9
	return ret;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Handles incoming control packet (called from bfd_receiver_thread) and
Packit c22fc9
   processes it through a BFD state machine. */
Packit c22fc9
static void
Packit c22fc9
bfd_handle_packet(bfdpkt_t *pkt)
Packit c22fc9
{
Packit c22fc9
	uint32_t old_local_tx_intv;
Packit c22fc9
	uint32_t old_remote_rx_intv;
Packit c22fc9
	uint32_t old_remote_tx_intv;
Packit c22fc9
	uint8_t old_remote_detect_mult;
Packit c22fc9
	uint64_t old_local_detect_time;
Packit c22fc9
	bfd_t *bfd;
Packit c22fc9
Packit c22fc9
	assert(pkt);
Packit c22fc9
	assert(pkt->hdr);
Packit c22fc9
Packit c22fc9
	/* Perform sanity checks on a packet */
Packit c22fc9
	if (bfd_check_packet(pkt)) {
Packit c22fc9
		if (__test_bit(LOG_DETAIL_BIT, &debug))
Packit c22fc9
			log_message(LOG_ERR,
Packit c22fc9
				    "Discarding bogus packet from %s",
Packit c22fc9
				    inet_sockaddrtopair(&pkt->src_addr));
Packit c22fc9
Packit c22fc9
		return;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Lookup session */
Packit c22fc9
	if (!pkt->hdr->remote_discr)
Packit c22fc9
		bfd = find_bfd_by_addr(&pkt->src_addr);
Packit c22fc9
	else
Packit c22fc9
		bfd = find_bfd_by_discr(ntohl(pkt->hdr->remote_discr));
Packit c22fc9
Packit c22fc9
	if (!bfd) {
Packit c22fc9
		if (__test_bit(LOG_DETAIL_BIT, &debug))
Packit c22fc9
			log_message(LOG_ERR, "Discarding packet from %s"
Packit c22fc9
				    " (session is not found - your"
Packit c22fc9
				    " discriminator field is %u)",
Packit c22fc9
				    inet_sockaddrtopair(&pkt->src_addr),
Packit c22fc9
				    pkt->hdr->remote_discr);
Packit c22fc9
Packit c22fc9
		return;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* We can't check the TTL any earlier, since we need to know what
Packit c22fc9
	 * is configured for this particular instance */
Packit c22fc9
	if (bfd->max_hops != UCHAR_MAX && bfd_check_packet_ttl(pkt, bfd))
Packit c22fc9
		return;
Packit c22fc9
Packit c22fc9
	/* Authentication is not supported for now */
Packit c22fc9
	if (pkt->hdr->auth != 0) {
Packit c22fc9
		if (__test_bit(LOG_DETAIL_BIT, &debug))
Packit c22fc9
			log_message(LOG_ERR, "Discarding packet from %s"
Packit c22fc9
				    " (auth bit is set, but no authentication"
Packit c22fc9
				    "  is in use)",
Packit c22fc9
				    inet_sockaddrtopair(&pkt->src_addr));
Packit c22fc9
Packit c22fc9
		return;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Discard all packets while in AdminDown state */
Packit c22fc9
	if (bfd->local_state == BFD_STATE_ADMINDOWN) {
Packit c22fc9
		if (__test_bit(LOG_DETAIL_BIT, &debug))
Packit c22fc9
			log_message(LOG_INFO, "Discarding packet from %s"
Packit c22fc9
				    " (session is in AdminDown state)",
Packit c22fc9
				    inet_sockaddrtopair(&pkt->src_addr));
Packit c22fc9
Packit c22fc9
		return;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Save old timers */
Packit c22fc9
	old_remote_rx_intv = bfd->remote_min_rx_intv;
Packit c22fc9
	old_remote_tx_intv = bfd->remote_min_tx_intv;
Packit c22fc9
	old_remote_detect_mult = bfd->remote_detect_mult;
Packit c22fc9
	old_local_detect_time = bfd->local_detect_time ;
Packit c22fc9
	old_local_tx_intv = bfd->local_tx_intv ;
Packit c22fc9
Packit c22fc9
	/* Update state variables */
Packit c22fc9
	bfd->remote_discr = ntohl(pkt->hdr->local_discr);
Packit c22fc9
	bfd->remote_state = pkt->hdr->state;
Packit c22fc9
	bfd->remote_diag = pkt->hdr->diag;
Packit c22fc9
	bfd->remote_min_rx_intv = ntohl(pkt->hdr->min_rx_intv);
Packit c22fc9
	bfd->remote_min_tx_intv = ntohl(pkt->hdr->min_tx_intv);
Packit c22fc9
	bfd->remote_demand = pkt->hdr->demand;
Packit c22fc9
	bfd->remote_detect_mult = pkt->hdr->detect_mult;
Packit c22fc9
Packit c22fc9
	/* Terminate poll sequence */
Packit c22fc9
	if (pkt->hdr->final)
Packit c22fc9
		bfd->poll = 0;
Packit c22fc9
Packit c22fc9
	/*
Packit c22fc9
	 * Recalculate local and remote TX intervals if:
Packit c22fc9
	 *  Control packet with 'Final' bit is received OR
Packit c22fc9
	 *  Control packet with 'Poll' bit is received OR
Packit c22fc9
	 *  Session is not UP
Packit c22fc9
	 */
Packit c22fc9
	if ((bfd->local_state == BFD_STATE_UP &&
Packit c22fc9
	     (pkt->hdr->poll || pkt->hdr->final)) ||
Packit c22fc9
	    bfd->local_state != BFD_STATE_UP) {
Packit c22fc9
		if (bfd->remote_state == BFD_STATE_UP &&
Packit c22fc9
		    (bfd->local_state == BFD_STATE_INIT || bfd->local_state == BFD_STATE_UP))
Packit c22fc9
			bfd_update_local_tx_intv(bfd);
Packit c22fc9
		else
Packit c22fc9
			bfd_idle_local_tx_intv(bfd);
Packit c22fc9
		bfd_update_remote_tx_intv(bfd);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Update the Detection Time */
Packit c22fc9
	bfd->local_detect_time = bfd->remote_detect_mult * bfd->remote_tx_intv;
Packit c22fc9
	bfd->remote_detect_time = bfd->local_detect_mult * bfd->local_tx_intv;
Packit c22fc9
Packit c22fc9
	/* Check if timers are changed */
Packit c22fc9
	if (__test_bit(LOG_EXTRA_DETAIL_BIT, &debug) ||
Packit c22fc9
	    (__test_bit(LOG_DETAIL_BIT, &debug) &&
Packit c22fc9
	     (bfd->remote_min_rx_intv != old_remote_rx_intv ||
Packit c22fc9
	      bfd->remote_min_tx_intv != old_remote_tx_intv ||
Packit c22fc9
	      bfd->remote_detect_mult != old_remote_detect_mult ||
Packit c22fc9
	      bfd->local_tx_intv != old_local_tx_intv)))
Packit c22fc9
		bfd_dump_timers(NULL, bfd);
Packit c22fc9
Packit c22fc9
	/* Reschedule sender if local_tx_intv is being reduced */
Packit c22fc9
	if (bfd->local_tx_intv < old_local_tx_intv &&
Packit c22fc9
	    bfd_sender_scheduled(bfd))
Packit c22fc9
		bfd_sender_reschedule(bfd);
Packit c22fc9
Packit c22fc9
	/* Report detection time changes */
Packit c22fc9
	if (bfd->local_detect_time != old_local_detect_time)
Packit c22fc9
		log_message(LOG_INFO, "BFD_Instance(%s) Detection time"
Packit c22fc9
			    " is %" PRIu64 " ms (was %" PRIu64 " ms)", bfd->iname,
Packit c22fc9
			    bfd->local_detect_time / 1000,
Packit c22fc9
			    old_local_detect_time / 1000);
Packit c22fc9
Packit c22fc9
	/* BFD state machine */
Packit c22fc9
	if (bfd->remote_state == BFD_STATE_ADMINDOWN &&
Packit c22fc9
	    bfd->local_state != BFD_STATE_DOWN)
Packit c22fc9
		bfd_state_down(bfd, BFD_DIAG_NBR_SIGNALLED_DOWN);
Packit c22fc9
	else {
Packit c22fc9
		if (bfd->local_state == BFD_STATE_DOWN) {
Packit c22fc9
			if (bfd->remote_state == BFD_STATE_DOWN)
Packit c22fc9
				bfd_state_init(bfd);
Packit c22fc9
			else if (bfd->remote_state == BFD_STATE_INIT)
Packit c22fc9
				bfd_state_up(bfd);
Packit c22fc9
		} else if (bfd->local_state == BFD_STATE_INIT) {
Packit c22fc9
			if (bfd->remote_state == BFD_STATE_INIT ||
Packit c22fc9
			    bfd->remote_state == BFD_STATE_UP)
Packit c22fc9
				bfd_state_up(bfd);
Packit c22fc9
		} else if (bfd->local_state == BFD_STATE_UP)
Packit c22fc9
			if (bfd->remote_state == BFD_STATE_DOWN)
Packit c22fc9
				bfd_state_down(bfd, BFD_DIAG_NBR_SIGNALLED_DOWN);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	if (bfd->remote_demand &&
Packit c22fc9
	    bfd->local_state == BFD_STATE_UP &&
Packit c22fc9
	    bfd->remote_state == BFD_STATE_UP)
Packit c22fc9
		if (bfd_sender_scheduled(bfd))
Packit c22fc9
			bfd_sender_cancel(bfd);
Packit c22fc9
Packit c22fc9
	if (!bfd->remote_demand ||
Packit c22fc9
	    bfd->local_state != BFD_STATE_UP ||
Packit c22fc9
	    bfd->remote_state != BFD_STATE_UP)
Packit c22fc9
		if (!bfd_sender_scheduled(bfd))
Packit c22fc9
			bfd_sender_schedule(bfd);
Packit c22fc9
Packit c22fc9
	if (pkt->hdr->poll) {
Packit c22fc9
		bfd->final = 1;
Packit c22fc9
		thread_add_event(master, bfd_sender_thread, bfd, 0);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Update last seen timer */
Packit c22fc9
	bfd->last_seen = timer_now();
Packit c22fc9
Packit c22fc9
	/* Delay expiration if scheduled */
Packit c22fc9
	if (bfd->local_state == BFD_STATE_UP &&
Packit c22fc9
	    bfd_expire_scheduled(bfd))
Packit c22fc9
		bfd_expire_reschedule(bfd);
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Reads one packet from input socket */
Packit c22fc9
static int
Packit c22fc9
bfd_receive_packet(bfdpkt_t *pkt, int fd, char *buf, ssize_t bufsz)
Packit c22fc9
{
Packit c22fc9
	ssize_t len;
Packit c22fc9
	unsigned int ttl = 0;
Packit c22fc9
	struct msghdr msg = { 0 };
Packit c22fc9
	struct cmsghdr *cmsg = NULL;
Packit c22fc9
	char cbuf[CMSG_SPACE(sizeof (ttl))] = { 0 };
Packit c22fc9
	struct iovec iov[1] = { {0} };
Packit c22fc9
Packit c22fc9
	assert(pkt);
Packit c22fc9
	assert(fd >= 0);
Packit c22fc9
	assert(buf);
Packit c22fc9
	assert(bufsz);
Packit c22fc9
Packit c22fc9
	iov[0].iov_base = buf;
Packit c22fc9
	iov[0].iov_len = bufsz;
Packit c22fc9
Packit c22fc9
	msg.msg_name = &pkt->src_addr;
Packit c22fc9
	msg.msg_namelen = sizeof (pkt->src_addr);
Packit c22fc9
	msg.msg_iov = iov;
Packit c22fc9
	msg.msg_iovlen = 1;
Packit c22fc9
	msg.msg_control = cbuf;
Packit c22fc9
	msg.msg_controllen = sizeof (cbuf);
Packit c22fc9
Packit c22fc9
	len = recvmsg(fd, &msg, MSG_DONTWAIT);
Packit c22fc9
	if (len == -1) {
Packit c22fc9
		log_message(LOG_ERR, "recvmsg() error (%m)");
Packit c22fc9
		return 1;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	if (msg.msg_flags & MSG_TRUNC) {
Packit c22fc9
		log_message(LOG_WARNING, "recvmsg() message truncated");
Packit c22fc9
		return 1;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	if (msg.msg_flags & MSG_CTRUNC)
Packit c22fc9
		log_message(LOG_WARNING, "recvmsg() control message truncated");
Packit c22fc9
Packit c22fc9
	for (cmsg = CMSG_FIRSTHDR(&msg;; cmsg != NULL;
Packit c22fc9
	     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
Packit c22fc9
		if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) ||
Packit c22fc9
		    (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT))
Packit c22fc9
			ttl = *CMSG_DATA(cmsg);
Packit c22fc9
		else
Packit c22fc9
			log_message(LOG_WARNING, "recvmsg() received"
Packit c22fc9
				    " unexpected control message (level %d type %d)",
Packit c22fc9
				    cmsg->cmsg_level, cmsg->cmsg_type);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	if (!ttl)
Packit c22fc9
		log_message(LOG_WARNING, "recvmsg() returned no TTL control message");
Packit c22fc9
Packit c22fc9
	pkt->hdr = (bfdhdr_t *) buf;
Packit c22fc9
	pkt->len = len;
Packit c22fc9
	pkt->ttl = ttl;
Packit c22fc9
Packit c22fc9
	/* Convert an IPv4-mapped IPv6 address to a real IPv4 address */
Packit c22fc9
	if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&pkt->src_addr)->sin6_addr)) {
Packit c22fc9
		((struct sockaddr_in *)&pkt->src_addr)->sin_addr.s_addr = ((struct sockaddr_in6 *)&pkt->src_addr)->sin6_addr.s6_addr32[3];
Packit c22fc9
		pkt->src_addr.ss_family = AF_INET;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * Reciever thread
Packit c22fc9
 */
Packit c22fc9
Packit c22fc9
/* Runs when data is available in listening socket */
Packit c22fc9
static int
Packit c22fc9
bfd_receiver_thread(thread_t *thread)
Packit c22fc9
{
Packit c22fc9
	bfd_data_t *data;
Packit c22fc9
	bfdpkt_t pkt;
Packit c22fc9
	int fd;
Packit c22fc9
Packit c22fc9
	assert(thread);
Packit c22fc9
Packit c22fc9
	data = THREAD_ARG(thread);
Packit c22fc9
	assert(data);
Packit c22fc9
Packit c22fc9
	fd = thread->u.fd;
Packit c22fc9
	assert(fd >= 0);
Packit c22fc9
Packit c22fc9
	data->thread_in = NULL;
Packit c22fc9
Packit c22fc9
	/* Ignore THREAD_READ_TIMEOUT */
Packit c22fc9
	if (thread->type == THREAD_READY_FD) {
Packit c22fc9
		if (!bfd_receive_packet(&pkt, fd, bfd_buffer, BFD_BUFFER_SIZE))
Packit c22fc9
			bfd_handle_packet(&pkt);
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	data->thread_in =
Packit c22fc9
	    thread_add_read(thread->master, bfd_receiver_thread, data,
Packit c22fc9
			    fd, TIMER_NEVER);
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/*
Packit c22fc9
 * Initialization functions
Packit c22fc9
 */
Packit c22fc9
Packit c22fc9
/* Prepares UDP socket for listening on *:3784 (both IPv4 and IPv6) */
Packit c22fc9
static int
Packit c22fc9
bfd_open_fd_in(bfd_data_t *data)
Packit c22fc9
{
Packit c22fc9
	struct addrinfo hints;
Packit c22fc9
	struct addrinfo *ai_in;
Packit c22fc9
	int ret;
Packit c22fc9
	int yes = 1;
Packit c22fc9
Packit c22fc9
	assert(data);
Packit c22fc9
	assert(data->fd_in == -1);
Packit c22fc9
Packit c22fc9
	memset(&hints, 0, sizeof hints);
Packit c22fc9
	hints.ai_family = AF_INET6;
Packit c22fc9
	hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
Packit c22fc9
	hints.ai_protocol = IPPROTO_UDP;
Packit c22fc9
	hints.ai_socktype = SOCK_DGRAM;
Packit c22fc9
Packit c22fc9
	if ((ret = getaddrinfo(NULL, BFD_CONTROL_PORT, &hints, &ai_in)))
Packit c22fc9
		log_message(LOG_ERR, "getaddrinfo() error (%s)", gai_strerror(ret));
Packit c22fc9
	else if ((data->fd_in = socket(AF_INET6, ai_in->ai_socktype, ai_in->ai_protocol)) == -1)
Packit c22fc9
		log_message(LOG_ERR, "socket() error (%m)");
Packit c22fc9
	else if ((ret = setsockopt(data->fd_in, IPPROTO_IP, IP_RECVTTL, &yes, sizeof (yes))) == -1)
Packit c22fc9
		log_message(LOG_ERR, "setsockopt(IP_RECVTTL) error (%m)");
Packit c22fc9
	else if ((ret = setsockopt(data->fd_in, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof (yes))) == -1)
Packit c22fc9
		log_message(LOG_ERR, "setsockopt(IPV6_RECVHOPLIMIT) error (%m)");
Packit c22fc9
	else if ((ret = bind(data->fd_in, ai_in->ai_addr, ai_in->ai_addrlen)) == -1)
Packit c22fc9
		log_message(LOG_ERR, "bind() error (%m)");
Packit c22fc9
Packit c22fc9
	if (ret)
Packit c22fc9
		ret = 1;
Packit c22fc9
Packit c22fc9
	freeaddrinfo(ai_in);
Packit c22fc9
	return ret;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Prepares UDP socket for sending data to neighbor */
Packit c22fc9
static int
Packit c22fc9
bfd_open_fd_out(bfd_t *bfd)
Packit c22fc9
{
Packit c22fc9
	int ttl;
Packit c22fc9
	int ret;
Packit c22fc9
Packit c22fc9
	assert(bfd);
Packit c22fc9
	assert(bfd->fd_out == -1);
Packit c22fc9
Packit c22fc9
	bfd->fd_out = socket(bfd->nbr_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP);
Packit c22fc9
	if (bfd->fd_out == -1) {
Packit c22fc9
		log_message(LOG_ERR, "BFD_Instance(%s) socket() error (%m)",
Packit c22fc9
			    bfd->iname);
Packit c22fc9
		return 1;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	if (bfd->src_addr.ss_family) {
Packit c22fc9
		ret =
Packit c22fc9
		    bind(bfd->fd_out, (struct sockaddr *) &bfd->src_addr,
Packit c22fc9
			 sizeof (struct sockaddr));
Packit c22fc9
		if (ret == -1) {
Packit c22fc9
			log_message(LOG_ERR,
Packit c22fc9
				    "BFD_Instance(%s) bind() error (%m)",
Packit c22fc9
				    bfd->iname);
Packit c22fc9
			return 1;
Packit c22fc9
		}
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	ttl = bfd->ttl;
Packit c22fc9
	if (bfd->nbr_addr.ss_family == AF_INET)
Packit c22fc9
		ret = setsockopt(bfd->fd_out, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl));
Packit c22fc9
	else
Packit c22fc9
		ret = setsockopt(bfd->fd_out, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl));
Packit c22fc9
Packit c22fc9
	if (ret == -1) {
Packit c22fc9
		log_message(LOG_ERR, "BFD_Instance(%s) setsockopt() "
Packit c22fc9
			    " error (%m)", bfd->iname);
Packit c22fc9
		return 1;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Opens all needed sockets */
Packit c22fc9
static int
Packit c22fc9
bfd_open_fds(bfd_data_t *data)
Packit c22fc9
{
Packit c22fc9
	bfd_t *bfd;
Packit c22fc9
	element e;
Packit c22fc9
Packit c22fc9
	assert(data);
Packit c22fc9
	assert(data->bfd);
Packit c22fc9
Packit c22fc9
	/* Do not reopen input socket on reload */
Packit c22fc9
	if (bfd_data->fd_in == -1) {
Packit c22fc9
		if (bfd_open_fd_in(data)) {
Packit c22fc9
			log_message(LOG_ERR, "Unable to open listening socket");
Packit c22fc9
Packit c22fc9
			/* There is no point to stay alive w/o listening socket */
Packit c22fc9
			return 1;
Packit c22fc9
		}
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	for (e = LIST_HEAD(data->bfd); e; ELEMENT_NEXT(e)) {
Packit c22fc9
		bfd = ELEMENT_DATA(e);
Packit c22fc9
		assert(bfd);
Packit c22fc9
Packit c22fc9
		if (bfd_open_fd_out(bfd)) {
Packit c22fc9
			log_message(LOG_ERR, "BFD_Instance(%s) Unable to"
Packit c22fc9
				    " open output socket, disabling instance",
Packit c22fc9
				    bfd->iname);
Packit c22fc9
			bfd_state_admindown(bfd);
Packit c22fc9
		}
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Registers sender and receiver threads */
Packit c22fc9
static void
Packit c22fc9
bfd_register_workers(bfd_data_t *data)
Packit c22fc9
{
Packit c22fc9
	bfd_t *bfd;
Packit c22fc9
	element e;
Packit c22fc9
Packit c22fc9
	assert(data);
Packit c22fc9
	assert(!data->thread_in);
Packit c22fc9
Packit c22fc9
	/* Set timeout to not expire */
Packit c22fc9
	data->thread_in = thread_add_read(master, bfd_receiver_thread,
Packit c22fc9
					  data, data->fd_in, TIMER_NEVER);
Packit c22fc9
Packit c22fc9
	/* Resume or schedule threads */
Packit c22fc9
	for (e = LIST_HEAD(data->bfd); e; ELEMENT_NEXT(e)) {
Packit c22fc9
		bfd = ELEMENT_DATA(e);
Packit c22fc9
Packit c22fc9
		/* Do not start anything if instance is in AdminDown state.
Packit c22fc9
		   Discard saved state if any */
Packit c22fc9
		if (bfd_sender_suspended(bfd)) {
Packit c22fc9
			if (BFD_ISADMINDOWN(bfd))
Packit c22fc9
				bfd_sender_discard(bfd);
Packit c22fc9
			else
Packit c22fc9
				bfd_sender_resume(bfd);
Packit c22fc9
		} else if (!BFD_ISADMINDOWN(bfd) && !bfd->passive)
Packit c22fc9
			bfd_sender_schedule(bfd);
Packit c22fc9
Packit c22fc9
		if (bfd_expire_suspended(bfd)) {
Packit c22fc9
			if (BFD_ISADMINDOWN(bfd))
Packit c22fc9
				bfd_expire_discard(bfd);
Packit c22fc9
			else
Packit c22fc9
				bfd_expire_resume(bfd);
Packit c22fc9
		}
Packit c22fc9
Packit c22fc9
		if (bfd_reset_suspended(bfd)) {
Packit c22fc9
			if (BFD_ISADMINDOWN(bfd))
Packit c22fc9
				bfd_reset_discard(bfd);
Packit c22fc9
			else
Packit c22fc9
				bfd_reset_resume(bfd);
Packit c22fc9
		}
Packit c22fc9
Packit c22fc9
		/* Send our status to VRRP process */
Packit c22fc9
		bfd_event_send(bfd);
Packit c22fc9
Packit c22fc9
		/* If we are starting up, send a packet */
Packit c22fc9
		if (!reload && !bfd->passive)
Packit c22fc9
			thread_add_event(master, bfd_sender_thread, bfd, 0);
Packit c22fc9
	}
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Suspends threads, closes sockets */
Packit c22fc9
void
Packit c22fc9
bfd_dispatcher_release(bfd_data_t *data)
Packit c22fc9
{
Packit c22fc9
	bfd_t *bfd;
Packit c22fc9
	element e;
Packit c22fc9
Packit c22fc9
	assert(data);
Packit c22fc9
Packit c22fc9
	/* Looks like dispatcher wasn't initialized yet
Packit c22fc9
	   This can happen is case of a configuration error */
Packit c22fc9
	if (!data->thread_in)
Packit c22fc9
		return;
Packit c22fc9
Packit c22fc9
	assert(data->fd_in != -1);
Packit c22fc9
Packit c22fc9
	thread_cancel(data->thread_in);
Packit c22fc9
	data->thread_in = NULL;
Packit c22fc9
Packit c22fc9
	/* Do not close fd_in on reload */
Packit c22fc9
	if (!reload) {
Packit c22fc9
		close(data->fd_in);
Packit c22fc9
		data->fd_in = -1;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	/* Suspend threads for possible resuming after reconfiguration */
Packit c22fc9
	set_time_now();
Packit c22fc9
	for (e = LIST_HEAD(data->bfd); e; ELEMENT_NEXT(e)) {
Packit c22fc9
		bfd = ELEMENT_DATA(e);
Packit c22fc9
Packit c22fc9
		if (bfd_sender_scheduled(bfd))
Packit c22fc9
			bfd_sender_suspend(bfd);
Packit c22fc9
Packit c22fc9
		if (bfd_expire_scheduled(bfd))
Packit c22fc9
			bfd_expire_suspend(bfd);
Packit c22fc9
Packit c22fc9
		if (bfd_reset_scheduled(bfd))
Packit c22fc9
			bfd_reset_suspend(bfd);
Packit c22fc9
Packit c22fc9
		assert(bfd->fd_out != -1);
Packit c22fc9
Packit c22fc9
		close(bfd->fd_out);
Packit c22fc9
		bfd->fd_out = -1;
Packit c22fc9
	}
Packit c22fc9
Packit c22fc9
	cancel_signal_read_thread();
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
/* Starts BFD dispatcher */
Packit c22fc9
int
Packit c22fc9
bfd_dispatcher_init(thread_t *thread)
Packit c22fc9
{
Packit c22fc9
	bfd_data_t *data;
Packit c22fc9
Packit c22fc9
	assert(thread);
Packit c22fc9
Packit c22fc9
	data = THREAD_ARG(thread);
Packit c22fc9
	if (bfd_open_fds(data))
Packit c22fc9
		exit(EXIT_FAILURE);
Packit c22fc9
Packit c22fc9
	bfd_register_workers(data);
Packit c22fc9
Packit c22fc9
	return 0;
Packit c22fc9
}
Packit c22fc9
Packit c22fc9
Packit c22fc9
#ifdef THREAD_DUMP
Packit c22fc9
void
Packit c22fc9
register_bfd_scheduler_addresses(void)
Packit c22fc9
{
Packit c22fc9
	register_thread_address("bfd_sender_thread", bfd_sender_thread);
Packit c22fc9
	register_thread_address("bfd_expire_thread", bfd_expire_thread);
Packit c22fc9
	register_thread_address("bfd_reset_thread", bfd_reset_thread);
Packit c22fc9
	register_thread_address("bfd_receiver_thread", bfd_receiver_thread);
Packit c22fc9
}
Packit c22fc9
#endif