Bugfix for avltree modification
[olsrd.git] / src / lq_route.c
index 1751d4f..ac905c9 100644 (file)
-/* 
- * OLSR ad-hoc routing table management protocol
- * Copyright (C) 2004 Thomas Lopatic (thomas@lopatic.de)
+/*
+ * The olsr.org Optimized Link-State Routing daemon(olsrd)
+ * Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de)
+ * IPv4 performance optimization (c) 2006, sven-ola(gmx.de)
+ * SPF implementation (c) 2007, Hannes Gredler (hannes@gredler.at)
+ * All rights reserved.
  *
- * This file is part of the olsr.org OLSR daemon.
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met:
  *
- * olsr.org is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * * Redistributions of source code must retain the above copyright 
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright 
+ *   notice, this list of conditions and the following disclaimer in 
+ *   the documentation and/or other materials provided with the 
+ *   distribution.
+ * * Neither the name of olsr.org, olsrd nor the names of its 
+ *   contributors may be used to endorse or promote products derived 
+ *   from this software without specific prior written permission.
  *
- * olsr.org is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
  *
- * You should have received a copy of the GNU General Public License
- * along with olsr.org; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Visit http://www.olsr.org for more information.
  *
- * $Id: lq_route.c,v 1.11 2004/11/15 14:59:38 tlopatic Exp $
+ * If you find this software useful feel free to make a donation
+ * to the project. For more information see the website or contact
+ * the copyright holders.
  *
+ * 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.
  */
 
-#if defined USE_LINK_QUALITY
+#include "ipcalc.h"
 #include "defs.h"
+#include "olsr.h"
 #include "tc_set.h"
 #include "neighbor_table.h"
+#include "two_hop_neighbor_table.h"
 #include "link_set.h"
 #include "routing_table.h"
 #include "mid_set.h"
-#include "lq_list.h"
+#include "hna_set.h"
+#include "common/list.h"
+#include "common/avl.h"
 #include "lq_route.h"
+#include "net_olsr.h"
+#include "lq_plugin.h"
 
-#define INFINITE_ETX (1 << 30)
-#define MIN_LINK_QUALITY 0.0001
+struct timer_entry *spf_backoff_timer = NULL;
 
