* to the project. For more information see the website or contact
* the copyright holders.
*
- * $Id: lq_route.c,v 1.59 2007/11/16 21:43:55 bernd67 Exp $
+ * Implementation of Dijkstras algorithm. Initially all nodes
+ * are initialized to infinite cost. First we put ourselves
+ * on the heap of reachable nodes. Our heap implementation
+ * is based on an AVL tree which gives interesting performance
+ * characteristics for the frequent operations of minimum key
+ * extraction and re-keying. Next all neighbors of a node are
+ * explored and put on the heap if the cost of reaching them is
+ * better than reaching the current candidate node.
+ * The SPF calculation is terminated if there are no more nodes
+ * on the heap.
*/
-#define SPF_PROFILING 1
-
+#include "ipcalc.h"
#include "defs.h"
#include "olsr.h"
#include "tc_set.h"
#include "routing_table.h"
#include "mid_set.h"
#include "hna_set.h"
-#include "lq_list.h"
-#include "lq_avl.h"
+#include "common/list.h"
+#include "common/avl.h"
#include "lq_route.h"
#include "net_olsr.h"
+#include "lq_plugin.h"
+
+struct timer_entry *spf_backoff_timer = NULL;
/*
* avl_comp_etx
static int
avl_comp_etx (const void *etx1, const void *etx2)
{
- if (*(float *)etx1 < *(float *)etx2) {
+ if (*(const olsr_linkcost *)etx1 < *(const olsr_linkcost *)etx2) {
return -1;
}
- if (*(float *)etx1 > *(float *)etx2) {
+ if (*(const olsr_linkcost *)etx1 > *(const olsr_linkcost *)etx2) {
return +1;
}
*/
static void
olsr_spf_add_cand_tree (struct avl_tree *tree,
- struct tc_entry *vert)
+ struct tc_entry *tc)
{
#if !defined(NODEBUG) && defined(DEBUG)
struct ipaddr_str buf;
+ struct lqtextbuffer lqbuffer;
#endif
- vert->cand_tree_node.key = &vert->path_etx;
- vert->cand_tree_node.data = vert;
+ tc->cand_tree_node.key = &tc->path_cost;
#ifdef DEBUG
- OLSR_PRINTF(1, "SPF: insert candidate %s, cost %f\n",
- olsr_ip_to_string(&buf, &vert->addr),
- vert->path_etx);
+ OLSR_PRINTF(2, "SPF: insert candidate %s, cost %s\n",
+ olsr_ip_to_string(&buf, &tc->addr),
+ get_linkcost_text(tc->path_cost, OLSR_FALSE, &lqbuffer));
#endif
- avl_insert(tree, &vert->cand_tree_node, AVL_DUP);
+ avl_insert(tree, &tc->cand_tree_node, AVL_DUP);
}
/*
*/
static void
olsr_spf_del_cand_tree (struct avl_tree *tree,
- struct tc_entry *vert)
+ struct tc_entry *tc)
{
#ifdef DEBUG
#ifndef NODEBUG
struct ipaddr_str buf;
+ struct lqtextbuffer lqbuffer;
#endif
- OLSR_PRINTF(1, "SPF: delete candidate %s, cost %f\n",
- olsr_ip_to_string(&buf, &vert->addr),
- vert->path_etx);
+ OLSR_PRINTF(2, "SPF: delete candidate %s, cost %s\n",
+ olsr_ip_to_string(&buf, &tc->addr),
+ get_linkcost_text(tc->path_cost, OLSR_FALSE, &lqbuffer));
#endif
- avl_delete(tree, &vert->cand_tree_node);
+ avl_delete(tree, &tc->cand_tree_node);
}
/*
* Insert an SPF result at the end of the path list.
*/
static void
-olsr_spf_add_path_list (struct list_node *head,
- int *path_count,
- struct tc_entry *vert)
+olsr_spf_add_path_list (struct list_node *head, int *path_count,
+ struct tc_entry *tc)
{
#if !defined(NODEBUG) && defined(DEBUG)
struct ipaddr_str pathbuf, nbuf;
+ struct lqtextbuffer lqbuffer;
#endif
- vert->path_list_node.data = vert;
#ifdef DEBUG
- OLSR_PRINTF(1, "SPF: append path %s, cost %f, via %s\n",
- olsr_ip_to_string(&pathbuf, &vert->addr),
- vert->path_etx,
- vert->next_hop ? olsr_ip_to_string(&nbuf, &vert->next_hop->neighbor_iface_addr) : "-");
+ OLSR_PRINTF(2, "SPF: append path %s, cost %s, via %s\n",
+ olsr_ip_to_string(&pathbuf, &tc->addr),
+ get_linkcost_text(tc->path_cost, OLSR_FALSE, &lqbuffer),
+ tc->next_hop ? olsr_ip_to_string(
+ &nbuf, &tc->next_hop->neighbor_iface_addr) : "-");
#endif
- list_add_before(head, &vert->path_list_node);
+ list_add_before(head, &tc->path_list_node);
*path_count = *path_count + 1;
}
{
struct avl_node *node = avl_walk_first(tree);
- return (node ? node->data : NULL);
-}
-
-
-const char *olsr_etx_to_string(float etx)
-{
- static char buff[20];
-
- if (etx == INFINITE_ETX) {
- return "INF";
- }
- snprintf(buff, sizeof(buff), "%.6f", etx);
- return buff;
+ return (node ? cand_tree2tc(node) : NULL);
}
* path cost is better.
*/
static void
-olsr_spf_relax (struct avl_tree *cand_tree, struct tc_entry *vert)
+olsr_spf_relax (struct avl_tree *cand_tree, struct tc_entry *tc)
{
struct avl_node *edge_node;
- float new_etx;
+ olsr_linkcost new_cost;
#ifdef DEBUG
#ifndef NODEBUG
struct ipaddr_str buf, nbuf;
+ struct lqtextbuffer lqbuffer;
#endif
- OLSR_PRINTF(1, "SPF: exploring node %s, cost %f\n",
- olsr_ip_to_string(&buf, &vert->addr),
- vert->path_etx);
+ OLSR_PRINTF(2, "SPF: exploring node %s, cost %s\n",
+ olsr_ip_to_string(&buf, &tc->addr),
+ get_linkcost_text(tc->path_cost, OLSR_FALSE, &lqbuffer));
#endif
/*
* loop through all edges of this vertex.
*/
- for (edge_node = avl_walk_first(&vert->edge_tree);
+ for (edge_node = avl_walk_first(&tc->edge_tree);
edge_node;
edge_node = avl_walk_next(edge_node)) {
- struct tc_entry *new_vert;
- struct tc_edge_entry *tc_edge = edge_node->data;
+
+ struct tc_entry *new_tc;
+ struct tc_edge_entry *tc_edge = edge_tree2tc_edge(edge_node);
/*
* We are not interested in dead-end or dying edges.
*/
- if (!tc_edge->edge_inv || (tc_edge->flags & OLSR_TC_EDGE_DOWN)) {
+ if (!tc_edge->edge_inv ||
+ ((tc_edge->flags | tc_edge->edge_inv->flags) & OLSR_TC_EDGE_DOWN)) {
#ifdef DEBUG
- OLSR_PRINTF(1, "SPF: ignoring edge %s\n",
+ OLSR_PRINTF(2, "SPF: ignoring edge %s\n",
olsr_ip_to_string(&buf, &tc_edge->T_dest_addr));
+ if (!tc_edge->edge_inv) {
+ OLSR_PRINTF(2, "SPF: no inverse edge\n");
+ }
+
if (tc_edge->flags & OLSR_TC_EDGE_DOWN) {
- OLSR_PRINTF(1, "SPF: edge down\n");
+ OLSR_PRINTF(2, "SPF: edge down\n");
}
if (!tc_edge->edge_inv) {
- OLSR_PRINTF(1, "SPF: no inverse edge\n");
+ OLSR_PRINTF(2, "SPF: no inverse edge\n");
+ } else if (tc_edge->edge_inv->flags & OLSR_TC_EDGE_DOWN){
+ OLSR_PRINTF(2, "SPF: inverse edge down\n");
}
#endif
continue;
* total quality of the path through this vertex
* to the destination of this edge
*/
- new_etx = vert->path_etx + tc_edge->etx;
+ new_cost = tc->path_cost + tc_edge->cost;
#ifdef DEBUG
- OLSR_PRINTF(1, "SPF: exploring edge %s, cost %s\n",
+ OLSR_PRINTF(2, "SPF: exploring edge %s, cost %s\n",
olsr_ip_to_string(&buf, &tc_edge->T_dest_addr),
- olsr_etx_to_string(new_etx));
+ get_linkcost_text(new_cost, OLSR_TRUE, &lqbuffer));
#endif
/*
* if it's better than the current path quality of this edge's
* destination node, then we've found a better path to this node.
*/
- new_vert = tc_edge->edge_inv->tc;
+ new_tc = tc_edge->edge_inv->tc;
- if (new_etx < new_vert->path_etx) {
+ if (new_cost < new_tc->path_cost) {
/* if this node has been on the candidate tree delete it */
- if (new_vert->path_etx != INFINITE_ETX) {
- olsr_spf_del_cand_tree(cand_tree, new_vert);
+ if (new_tc->path_cost < ROUTE_COST_BROKEN) {
+ olsr_spf_del_cand_tree(cand_tree, new_tc);
}
/* re-insert on candidate tree with the better metric */
- new_vert->path_etx = new_etx;
- olsr_spf_add_cand_tree(cand_tree, new_vert);
+ new_tc->path_cost = new_cost;
+ olsr_spf_add_cand_tree(cand_tree, new_tc);
/* pull-up the next-hop and bump the hop count */
- if (vert->next_hop) {
- new_vert->next_hop = vert->next_hop;
+ if (tc->next_hop) {
+ new_tc->next_hop = tc->next_hop;
}
- new_vert->hops = vert->hops + 1;
+ new_tc->hops = tc->hops + 1;
#ifdef DEBUG
- OLSR_PRINTF(1, "SPF: better path to %s, cost %s -> %s, via %s, hops %u\n",
- olsr_ip_to_string(&buf, &new_vert->addr),
- olsr_etx_to_string(new_vert->path_etx),
- olsr_etx_to_string(new_etx),
- vert->next_hop ? olsr_ip_to_string(&nbuf, &vert->next_hop->neighbor_iface_addr) : "<none>",
- new_vert->hops);
+ OLSR_PRINTF(2, "SPF: better path to %s, cost %s, via %s, hops %u\n",
+ olsr_ip_to_string(&buf, &new_tc->addr),
+ get_linkcost_text(new_cost, OLSR_TRUE, &lqbuffer),
+ tc->next_hop ? olsr_ip_to_string(
+ &nbuf, &tc->next_hop->neighbor_iface_addr) : "<none>",
+ new_tc->hops);
#endif
}
olsr_spf_run_full (struct avl_tree *cand_tree, struct list_node *path_list,
int *path_count)
{
- struct tc_entry *vert;
+ struct tc_entry *tc;
*path_count = 0;
- while ((vert = olsr_spf_extract_best(cand_tree))) {
+ while ((tc = olsr_spf_extract_best(cand_tree))) {
- olsr_spf_relax(cand_tree, vert);
+ olsr_spf_relax(cand_tree, tc);
/*
* move the best path from the candidate tree
* to the path list.
*/
- olsr_spf_del_cand_tree(cand_tree, vert);
- olsr_spf_add_path_list(path_list, path_count, vert);
+ olsr_spf_del_cand_tree(cand_tree, tc);
+ olsr_spf_add_path_list(path_list, path_count, tc);
}
}
+/**
+ * Callback for the SPF backoff timer.
+ */
+static void
+olsr_expire_spf_backoff(void *context __attribute__((unused)))
+{
+ spf_backoff_timer = NULL;
+}
+
void
-olsr_calculate_routing_table (void)
+olsr_calculate_routing_table (void *context __attribute__((unused)))
{
+#ifdef SPF_PROFILING
+ struct timeval t1, t2, t3, t4, t5, spf_init, spf_run, route, kernel, total;
+#endif
struct avl_tree cand_tree;
- struct list_node path_list;
- int i, plen, path_count = 0;
+ struct avl_node *rtp_tree_node;
+ struct list_node path_list; /* head of the path_list */
struct tc_entry *tc;
+ struct rt_path *rtp;
struct tc_edge_entry *tc_edge;
- struct tc_entry *vert;
struct neighbor_entry *neigh;
- struct mid_address *mid_walker;
- struct hna_entry *hna_gw;
- struct hna_net *hna;
- struct interface *inter;
struct link_entry *link;
+ int path_count = 0;
+
+ /* We are done if our backoff timer is running */
+ if (!spf_backoff_timer) {
+ spf_backoff_timer =
+ olsr_start_timer(1000, 5, OLSR_TIMER_ONESHOT, &olsr_expire_spf_backoff,
+ NULL, 0);
+ } else {
+ return;
+ }
#ifdef SPF_PROFILING
- struct timeval t1, t2, t3, t4, t5, spf_init, spf_run, route, kernel, total;
-
gettimeofday(&t1, NULL);
#endif
*/
OLSR_FOR_ALL_TC_ENTRIES(tc) {
tc->next_hop = NULL;
- tc->path_etx = INFINITE_ETX;
+ tc->path_cost = ROUTE_COST_BROKEN;
tc->hops = 0;
} OLSR_FOR_ALL_TC_ENTRIES_END(tc);
* zero ourselves and add us to the candidate tree.
*/
olsr_change_myself_tc();
- tc_myself->path_etx = ZERO_ETX;
+ tc_myself->path_cost = ZERO_ROUTE_COST;
olsr_spf_add_cand_tree(&cand_tree, tc_myself);
/*
* add edges to and from our neighbours.
*/
- for (i = 0; i < HASHSIZE; i++)
- for (neigh = neighbortable[i].next; neigh != &neighbortable[i];
- neigh = neigh->next) {
-
- if (neigh->status == SYM) {
-
- tc_edge = olsr_lookup_tc_edge(tc_myself, &neigh->neighbor_main_addr);
- link = get_best_link_to_neighbor(&neigh->neighbor_main_addr);
- if (!link) {
-
- /*
- * If there is no best link to this neighbor
- * and we had an edge before then flush the edge.
- */
- if (tc_edge) {
- olsr_delete_tc_edge_entry(tc_edge);
- }
- continue;
- }
+ OLSR_FOR_ALL_NBR_ENTRIES(neigh) {
+
+ if (neigh->status == SYM) {
+
+ tc_edge = olsr_lookup_tc_edge(tc_myself, &neigh->neighbor_main_addr);
+ link = get_best_link_to_neighbor(&neigh->neighbor_main_addr);
+ if (!link) {
/*
- * Set the next-hops of our neighbors.
+ * If there is no best link to this neighbor
+ * and we had an edge before then flush the edge.
*/
- if (!tc_edge) {
- tc_edge = olsr_add_tc_edge_entry(tc_myself, &neigh->neighbor_main_addr,
- 0, link->last_htime,
- link->loss_link_quality2,
- link->neigh_link_quality2);
- } else {
- tc_edge->link_quality = link->loss_link_quality2;
- tc_edge->inverse_link_quality = link->neigh_link_quality2;
- olsr_calc_tc_edge_entry_etx(tc_edge);
- }
- if (tc_edge->edge_inv) {
- tc_edge->edge_inv->tc->next_hop = link;
+ if (tc_edge) {
+ olsr_delete_tc_edge_entry(tc_edge);
}
+ continue;
+ }
+
+ /* find the interface for the link */
+ if (link->if_name) {
+ link->inter = if_ifwithname(link->if_name);
+ } else {
+ link->inter = if_ifwithaddr(&link->local_iface_addr);
+ }
+
+ /*
+ * Set the next-hops of our neighbors.
+ */
+ if (!tc_edge) {
+ tc_edge = olsr_add_tc_edge_entry(tc_myself, &neigh->neighbor_main_addr,
+ 0, link->vtime);
+ } else {
+ /*
+ * Update LQ and timers, such that the edge does not get deleted.
+ */
+ olsr_copylq_link_entry_2_tc_edge_entry(tc_edge, link);
+ olsr_set_tc_edge_timer(tc_edge, link->vtime*1000);
+ olsr_calc_tc_edge_entry_etx(tc_edge);
+ }
+ if (tc_edge->edge_inv) {
+ tc_edge->edge_inv->tc->next_hop = link;
}
}
+ } OLSR_FOR_ALL_NBR_ENTRIES_END(neigh);
#ifdef SPF_PROFILING
gettimeofday(&t2, NULL);
*/
olsr_spf_run_full(&cand_tree, &path_list, &path_count);
- OLSR_PRINTF(2, "\n--- %02d:%02d:%02d.%02d ------------------------------------------------- DIJKSTRA\n\n",
- nowtm->tm_hour,
- nowtm->tm_min,
- nowtm->tm_sec,
- (int)now.tv_usec/10000);
+ OLSR_PRINTF(2, "\n--- %s ------------------------------------------------- DIJKSTRA\n\n",
+ olsr_wallclock_string());
#ifdef SPF_PROFILING
gettimeofday(&t3, NULL);
#endif
/*
- * In the path tree we have all the reachable nodes in our topology.
+ * In the path list we have all the reachable nodes in our topology.
*/
for (; !list_is_empty(&path_list); list_remove(path_list.next)) {
- vert = path_list.next->data;
- link = vert->next_hop;
+ tc = pathlist2tc(path_list.next);
+ link = tc->next_hop;
if (!link) {
+#ifdef DEBUG
+ /*
+ * Supress the error msg when our own tc_entry
+ * does not contain a next-hop.
+ */
+ if (tc != tc_myself) {
#ifndef NODEBUG
- struct ipaddr_str buf;
+ struct ipaddr_str buf;
+#endif
+ OLSR_PRINTF(2, "SPF: %s no next-hop\n", olsr_ip_to_string(&buf, &tc->addr));
+ }
#endif
- OLSR_PRINTF(2, "%s no next-hop\n", olsr_ip_to_string(&buf, &vert->addr));
continue;
}
- /* find the interface for the found link */
- inter = link->if_name ? if_ifwithname(link->if_name)
- : if_ifwithaddr(&link->local_iface_addr);
-
- /* interface is up ? */
- if (inter) {
-
- /* add a route to the main address of the destination node */
- olsr_insert_routing_table(&vert->addr, olsr_cnf->maxplen, &vert->addr,
- &link->neighbor_iface_addr, inter->if_index,
- vert->hops, vert->path_etx);
+ /*
+ * Now walk all prefixes advertised by that node.
+ * Since the node is reachable, insert the prefix into the global RIB.
+ * If the prefix is already in the RIB, refresh the entry such
+ * that olsr_delete_outdated_routes() does not purge it off.
+ */
+ for (rtp_tree_node = avl_walk_first(&tc->prefix_tree);
+ rtp_tree_node;
+ rtp_tree_node = avl_walk_next(rtp_tree_node)) {
- /* add routes to the remaining interfaces of the destination node */
- for (mid_walker = mid_lookup_aliases(&vert->addr);
- mid_walker != NULL;
- mid_walker = mid_walker->next_alias) {
+ rtp = rtp_prefix_tree2rtp(rtp_tree_node);
- olsr_insert_routing_table(&mid_walker->alias, olsr_cnf->maxplen, &vert->addr,
- &link->neighbor_iface_addr, inter->if_index,
- vert->hops, vert->path_etx);
- }
+ if (rtp->rtp_rt) {
- /* find the node's HNAs */
- hna_gw = olsr_lookup_hna_gw(&vert->addr);
-
- /* node doesn't announce any HNAs */
- if (!hna_gw) {
- continue;
- }
+ /*
+ * If there is a route entry, the prefix is already in the global RIB.
+ */
+ olsr_update_rt_path(rtp, tc, link);
- /* loop through the node's HNAs */
- for (hna = hna_gw->networks.next;
- hna != &hna_gw->networks;
- hna = hna->next) {
+ } else {
- plen = olsr_get_hna_prefix_len(hna);
- if (vert->path_etx != INFINITE_ETX)
- olsr_insert_routing_table(&hna->A_network_addr, plen, &vert->addr,
- &link->neighbor_iface_addr, inter->if_index,
- vert->hops, vert->path_etx);
+ /*
+ * The prefix is reachable and not yet in the global RIB.
+ * Build a rt_entry for it.
+ */
+ olsr_insert_rt_path(rtp, tc, link);
}
}
}
+ /* Update the RIB based on the new SPF results */
+
+ olsr_update_rib_routes();
+
#ifdef SPF_PROFILING
gettimeofday(&t4, NULL);
#endif