// SPDX-License-Identifier: MIT
/*
Copyright (c) 2007, 2008 by Juliusz Chroboczek
Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek
*/

#include <zebra.h>
#include "if.h"

#include "babeld.h"
#include "util.h"
#include "kernel.h"
#include "babel_interface.h"
#include "source.h"
#include "neighbour.h"
#include "route.h"
#include "xroute.h"
#include "message.h"
#include "resend.h"
#include "babel_errors.h"

static void consider_route(struct babel_route *route);

struct babel_route **routes = NULL;
static int route_slots = 0, max_route_slots = 0;
int kernel_metric = 0;
enum babel_diversity diversity_kind = DIVERSITY_NONE;
int diversity_factor = BABEL_DEFAULT_DIVERSITY_FACTOR;
int keep_unfeasible = 0;

int smoothing_half_life = 0;
static int two_to_the_one_over_hl = 0; /* 2^(1/hl) * 0x10000 */

/* We maintain a list of "slots", ordered by prefix.  Every slot
   contains a linked list of the routes to this prefix, with the
   installed route, if any, at the head of the list. */

static int route_compare(const unsigned char *prefix, unsigned char plen, struct babel_route *route)
{
	int i = memcmp(prefix, route->src->prefix, 16);
	if (i != 0)
		return i;

	if (plen < route->src->plen)
		return -1;
	else if (plen > route->src->plen)
		return 1;
	else
		return 0;
}

/* Performs binary search, returns -1 in case of failure.  In the latter
   case, new_return is the place where to insert the new element. */

static int find_route_slot(const unsigned char *prefix, unsigned char plen, int *new_return)
{
	int p, m, g, c;

	if (route_slots < 1) {
		if (new_return)
			*new_return = 0;
		return -1;
	}

	p = 0;
	g = route_slots - 1;

	do {
		m = (p + g) / 2;
		c = route_compare(prefix, plen, routes[m]);
		if (c == 0)
			return m;
		else if (c < 0)
			g = m - 1;
		else
			p = m + 1;
	} while (p <= g);

	if (new_return)
		*new_return = p;

	return -1;
}

struct babel_route *find_route(const unsigned char *prefix, unsigned char plen,
			       struct neighbour *neigh, const unsigned char *nexthop)
{
	struct babel_route *route;
	int i = find_route_slot(prefix, plen, NULL);

	if (i < 0)
		return NULL;

	route = routes[i];

	while (route) {
		if (route->neigh == neigh && memcmp(route->nexthop, nexthop, 16) == 0)
			return route;
		route = route->next;
	}

	return NULL;
}

struct babel_route *find_installed_route(const unsigned char *prefix, unsigned char plen)
{
	int i = find_route_slot(prefix, plen, NULL);

	if (i >= 0 && routes[i]->installed)
		return routes[i];

	return NULL;
}

/* Returns an overestimate of the number of installed routes. */
int installed_routes_estimate(void)
{
	return route_slots;
}

static int resize_route_table(int new_slots)
{
	struct babel_route **new_routes;
	assert(new_slots >= route_slots);

	if (new_slots == 0) {
		new_routes = NULL;
		free(routes);
	} else {
		new_routes = realloc(routes, new_slots * sizeof(struct babel_route *));
		if (new_routes == NULL)
			return -1;
	}

	max_route_slots = new_slots;
	routes = new_routes;
	return 1;
}

/* Insert a route into the table.  If successful, retains the route.
   On failure, caller must free the route. */
static struct babel_route *insert_route(struct babel_route *route)
{
	int i, n = 0;

	assert(!route->installed);

	i = find_route_slot(route->src->prefix, route->src->plen, &n);

	if (i < 0) {
		if (route_slots >= max_route_slots)
			resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots);
		if (route_slots >= max_route_slots)
			return NULL;
		assert(routes);
		route->next = NULL;
		if (n < route_slots)
			memmove(routes + n + 1, routes + n,
				(route_slots - n) * sizeof(struct babel_route *));
		route_slots++;
		routes[n] = route;
	} else {
		struct babel_route *r;
		r = routes[i];
		while (r->next)
			r = r->next;
		r->next = route;
		route->next = NULL;
	}

	return route;
}