-struct dijk_edge
-{
-  struct list_node node;
-  struct dijk_vertex *dest;
-  int etx;
-};
-
-struct dijk_vertex
-{
-  struct list_node node;
-  union olsr_ip_addr addr;
-  struct list edge_list;
-  int path_etx;
-  struct dijk_vertex *prev;
-  olsr_bool done;
-};
-
-// XXX - bad complexity
-
-static void add_vertex(struct list *vertex_list, union olsr_ip_addr *addr,
-                       int path_etx)
-{
-  struct list_node *node;
-  struct dijk_vertex *vert;
-
-  // see whether this destination is already in our list
-
-  node = list_get_head(vertex_list);
-
-  while (node != NULL)
-  {
-    vert = node->data;
-
-    if (COMP_IP(&vert->addr, addr))
-      break;
-
-    node = list_get_next(node);
+/*
+ * avl_comp_etx
+ *
+ * compare two etx metrics.
+ * return 0 if there is an exact match and
+ * -1 / +1 depending on being smaller or bigger.
+ * note that this results in the most optimal code
+ * after compiler optimization.
+ */
+static int
+avl_comp_etx (const void *etx1, const void *etx2)
+{       
+  if (*(const olsr_linkcost *)etx1 < *(const olsr_linkcost *)etx2) {
+    return -1;
   }
 
-  // if it's not in our list, add it
-
-  if (node == NULL)
-  {
-    vert = olsr_malloc(sizeof (struct dijk_vertex), "Dijkstra vertex");
-
-    vert->node.data = vert;
-
-    COPY_IP(&vert->addr, addr);
-    
-    vert->path_etx = path_etx;
-    vert->prev = NULL;
-    vert->done = OLSR_FALSE;
-
-    list_init(&vert->edge_list);
-
-    list_add_tail(vertex_list, &vert->node);
+  if (*(const olsr_linkcost *)etx1 > *(const olsr_linkcost *)etx2) {
+    return +1;
   }
-}
 
-// XXX - bad complexity
+  return 0;
+}
 
-static void add_edge(struct list *vertex_list, union olsr_ip_addr *src,
-                     union olsr_ip_addr *dst, int etx)
+/*
+ * olsr_spf_add_cand_tree
+ *
+ * Key an existing vertex to a candidate tree.
+ */
+static void
+olsr_spf_add_cand_tree (struct avl_tree *tree,
+                        struct tc_entry *tc)
 {
-  struct list_node *node;
-  struct dijk_vertex *vert;
-  struct dijk_vertex *svert;
-  struct dijk_vertex *dvert;
-  struct dijk_edge *edge;
-
-  // source and destination lookup
-
-  node = list_get_head(vertex_list);
-
-  svert = NULL;
-  dvert = NULL;
-
-  while (node != NULL)
-  {
-    vert = node->data;
-
-    if (COMP_IP(&vert->addr, src))
-    {
-      svert = vert;
-
-      if (dvert != NULL)
-        break;
-    }
-
-    else if (COMP_IP(&vert->addr, dst))
-    {
-      dvert = vert;
-
-      if (svert != NULL)
-        break;
-    }
-
-    node = list_get_next(node);
-  }
-
-  // source or destination vertex does not exist
-
-  if (svert == NULL || dvert == NULL)
-    return;
-
-  node = list_get_head(&svert->edge_list);
-
-  while (node != NULL)
-  {
-    edge = node->data;
+#if !defined(NODEBUG) && defined(DEBUG)
+  struct ipaddr_str buf;
+  struct lqtextbuffer lqbuffer;
+#endif
+  tc->cand_tree_node.key = &tc->path_cost;
 
-    if (edge->dest == dvert)
-      break;
+#ifdef DEBUG
+  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
 
-    node = list_get_next(node);
-  }
+  avl_insert(tree, &tc->cand_tree_node, AVL_DUP);
+}
 
-  // edge already exists
+/*
+ * olsr_spf_del_cand_tree
+ *
+ * Unkey an existing vertex from a candidate tree.
+ */
+static void
+olsr_spf_del_cand_tree (struct avl_tree *tree,
+                        struct tc_entry *tc)
+{
 
-  if (node != NULL)
-    return;
+#ifdef DEBUG
+#ifndef NODEBUG
+  struct ipaddr_str buf;
+  struct lqtextbuffer lqbuffer;
+#endif
+  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
 
-  edge = olsr_malloc(sizeof (struct dijk_vertex), "Dijkstra edge");
+  avl_delete(tree, &tc->cand_tree_node);
+}
 
-  edge->node.data = edge;
+/*
+ * olsr_spf_add_path_list
+ *
+ * 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 *tc)
+{
+#if !defined(NODEBUG) && defined(DEBUG)
+  struct ipaddr_str pathbuf, nbuf;
+  struct lqtextbuffer lqbuffer;
+#endif
 
-  edge->dest = dvert;
-  edge->etx = etx;
+#ifdef DEBUG
+  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_tail(&svert->edge_list, &edge->node);
+  list_add_before(head, &tc->path_list_node);
+  *path_count = *path_count + 1;
 }
 
-static void free_everything(struct list *vertex_list)
+/*
+ * olsr_spf_extract_best
+ *
+ * return the node with the minimum pathcost.
+ */
+static struct tc_entry *
+olsr_spf_extract_best (struct avl_tree *tree)
 {
-  struct list_node *vnode, *enode;
-  struct dijk_vertex *vert;
-  struct dijk_edge *edge;
-
-  vnode = list_get_head(vertex_list);
-
-  // loop through all vertices
+  struct avl_node *node = avl_walk_first(tree);
 
-  while (vnode != NULL)
-  {
-    vert = vnode->data;
+  return (node ? cand_tree2tc(node) :  NULL);
+}
 
-    enode = list_get_head(&vert->edge_list);
 
-    // loop through all edges of the current vertex
+/*
+ * olsr_spf_relax
+ *
+ * Explore all edges of a node and add the node
+ * to the candidate tree if the if the aggregate
+ * path cost is better.
+ */
+static void
+olsr_spf_relax (struct avl_tree *cand_tree, struct tc_entry *tc)
+{
+  struct avl_node *edge_node;
+  olsr_linkcost new_cost;
 
-    while (enode != NULL)
-    {
-      edge = enode->data;
+#ifdef DEBUG
+#ifndef NODEBUG
+  struct ipaddr_str buf, nbuf;
+  struct lqtextbuffer lqbuffer;
+#endif
+  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
 
-      enode = list_get_next(enode);
-      free(edge);
+  /*
+   * loop through all edges of this vertex.
+   */
+  for (edge_node = avl_walk_first(&tc->edge_tree);
+       edge_node;
+       edge_node = avl_walk_next(edge_node)) {
+
+    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 | tc_edge->edge_inv->flags) & OLSR_TC_EDGE_DOWN)) {
+#ifdef DEBUG
+      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(2, "SPF:     edge down\n");
+      }
+      if (!tc_edge->edge_inv) {
+        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;
     }
 
-    vnode = list_get_next(vnode);
-    free(vert);
-  }
-}
+    /*
+     * total quality of the path through this vertex
+     * to the destination of this edge
+     */
+    new_cost = tc->path_cost + tc_edge->cost;
 
-// XXX - bad complexity
+#ifdef DEBUG
+      OLSR_PRINTF(2, "SPF:   exploring edge %s, cost %s\n",
+                  olsr_ip_to_string(&buf, &tc_edge->T_dest_addr),
+                  get_linkcost_text(new_cost, OLSR_TRUE, &lqbuffer));
+#endif
 
-static struct dijk_vertex *extract_best(struct list *vertex_list)
-{
-  int best_etx = INFINITE_ETX + 1;
-  struct list_node *node;
-  struct dijk_vertex *vert;
-  struct dijk_vertex *res = NULL;
+      /* 
+       * 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_tc = tc_edge->edge_inv->tc;
 
-  node = list_get_head(vertex_list);
+    if (new_cost < new_tc->path_cost) {
 
-  // loop through all vertices
-  
-  while (node != NULL)
-  {
-    vert = node->data;
+      /* if this node has been on the candidate tree delete it */
+      if (new_tc->path_cost < ROUTE_COST_BROKEN) {
+        olsr_spf_del_cand_tree(cand_tree, new_tc);
+      }
 
-    // see whether the current vertex is better than what we have
+      /* re-insert on candidate tree with the better metric */
+      new_tc->path_cost = new_cost;
+      olsr_spf_add_cand_tree(cand_tree, new_tc);
 
-    if (!vert->done && vert->path_etx < best_etx)
-    {
-      best_etx = vert->path_etx;
-      res = vert;
-    }
+      /* pull-up the next-hop and bump the hop count */
+      if (tc->next_hop) {
+        new_tc->next_hop = tc->next_hop;
+      }
+      new_tc->hops = tc->hops + 1;
+
+#ifdef DEBUG
+      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
 
-    node = list_get_next(node);
+    }
   }
