frr/babeld/neighbour.c

355 lines
9.7 KiB
C

// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
#include <zebra.h>
#include "if.h"
#include "babel_main.h"
#include "babeld.h"
#include "util.h"
#include "babel_interface.h"
#include "neighbour.h"
#include "source.h"
#include "route.h"
#include "message.h"
#include "resend.h"
#include "babel_errors.h"
struct neighbour *neighs = NULL;
static struct neighbour *
find_neighbour_nocreate(const unsigned char *address, struct interface *ifp)
{
struct neighbour *neigh;
FOR_ALL_NEIGHBOURS(neigh) {
if(memcmp(address, neigh->address, 16) == 0 &&
neigh->ifp == ifp)
return neigh;
}
return NULL;
}
void
flush_neighbour(struct neighbour *neigh)
{
debugf(BABEL_DEBUG_COMMON,"Flushing neighbour %s (reach 0x%04x)",
format_address(neigh->address), neigh->reach);
flush_neighbour_routes(neigh);
if(unicast_neighbour == neigh)
flush_unicast(1);
flush_resends(neigh);
if(neighs == neigh) {
neighs = neigh->next;
} else {
struct neighbour *previous = neighs;
while(previous->next != neigh)
previous = previous->next;
previous->next = neigh->next;
}
free(neigh);
}
struct neighbour *
find_neighbour(const unsigned char *address, struct interface *ifp)
{
struct neighbour *neigh;
const struct timeval zero = {0, 0};
neigh = find_neighbour_nocreate(address, ifp);
if(neigh)
return neigh;
debugf(BABEL_DEBUG_COMMON,"Creating neighbour %s on %s.",
format_address(address), ifp->name);
neigh = malloc(sizeof(struct neighbour));
if(neigh == NULL) {
flog_err(EC_BABEL_MEMORY, "malloc(neighbour): %s",
safe_strerror(errno));
return NULL;
}
neigh->hello_seqno = -1;
memcpy(neigh->address, address, 16);
neigh->reach = 0;
neigh->txcost = INFINITY;
neigh->ihu_time = babel_now;
neigh->hello_time = zero;
neigh->hello_interval = 0;
neigh->ihu_interval = 0;
neigh->hello_send_us = 0;
neigh->hello_rtt_receive_time = zero;
neigh->rtt = 0;
neigh->rtt_time = zero;
neigh->ifp = ifp;
neigh->next = neighs;
neighs = neigh;
send_hello(ifp);
return neigh;
}
/* Recompute a neighbour's rxcost. Return true if anything changed. */
int
update_neighbour(struct neighbour *neigh, int hello, int hello_interval)
{
int missed_hellos;
int rc = 0;
if(hello < 0) {
if(neigh->hello_interval == 0)
return rc;
missed_hellos =
((int)timeval_minus_msec(&babel_now, &neigh->hello_time) -
neigh->hello_interval * 7) /
(neigh->hello_interval * 10);
if(missed_hellos <= 0)
return rc;
timeval_add_msec(&neigh->hello_time, &neigh->hello_time,
missed_hellos * neigh->hello_interval * 10);
} else {
if(neigh->hello_seqno >= 0 && neigh->reach > 0) {
missed_hellos = seqno_minus(hello, neigh->hello_seqno) - 1;
if(missed_hellos < -8) {
/* Probably a neighbour that rebooted and lost its seqno.
Reboot the universe. */
neigh->reach = 0;
missed_hellos = 0;
rc = 1;
} else if(missed_hellos < 0) {
if(hello_interval > neigh->hello_interval) {
/* This neighbour has increased its hello interval,
and we didn't notice. */
neigh->reach <<= -missed_hellos;
missed_hellos = 0;
} else {
/* Late hello. Probably due to the link layer buffering
packets during a link outage. Ignore it, but reset
the expected seqno. */
neigh->hello_seqno = hello;
hello = -1;
missed_hellos = 0;
}
rc = 1;
}
} else {
missed_hellos = 0;
}
neigh->hello_time = babel_now;
neigh->hello_interval = hello_interval;
}
if(missed_hellos > 0) {
neigh->reach >>= missed_hellos;
neigh->hello_seqno = seqno_plus(neigh->hello_seqno, missed_hellos);
rc = 1;
}
if(hello >= 0) {
neigh->hello_seqno = hello;
neigh->reach >>= 1;
neigh->reach |= 0x8000;
if((neigh->reach & 0xFC00) != 0xFC00)
rc = 1;
}
/* Make sure to give neighbours some feedback early after association */
if((neigh->reach & 0xBF00) == 0x8000) {
/* A new neighbour */
send_hello(neigh->ifp);
} else {
/* Don't send hellos, in order to avoid a positive feedback loop. */
int a = (neigh->reach & 0xC000);
int b = (neigh->reach & 0x3000);
if((a == 0xC000 && b == 0) || (a == 0 && b == 0x3000)) {
/* Reachability is either 1100 or 0011 */
send_self_update(neigh->ifp);
}
}
if((neigh->reach & 0xFC00) == 0xC000) {
/* This is a newish neighbour, let's request a full route dump.
We ought to avoid this when the network is dense */
send_unicast_request(neigh, NULL, 0);
send_ihu(neigh, NULL);
}
return rc;
}
static int
reset_txcost(struct neighbour *neigh)
{
unsigned delay;
delay = timeval_minus_msec(&babel_now, &neigh->ihu_time);
if(neigh->ihu_interval > 0 && delay < neigh->ihu_interval * 10U * 3U)
return 0;
/* If we're losing a lot of packets, we probably lost an IHU too */
if(delay >= 180000 || (neigh->reach & 0xFFF0) == 0 ||
(neigh->ihu_interval > 0 &&
delay >= neigh->ihu_interval * 10U * 10U)) {
neigh->txcost = INFINITY;
neigh->ihu_time = babel_now;
return 1;
}
return 0;
}
unsigned
neighbour_txcost(struct neighbour *neigh)
{
return neigh->txcost;
}
unsigned
check_neighbours(void)
{
struct neighbour *neigh;
int changed, rc;
unsigned msecs = 50000;
debugf(BABEL_DEBUG_COMMON,"Checking neighbours.");
neigh = neighs;
while(neigh) {
changed = update_neighbour(neigh, -1, 0);
if(neigh->reach == 0 ||
neigh->hello_time.tv_sec > babel_now.tv_sec || /* clock stepped */
timeval_minus_msec(&babel_now, &neigh->hello_time) > 300000) {
struct neighbour *old = neigh;
neigh = neigh->next;
flush_neighbour(old);
continue;
}
rc = reset_txcost(neigh);
changed = changed || rc;
update_neighbour_metric(neigh, changed);
if(neigh->hello_interval > 0)
msecs = MIN(msecs, neigh->hello_interval * 10U);
if(neigh->ihu_interval > 0)
msecs = MIN(msecs, neigh->ihu_interval * 10U);
neigh = neigh->next;
}
return msecs;
}
unsigned
neighbour_rxcost(struct neighbour *neigh)
{
unsigned delay;
unsigned short reach = neigh->reach;
delay = timeval_minus_msec(&babel_now, &neigh->hello_time);
if((reach & 0xFFF0) == 0 || delay >= 180000) {
return INFINITY;
} else if(babel_get_if_nfo(neigh->ifp)->flags & BABEL_IF_LQ) {
int sreach =
((reach & 0x8000) >> 2) +
((reach & 0x4000) >> 1) +
(reach & 0x3FFF);
/* 0 <= sreach <= 0x7FFF */
int cost = (0x8000 * babel_get_if_nfo(neigh->ifp)->cost) / (sreach + 1);
/* cost >= interface->cost */
if(delay >= 40000)
cost = (cost * (delay - 20000) + 10000) / 20000;
return MIN(cost, INFINITY);
} else {
/* To lose one hello is a misfortune, to lose two is carelessness. */
if((reach & 0xC000) == 0xC000)
return babel_get_if_nfo(neigh->ifp)->cost;
else if((reach & 0xC000) == 0)
return INFINITY;
else if((reach & 0x2000))
return babel_get_if_nfo(neigh->ifp)->cost;
else
return INFINITY;
}
}
unsigned
neighbour_rttcost(struct neighbour *neigh)
{
struct interface *ifp = neigh->ifp;
babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
if(!babel_ifp->max_rtt_penalty || !valid_rtt(neigh))
return 0;
/* Function: linear behaviour between rtt_min and rtt_max. */
if(neigh->rtt <= babel_ifp->rtt_min) {
return 0;
} else if(neigh->rtt <= babel_ifp->rtt_max) {
unsigned long long tmp =
(unsigned long long)babel_ifp->max_rtt_penalty *
(neigh->rtt - babel_ifp->rtt_min) /
(babel_ifp->rtt_max - babel_ifp->rtt_min);
assert((tmp & 0x7FFFFFFF) == tmp);
return tmp;
} else {
return babel_ifp->max_rtt_penalty;
}
}
unsigned
neighbour_cost(struct neighbour *neigh)
{
unsigned a, b, cost;
if(!if_up(neigh->ifp))
return INFINITY;
a = neighbour_txcost(neigh);
if(a >= INFINITY)
return INFINITY;
b = neighbour_rxcost(neigh);
if(b >= INFINITY)
return INFINITY;
if(!(babel_get_if_nfo(neigh->ifp)->flags & BABEL_IF_LQ)
|| (a < 256 && b < 256)) {
cost = a;
} else {
/* a = 256/alpha, b = 256/beta, where alpha and beta are the expected
probabilities of a packet getting through in the direct and reverse
directions. */
a = MAX(a, 256);
b = MAX(b, 256);
/* 1/(alpha * beta), which is just plain ETX. */
/* Since a and b are capped to 16 bits, overflow is impossible. */
cost = (a * b + 128) >> 8;
}
cost += neighbour_rttcost(neigh);
return MIN(cost, INFINITY);
}
int
valid_rtt(struct neighbour *neigh)
{
return (timeval_minus_msec(&babel_now, &neigh->rtt_time) < 180000) ? 1 : 0;
}