void flush_route(struct babel_route *route)
{
	int i;
	struct source *src;
	unsigned oldmetric;
	int lost = 0;

	oldmetric = route_metric(route);
	src = route->src;

	if (route->installed) {
		uninstall_route(route);
		lost = 1;
	}

	i = find_route_slot(route->src->prefix, route->src->plen, NULL);
	assert(i >= 0 && i < route_slots);

	if (route == routes[i]) {
		routes[i] = route->next;
		route->next = NULL;
		free(route);

		if (routes[i] == NULL) {
			if (i < route_slots - 1)
				memmove(routes + i, routes + i + 1,
					(route_slots - i - 1) * sizeof(struct babel_route *));
			routes[route_slots - 1] = NULL;
			route_slots--;
		}

		if (route_slots == 0)
			resize_route_table(0);
		else if (max_route_slots > 8 && route_slots < max_route_slots / 4)
			resize_route_table(max_route_slots / 2);
	} else {
		struct babel_route *r = routes[i];
		while (r->next != route)
			r = r->next;
		r->next = route->next;
		route->next = NULL;
		free(route);
	}

	if (lost)
		route_lost(src, oldmetric);

	release_source(src);
}

void flush_all_routes(void)
{
	int i;

	/* Start from the end, to avoid shifting the table. */
	i = route_slots - 1;
	while (i >= 0) {
		while (i < route_slots) {
			/* Uninstall first, to avoid calling route_lost. */
			if (routes[i]->installed)
				uninstall_route(routes[i]);
			flush_route(routes[i]);
		}
		i--;
	}

	check_sources_released();
}

void flush_neighbour_routes(struct neighbour *neigh)
{
	int i;

	i = 0;
	while (i < route_slots) {
		struct babel_route *r;
		r = routes[i];
		while (r) {
			if (r->neigh == neigh) {
				flush_route(r);
				goto again;
			}
			r = r->next;
		}
		i++;
again:;
	}
}

void flush_interface_routes(struct interface *ifp, int v4only)
{
	int i;

	i = 0;
	while (i < route_slots) {
		struct babel_route *r;
		r = routes[i];
		while (r) {
			if (r->neigh->ifp == ifp && (!v4only || v4mapped(r->nexthop))) {
				flush_route(r);
				goto again;
			}
			r = r->next;
		}
		i++;
again:;
	}
}

struct route_stream {
	int installed;
	int index;
	struct babel_route *next;
};


struct route_stream *route_stream(int installed)
{
	struct route_stream *stream;

	stream = malloc(sizeof(struct route_stream));
	if (stream == NULL)
		return NULL;

	stream->installed = installed;
	stream->index = installed ? 0 : -1;
	stream->next = NULL;

	return stream;
}

struct babel_route *route_stream_next(struct route_stream *stream)
{
	if (stream->installed) {
		while (stream->index < route_slots && !routes[stream->index]->installed)
			stream->index++;

		if (stream->index < route_slots)
			return routes[stream->index++];
		else
			return NULL;
	} else {
		struct babel_route *next;
		if (!stream->next) {
			stream->index++;
			if (stream->index >= route_slots)
				return NULL;
			stream->next = routes[stream->index];
		}
		next = stream->next;
		stream->next = next->next;
		return next;
	}
}

void route_stream_done(struct route_stream *stream)
{
	free(stream);
}

static int metric_to_kernel(int metric)
{
	return metric < INFINITY ? metric : KERNEL_INFINITY;
}

/* This is used to maintain the invariant that the installed route is at
   the head of the list. */
static void move_installed_route(struct babel_route *route, int i)
{
	assert(i >= 0 && i < route_slots);
	assert(route->installed);

	if (route != routes[i]) {
		struct babel_route *r = routes[i];
		while (r->next != route)
			r = r->next;
		r->next = route->next;
		route->next = routes[i];
		routes[i] = route;
	}
}

void install_route(struct babel_route *route)
{
	int i, rc;

	if (route->installed)
		return;

	if (!route_feasible(route))
		flog_err(EC_BABEL_ROUTE, "Installing unfeasible route (this shouldn't happen).");

	i = find_route_slot(route->src->prefix, route->src->plen, NULL);
	assert(i >= 0 && i < route_slots);

	if (routes[i] != route && routes[i]->installed) {
		flog_err(EC_BABEL_ROUTE,
			 "Attempting to install duplicate route (this shouldn't happen).");
		return;
	}

	rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen, route->nexthop,
			  route->neigh->ifp->ifindex, metric_to_kernel(route_metric(route)), NULL,
			  0, 0);
	if (rc < 0) {
		int save = errno;
		flog_err(EC_BABEL_ROUTE, "kernel_route(ADD): %s", safe_strerror(errno));
		if (save != EEXIST)
			return;
	}
	route->installed = 1;
	move_installed_route(route, i);
}