-
-  // if we've found a vertex, remove it from the set
-
-  if (res != NULL)
-    res->done = OLSR_TRUE;
-
-  return res;
 }
 
-static void relax(struct dijk_vertex *vert)
+/*
+ * olsr_spf_run_full
+ *
+ * Run the Dijkstra algorithm.
+ * 
+ * A node gets added to the candidate tree when one of its edges has
+ * an overall better root path cost than the node itself.
+ * The node with the shortest metric gets moved from the candidate to
+ * the path list every pass.
+ * The SPF computation is completed when there are no more nodes
+ * on the candidate tree. 
+ */ 
+static void
+olsr_spf_run_full (struct avl_tree *cand_tree, struct list_node *path_list,
+                   int *path_count)
 {
-  struct dijk_edge *edge;
-  int new_etx;
-  struct list_node *node;
-
-  node = list_get_head(&vert->edge_list);
-
-  // loop through all edges of this vertex
+  struct tc_entry *tc;
 
-  while (node != NULL)
-  {
-    edge = node->data;
+  *path_count = 0;
 
-    // total quality of the path through this vertex to the
-    // destination of this edge
+  while ((tc = olsr_spf_extract_best(cand_tree))) {
 
-    new_etx = vert->path_etx + edge->etx;
+    olsr_spf_relax(cand_tree, tc);
 
-    // if it's better than the current path quality of this
-    // edge's destination, then we've found a better path to
-    // this destination
-
-    if (new_etx < edge->dest->path_etx)
-    {
-      edge->dest->path_etx = new_etx;
-      edge->dest->prev = vert;
-    }
-
-    node = list_get_next(node);
+    /*
+     * move the best path from the candidate tree
+     * to the path list.
+     */
+    olsr_spf_del_cand_tree(cand_tree, tc);
+    olsr_spf_add_path_list(path_list, path_count, tc);
   }
 }
 
-static char *etx_to_string(int etx)
+/**
+ * Callback for the SPF backoff timer.
+ */
+static void
+olsr_expire_spf_backoff(void *context __attribute__((unused)))
 {
-  static char buff[10];
-
-  if (etx == INFINITE_ETX)
-    return "INF";
-
-  sprintf(buff, "%d", etx);
-  return buff;
+  spf_backoff_timer = NULL;
 }
 
-void olsr_calculate_lq_routing_table(void)
+void
+olsr_calculate_routing_table (void *context __attribute__((unused)))
 {
-  struct list vertex_list;
-  int i;
-  struct tc_entry *tcsrc;
-  struct topo_dst *tcdst;
-  struct dijk_vertex *vert;
-  struct link_entry *link;
+#ifdef SPF_PROFILING
+  struct timeval t1, t2, t3, t4, t5, spf_init, spf_run, route, kernel, total;
+#endif
+  struct avl_tree cand_tree;
+  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 neighbor_entry *neigh;
-  struct list_node *node;
-  struct dijk_vertex *myself;
-  struct dijk_vertex *walker;
-  int hops;
-  struct addresses *mid_walker;
-
-  // initialize the graph
-
-  list_init(&vertex_list);
-
-  // add ourselves to the vertex list
-
-  add_vertex(&vertex_list, &main_addr, 0);
-
-  // add our neighbours
-
-  for (i = 0; i < HASHSIZE; i++)
-    for (neigh = neighbortable[i].next; neigh != &neighbortable[i];
-         neigh = neigh->next)
-      if (neigh->status == SYM)
-        add_vertex(&vertex_list, &neigh->neighbor_main_addr, INFINITE_ETX);
-
-  // add remaining vertices
-
-  for (i = 0; i < HASHSIZE; i++)
-    for (tcsrc = tc_table[i].next; tcsrc != &tc_table[i]; tcsrc = tcsrc->next)
-    {
-      // add source
-
-      add_vertex(&vertex_list, &tcsrc->T_last_addr, INFINITE_ETX);
-
-      // add destinations of this source
-
-      for (tcdst = tcsrc->destinations.next; tcdst != &tcsrc->destinations;
-           tcdst = tcdst->next)
-        add_vertex(&vertex_list, &tcdst->T_dest_addr, INFINITE_ETX);
-    }
-
-  // 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)
-      {
-        link = olsr_neighbor_best_link(&neigh->neighbor_main_addr);
-
-        if (link->neigh_link_quality >= MIN_LINK_QUALITY)
-          add_edge(&vertex_list, &main_addr, &neigh->neighbor_main_addr,
-                   (int)(1.0 / link->neigh_link_quality));
+  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;
+  }
 
-        link = olsr_neighbor_best_inverse_link(&neigh->neighbor_main_addr);
+#ifdef SPF_PROFILING
+  gettimeofday(&t1, NULL);
+#endif
 
-        if (link->loss_link_quality >= MIN_LINK_QUALITY)
-          add_edge(&vertex_list, &neigh->neighbor_main_addr, &main_addr,
-                   (int)(1.0 / link->loss_link_quality));
+  /*
+   * Prepare the candidate tree and result list.
+   */
+  avl_init(&cand_tree, avl_comp_etx);
+  list_head_init(&path_list);
+  olsr_bump_routingtree_version();
+
+  /*
+   * Initialize vertices in the lsdb.
+   */
+  OLSR_FOR_ALL_TC_ENTRIES(tc) {
+    tc->next_hop = NULL;
+    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_cost = ZERO_ROUTE_COST;
+  olsr_spf_add_cand_tree(&cand_tree, tc_myself);
+
+  /*
+   * add edges to and from our neighbours.
+   */
+  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) {
+
+        /*
+         * 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;
       }
 
-  // add remaining edges
-
-  for (i = 0; i < HASHSIZE; i++)
-    for (tcsrc = tc_table[i].next; tcsrc != &tc_table[i]; tcsrc = tcsrc->next)
-      for (tcdst = tcsrc->destinations.next; tcdst != &tcsrc->destinations;
-           tcdst = tcdst->next)
-      {
-        if (tcdst->link_quality >= MIN_LINK_QUALITY)
-          add_edge(&vertex_list, &tcsrc->T_last_addr, &tcdst->T_dest_addr,
-                   (int)(1.0 / tcdst->link_quality));
-
-        if (tcdst->inverse_link_quality >= MIN_LINK_QUALITY)
-          add_edge(&vertex_list, &tcdst->T_dest_addr, &tcsrc->T_last_addr,
-                   (int)(1.0 / tcdst->inverse_link_quality));
+      /* 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);
       }
 
-  // run Dijkstra's algorithm
-
-  for (;;)
-  {
-    vert = extract_best(&vertex_list);
-
-    if (vert == NULL)
-      break;
-
-    relax(vert);
-  }
-
-  // save the old routing table
-
-  olsr_move_route_table(routingtable, old_routes);
-
-  node = list_get_head(&vertex_list);
-
-  // we're the first vertex in the list
-  
-  myself = node->data;
+      /*
+       * 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);
 
-  olsr_printf(2, "\n--- %02d:%02d:%02d.%02d ------------------------------------------------- DIJKSTRA\n\n",
-              nowtm->tm_hour,
-              nowtm->tm_min,
-              nowtm->tm_sec,
-              now.tv_usec/10000);
+#ifdef SPF_PROFILING
+  gettimeofday(&t2, NULL);
+#endif
 
-  for (node = list_get_next(node); node != NULL; node = list_get_next(node))
-  {
-    vert = node->data;
+  /*
+   * Run the SPF calculation.
+   */
+  olsr_spf_run_full(&cand_tree, &path_list, &path_count);
 
-    hops = 1;
+  OLSR_PRINTF(2, "\n--- %s ------------------------------------------------- DIJKSTRA\n\n",
+              olsr_wallclock_string());
 
-    // count hops to until the path ends or until we have reached a
-    // one-hop neighbour
+#ifdef SPF_PROFILING
+  gettimeofday(&t3, NULL);
+#endif
 
-    for (walker = vert; walker != NULL && walker->prev != myself;
-         walker = walker->prev)
-    {
-      olsr_printf(2, "%s:%s <- ", olsr_ip_to_string(&walker->addr),
-                  etx_to_string(walker->path_etx));
-      hops++;
+  /*
+   * In the path list we have all the reachable nodes in our topology.
+   */
+  for (; !list_is_empty(&path_list); list_remove(path_list.next)) {
+
+    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;
+#endif
+        OLSR_PRINTF(2, "SPF: %s no next-hop\n", olsr_ip_to_string(&buf, &tc->addr));
+      }
+#endif
+      continue;
     }
 
-    // if no path to a one-hop neighbour was found, ignore this node
+    /*
+     * 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)) {
 
-    if (walker != NULL)
-      olsr_printf(2, "%s:%s (one-hop)\n", olsr_ip_to_string(&walker->addr),
-                  etx_to_string(walker->path_etx));
+      rtp = rtp_prefix_tree2rtp(rtp_tree_node);
 
-    else
-    {
-      olsr_printf(2, "FAILED\n", olsr_ip_to_string(&vert->addr));
-      continue;
-    }
+      if (rtp->rtp_rt) {
 
-    // add a route to the main address of the destination node
+        /*
+         * If there is a route entry, the prefix is already in the global RIB.
+         */
+        olsr_update_rt_path(rtp, tc, link);
 
-    olsr_insert_routing_table(&vert->addr, &walker->addr, hops);
+      } else {
 
-    // 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)
-      olsr_insert_routing_table(&mid_walker->address, &walker->addr, hops);
+        /*
+         * The prefix is reachable and not yet in the global RIB.
+         * Build a rt_entry for it.
+         */
+        olsr_insert_rt_path(rtp, tc, link);
+      }
+    }
   }
 