void uninstall_route(struct babel_route *route)
{
	int rc;

	if (!route->installed)
		return;

	rc = kernel_route(ROUTE_FLUSH, route->src->prefix, route->src->plen, route->nexthop,
			  route->neigh->ifp->ifindex, metric_to_kernel(route_metric(route)), NULL,
			  0, 0);
	if (rc < 0)
		flog_err(EC_BABEL_ROUTE, "kernel_route(FLUSH): %s", safe_strerror(errno));

	route->installed = 0;
}

/* This is equivalent to uninstall_route followed with install_route,
   but without the race condition.  The destination of both routes
   must be the same. */

static void switch_routes(struct babel_route *old, struct babel_route *new)
{
	int rc;

	if (!old) {
		install_route(new);
		return;
	}

	if (!old->installed)
		return;

	if (!route_feasible(new))
		flog_err(EC_BABEL_ROUTE, "Switching to unfeasible route (this shouldn't happen).");

	rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen, old->nexthop,
			  old->neigh->ifp->ifindex, metric_to_kernel(route_metric(old)),
			  new->nexthop, new->neigh->ifp->ifindex,
			  metric_to_kernel(route_metric(new)));
	if (rc < 0) {
		flog_err(EC_BABEL_ROUTE, "kernel_route(MODIFY): %s", safe_strerror(errno));
		return;
	}

	old->installed = 0;
	new->installed = 1;
	move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen, NULL));
}

static void change_route_metric(struct babel_route *route, unsigned refmetric, unsigned cost,
				unsigned add)
{
	int old, new;
	int newmetric = MIN(refmetric + cost + add, INFINITY);

	old = metric_to_kernel(route_metric(route));
	new = metric_to_kernel(newmetric);

	if (route->installed && old != new) {
		int rc;
		rc = kernel_route(ROUTE_MODIFY, route->src->prefix, route->src->plen,
				  route->nexthop, route->neigh->ifp->ifindex, old, route->nexthop,
				  route->neigh->ifp->ifindex, new);
		if (rc < 0) {
			flog_err(EC_BABEL_ROUTE, "kernel_route(MODIFY metric): %s",
				 safe_strerror(errno));
			return;
		}
	}

	/* Update route->smoothed_metric using the old metric. */
	route_smoothed_metric(route);

	route->refmetric = refmetric;
	route->cost = cost;
	route->add_metric = add;

	if (smoothing_half_life == 0) {
		route->smoothed_metric = route_metric(route);
		route->smoothed_metric_time = babel_now.tv_sec;
	}
}

static void retract_route(struct babel_route *route)
{
	/* We cannot simply remove the route from the kernel, as that might
       cause a routing loop -- see RFC 6126 Sections 2.8 and 3.5.5. */
	change_route_metric(route, INFINITY, INFINITY, 0);
}

int route_feasible(struct babel_route *route)
{
	return update_feasible(route->src, route->seqno, route->refmetric);
}

int route_old(struct babel_route *route)
{
	return route->time < babel_now.tv_sec - route->hold_time * 7 / 8;
}

int route_expired(struct babel_route *route)
{
	return route->time < babel_now.tv_sec - route->hold_time;
}

static int channels_interfere(int ch1, int ch2)
{
	if (ch1 == BABEL_IF_CHANNEL_NONINTERFERING || ch2 == BABEL_IF_CHANNEL_NONINTERFERING)
		return 0;
	if (ch1 == BABEL_IF_CHANNEL_INTERFERING || ch2 == BABEL_IF_CHANNEL_INTERFERING)
		return 1;
	return ch1 == ch2;
}

int route_interferes(struct babel_route *route, struct interface *ifp)
{
	struct babel_interface *babel_ifp = NULL;
	switch (diversity_kind) {
	case DIVERSITY_NONE:
		return 1;
	case DIVERSITY_INTERFACE_1:
		return route->neigh->ifp == ifp;
	case DIVERSITY_CHANNEL_1:
	case DIVERSITY_CHANNEL:
		if (route->neigh->ifp == ifp)
			return 1;
		babel_ifp = babel_get_if_nfo(ifp);
		if (channels_interfere(babel_ifp->channel,
				       babel_get_if_nfo(route->neigh->ifp)->channel))
			return 1;
		if (diversity_kind == DIVERSITY_CHANNEL) {
			int i;
			for (i = 0; i < DIVERSITY_HOPS; i++) {
				if (route->channels[i] == 0)
					break;
				if (channels_interfere(babel_ifp->channel, route->channels[i]))
					return 1;
			}
		}
		return 0;
	}

	return 1;
}

int update_feasible(struct source *src, unsigned short seqno, unsigned short refmetric)
{
	if (src == NULL)
		return 1;

	if (src->time < babel_now.tv_sec - SOURCE_GC_TIME)
		/* Never mind what is probably stale data */
		return 1;

	if (refmetric >= INFINITY)
		/* Retractions are always feasible */
		return 1;

	return (seqno_compare(seqno, src->seqno) > 0 ||
		(src->seqno == seqno && refmetric < src->metric));
}

void change_smoothing_half_life(int half_life)
{
	if (half_life <= 0) {
		smoothing_half_life = 0;
		two_to_the_one_over_hl = 0;
		return;
	}

	smoothing_half_life = half_life;
	switch (smoothing_half_life) {
	case 1:
		two_to_the_one_over_hl = 131072;
		break;
	case 2:
		two_to_the_one_over_hl = 92682;
		break;
	case 3:
		two_to_the_one_over_hl = 82570;
		break;
	case 4:
		two_to_the_one_over_hl = 77935;
		break;
	default:
		/* 2^(1/x) is 1 + log(2)/x + O(1/x^2) at infinity. */
		two_to_the_one_over_hl = 0x10000 + 45426 / half_life;
	}
}

/* Update the smoothed metric, return the new value. */
int route_smoothed_metric(struct babel_route *route)
{
	int metric = route_metric(route);

	if (smoothing_half_life <= 0 ||			      /* no smoothing */
	    metric >= INFINITY ||			      /* route retracted */
	    route->smoothed_metric_time > babel_now.tv_sec || /* clock stepped */
	    route->smoothed_metric == metric) {		      /* already converged */
		route->smoothed_metric = metric;
		route->smoothed_metric_time = babel_now.tv_sec;
	} else {
		int diff;
		/* We randomise the computation, to minimise global synchronisation
           and hence oscillations. */
		while (route->smoothed_metric_time <= babel_now.tv_sec - smoothing_half_life) {
			diff = metric - route->smoothed_metric;
			route->smoothed_metric += roughly(diff) / 2;
			route->smoothed_metric_time += smoothing_half_life;
		}
		while (route->smoothed_metric_time < babel_now.tv_sec) {
			diff = metric - route->smoothed_metric;
			route->smoothed_metric += roughly(diff) *
						  (two_to_the_one_over_hl - 0x10000) / 0x10000;
			route->smoothed_metric_time++;
		}

		diff = metric - route->smoothed_metric;
		if (diff > -4 && diff < 4)
			route->smoothed_metric = metric;
	}

	/* change_route_metric relies on this */
	assert(route->smoothed_metric_time == babel_now.tv_sec);
	return route->smoothed_metric;
}

static int route_acceptable(struct babel_route *route, int feasible, struct neighbour *exclude)
{
	if (route_expired(route))
		return 0;
	if (feasible && !route_feasible(route))
		return 0;
	if (exclude && route->neigh == exclude)
		return 0;
	return 1;
}

/* Find the best route according to the weak ordering.  Any
   linearisation of the strong ordering (see consider_route) will do,
   we use sm <= sm'.  We could probably use a lexical ordering, but
   that's probably overkill. */

struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen, int feasible,
				    struct neighbour *exclude)
{
	struct babel_route *route = NULL, *r = NULL;
	int i = find_route_slot(prefix, plen, NULL);

	if (i < 0)
		return NULL;

	route = routes[i];
	while (route && !route_acceptable(route, feasible, exclude))
		route = route->next;

	if (!route)
		return NULL;

	r = route->next;
	while (r) {
		if (route_acceptable(r, feasible, exclude) &&
		    (route_smoothed_metric(r) < route_smoothed_metric(route)))
			route = r;
		r = r->next;
	}