-  // free the graph
+  /* Update the RIB based on the new SPF results */
+
+  olsr_update_rib_routes();
 
-  free_everything(&vertex_list);
+#ifdef SPF_PROFILING
+  gettimeofday(&t4, NULL);
+#endif
 
-  // move the route changes into the kernel
+  /* move the route changes into the kernel */
 
   olsr_update_kernel_routes();
 
-  // free the saved routing table
+#ifdef SPF_PROFILING
+  gettimeofday(&t5, NULL);
+#endif
 
-  olsr_free_routing_table(old_routes);
-}
+#ifdef SPF_PROFILING
+  timersub(&t2, &t1, &spf_init);
+  timersub(&t3, &t2, &spf_run);
+  timersub(&t4, &t3, &route);
+  timersub(&t5, &t4, &kernel);
+  timersub(&t5, &t1, &total);
+  OLSR_PRINTF(1, "\n--- SPF-stats for %d nodes, %d routes (total/init/run/route/kern): "
+              "%d, %d, %d, %d, %d\n",
+              path_count, routingtree.count,
+              (int)total.tv_usec, (int)spf_init.tv_usec, (int)spf_run.tv_usec,
+              (int)route.tv_usec, (int)kernel.tv_usec);
 #endif
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 2
+ * End:
+ */