	return route;
}

void update_route_metric(struct babel_route *route)
{
	int oldmetric = route_metric(route);
	int old_smoothed_metric = route_smoothed_metric(route);

	if (route_expired(route)) {
		if (route->refmetric < INFINITY) {
			route->seqno = seqno_plus(route->src->seqno, 1);
			retract_route(route);
			if (oldmetric < INFINITY)
				route_changed(route, route->src, oldmetric);
		}
	} else {
		struct neighbour *neigh = route->neigh;
		int add_metric = input_filter(route->src->id, route->src->prefix, route->src->plen,
					      neigh->address, neigh->ifp->ifindex);
		change_route_metric(route, route->refmetric, neighbour_cost(route->neigh),
				    add_metric);
		if (route_metric(route) != oldmetric ||
		    route_smoothed_metric(route) != old_smoothed_metric)
			route_changed(route, route->src, oldmetric);
	}
}

/* Called whenever a neighbour's cost changes, to update the metric of
   all routes through that neighbour. */
void update_neighbour_metric(struct neighbour *neigh, int changed)
{
	if (changed) {
		int i;

		for (i = 0; i < route_slots; i++) {
			struct babel_route *r = routes[i];
			while (r) {
				if (r->neigh == neigh)
					update_route_metric(r);
				r = r->next;
			}
		}
	}
}

void update_interface_metric(struct interface *ifp)
{
	int i;

	for (i = 0; i < route_slots; i++) {
		struct babel_route *r = routes[i];
		while (r) {
			if (r->neigh->ifp == ifp)
				update_route_metric(r);
			r = r->next;
		}
	}
}

/* This is called whenever we receive an update. */
struct babel_route *update_route(const unsigned char *router_id, const unsigned char *prefix,
				 unsigned char plen, unsigned short seqno,
				 unsigned short refmetric, unsigned short interval,
				 struct neighbour *neigh, const unsigned char *nexthop,
				 const unsigned char *channels, int channels_len)
{
	struct babel_route *route;
	struct source *src;
	int metric, feasible;
	int add_metric;
	int hold_time = MAX((4 * interval) / 100 + interval / 50, 15);

	if (memcmp(router_id, myid, 8) == 0)
		return NULL;

	if (martian_prefix(prefix, plen)) {
		flog_err(EC_BABEL_ROUTE, "Rejecting martian route to %s through %s.",
			 format_prefix(prefix, plen), format_address(nexthop));
		return NULL;
	}

	add_metric = input_filter(router_id, prefix, plen, neigh->address, neigh->ifp->ifindex);
	if (add_metric >= INFINITY)
		return NULL;

	route = find_route(prefix, plen, neigh, nexthop);

	if (route && memcmp(route->src->id, router_id, 8) == 0)
		/* Avoid scanning the source table. */
		src = route->src;
	else
		src = find_source(router_id, prefix, plen, 1, seqno);

	if (src == NULL)
		return NULL;

	feasible = update_feasible(src, seqno, refmetric);
	metric = MIN((int)refmetric + neighbour_cost(neigh) + add_metric, INFINITY);

	if (route) {
		struct source *oldsrc;
		unsigned short oldmetric;
		int lost = 0;

		oldsrc = route->src;
		oldmetric = route_metric(route);

		/* If a successor switches sources, we must accept his update even
           if it makes a route unfeasible in order to break any routing loops
           in a timely manner.  If the source remains the same, we ignore
           the update. */
		if (!feasible && route->installed) {
			debugf(BABEL_DEBUG_COMMON,
			       "Unfeasible update for installed route to %s (%s %d %d -> %s %d %d).",
			       format_prefix(src->prefix, src->plen), format_eui64(route->src->id),
			       route->seqno, route->refmetric, format_eui64(src->id), seqno,
			       refmetric);
			if (src != route->src) {
				uninstall_route(route);
				lost = 1;
			}
		}

		route->src = retain_source(src);
		if ((feasible || keep_unfeasible) && refmetric < INFINITY)
			route->time = babel_now.tv_sec;
		route->seqno = seqno;

		memset(&route->channels, 0, sizeof(route->channels));
		if (channels_len > 0)
			memcpy(&route->channels, channels, MIN(channels_len, DIVERSITY_HOPS));

		change_route_metric(route, refmetric, neighbour_cost(neigh), add_metric);
		route->hold_time = hold_time;

		route_changed(route, oldsrc, oldmetric);
		if (lost)
			route_lost(oldsrc, oldmetric);

		if (!feasible)
			send_unfeasible_request(neigh, route->installed && route_old(route), seqno,
						metric, src);
		release_source(oldsrc);
	} else {
		struct babel_route *new_route;

		if (refmetric >= INFINITY)
			/* Somebody's retracting a route we never saw. */
			return NULL;
		if (!feasible) {
			send_unfeasible_request(neigh, 0, seqno, metric, src);
			if (!keep_unfeasible)
				return NULL;
		}

		route = malloc(sizeof(struct babel_route));
		if (route == NULL) {
			perror("malloc(route)");
			return NULL;
		}

		route->src = retain_source(src);
		route->refmetric = refmetric;
		route->cost = neighbour_cost(neigh);
		route->add_metric = add_metric;
		route->seqno = seqno;
		route->neigh = neigh;
		memcpy(route->nexthop, nexthop, 16);
		route->time = babel_now.tv_sec;
		route->hold_time = hold_time;
		route->smoothed_metric = MAX(route_metric(route), INFINITY / 2);
		route->smoothed_metric_time = babel_now.tv_sec;
		route->installed = 0;
		memset(&route->channels, 0, sizeof(route->channels));
		if (channels_len > 0)
			memcpy(&route->channels, channels, MIN(channels_len, DIVERSITY_HOPS));
		route->next = NULL;
		new_route = insert_route(route);
		if (new_route == NULL) {
			flog_err(EC_BABEL_ROUTE, "Couldn't insert route.");
			free(route);
			return NULL;
		}
		consider_route(route);
	}
	return route;
}

/* We just received an unfeasible update.  If it's any good, send
   a request for a new seqno. */
void send_unfeasible_request(struct neighbour *neigh, int force, unsigned short seqno,
			     unsigned short metric, struct source *src)
{
	struct babel_route *route = find_installed_route(src->prefix, src->plen);

	if (seqno_minus(src->seqno, seqno) > 100) {
		/* Probably a source that lost its seqno.  Let it time-out. */
		return;
	}

	if (force || !route || route_metric(route) >= metric + 512) {
		send_unicast_multihop_request(neigh, src->prefix, src->plen,
					      src->metric >= INFINITY ? src->seqno
								      : seqno_plus(src->seqno, 1),
					      src->id, 127);
	}
}

/* This takes a feasible route and decides whether to install it.
   This uses the strong ordering, which is defined by sm <= sm' AND
   m <= m'.  This ordering is not total, which is what causes
   hysteresis. */

static void consider_route(struct babel_route *route)
{
	struct babel_route *installed;
	struct xroute *xroute;

	if (route->installed)
		return;

	if (!route_feasible(route))
		return;

	xroute = find_xroute(route->src->prefix, route->src->plen);
	if (xroute)
		return;

	installed = find_installed_route(route->src->prefix, route->src->plen);

	if (installed == NULL)
		goto install;

	if (route_metric(route) >= INFINITY)
		return;

	if (route_metric(installed) >= INFINITY)
		goto install;

	if (route_metric(installed) >= route_metric(route) &&
	    route_smoothed_metric(installed) > route_smoothed_metric(route))
		goto install;

	return;

install:
	switch_routes(installed, route);
	if (installed && route->installed)
		send_triggered_update(route, installed->src, route_metric(installed));
	else
		send_update(NULL, 1, route->src->prefix, route->src->plen);
}

void retract_neighbour_routes(struct neighbour *neigh)
{
	int i;

	for (i = 0; i < route_slots; i++) {
		struct babel_route *r = routes[i];
		while (r) {
			if (r->neigh == neigh) {
				if (r->refmetric != INFINITY) {
					unsigned short oldmetric = route_metric(r);
					retract_route(r);
					if (oldmetric != INFINITY)
						route_changed(r, r->src, oldmetric);
				}
			}
			r = r->next;
		}
	}
}

void send_triggered_update(struct babel_route *route, struct source *oldsrc, unsigned oldmetric)
{
	unsigned newmetric, diff;
	/* 1 means send speedily, 2 means resend */
	int urgent;

	if (!route->installed)
		return;

	newmetric = route_metric(route);
	diff = newmetric >= oldmetric ? newmetric - oldmetric : oldmetric - newmetric;

	if (route->src != oldsrc || (oldmetric < INFINITY && newmetric >= INFINITY))
		/* Switching sources can cause transient routing loops.
		   Retractions can cause blackholes. */
		urgent = 2;
	else if (newmetric > oldmetric && oldmetric < 6 * 256 && diff >= 512)
		/* Route getting significantly worse */
		urgent = 1;
	else if (unsatisfied_request(route->src->prefix, route->src->plen, route->seqno,
				     route->src->id))
		/* Make sure that requests are satisfied speedily */
		urgent = 1;
	else if (oldmetric >= INFINITY && newmetric < INFINITY)
		/* New route */
		urgent = 0;
	else if (newmetric < oldmetric && diff < 1024)
		/* Route getting better.  This may be a transient fluctuation, so
		   don't advertise it to avoid making routes unfeasible later on. */
		return;
	else if (diff < 384)
		/* Don't fret about trivialities */
		return;
	else
		urgent = 0;

	if (urgent >= 2)
		send_update_resend(NULL, route->src->prefix, route->src->plen);
	else
		send_update(NULL, urgent, route->src->prefix, route->src->plen);

	if (oldmetric < INFINITY) {
		if (newmetric >= oldmetric + 512) {
			send_request_resend(NULL, route->src->prefix, route->src->plen,
					    route->src->metric >= INFINITY
						    ? route->src->seqno
						    : seqno_plus(route->src->seqno, 1),
					    route->src->id);
		} else if (newmetric >= oldmetric + 288) {
			send_request(NULL, route->src->prefix, route->src->plen);
		}
	}
}

/* A route has just changed.  Decide whether to switch to a different route or
   send an update. */
void route_changed(struct babel_route *route, struct source *oldsrc, unsigned short oldmetric)
{
	if (route->installed) {
		struct babel_route *better_route;
		/* Do this unconditionally -- microoptimisation is not worth it. */
		better_route = find_best_route(route->src->prefix, route->src->plen, 1, NULL);
		if (better_route && route_metric(better_route) < route_metric(route))
			consider_route(better_route);
	}

	if (route->installed) {
		/* We didn't change routes after all. */
		send_triggered_update(route, oldsrc, oldmetric);
	} else {
		/* Reconsider routes even when their metric didn't decrease,
           they may not have been feasible before. */
		consider_route(route);
	}
}

/* We just lost the installed route to a given destination. */
void route_lost(struct source *src, unsigned oldmetric)
{
	struct babel_route *new_route;
	new_route = find_best_route(src->prefix, src->plen, 1, NULL);
	if (new_route) {
		consider_route(new_route);
	} else {
		struct babel_route *unfeasible = find_best_route(src->prefix, src->plen, 0, NULL);

		if (unfeasible && !route_expired(unfeasible)) {
			/* MUST send seqno request when we have unexpired unfeasible routes */
			send_request_resend(NULL, src->prefix, src->plen,
					    seqno_plus(src->seqno, 1), src->id);
		} else if (oldmetric < INFINITY) {
			/* Avoid creating a blackhole. */
			send_update_resend(NULL, src->prefix, src->plen);
			/* If the route was usable enough, try to get an alternate one.
            If it was not, we could be dealing with oscillations around
            the value of INFINITY. */
			if (oldmetric <= INFINITY / 2)
				send_request_resend(NULL, src->prefix, src->plen,
						    src->metric >= INFINITY
							    ? src->seqno
							    : seqno_plus(src->seqno, 1),
						    src->id);
		}
	}
}

/* This is called periodically to flush old routes.  It will also send
   requests for routes that are about to expire. */
void expire_routes(void)
{
	struct babel_route *r;
	int i;

	debugf(BABEL_DEBUG_COMMON, "Expiring old routes.");

	i = 0;
	while (i < route_slots) {
		r = routes[i];
		while (r) {
			/* Protect against clock being stepped. */
			if (r->time > babel_now.tv_sec || route_old(r)) {
				flush_route(r);
				goto again;
			}

			update_route_metric(r);

			if (r->installed && r->refmetric < INFINITY) {
				if (route_old(r))
					/* Route about to expire, send a request. */
					send_unicast_request(r->neigh, r->src->prefix,
							     r->src->plen);
			}
			r = r->next;
		}
		i++;
again:;
	}
}
