2 * The olsr.org Optimized Link-State Routing daemon(olsrd)
3 * Copyright (c) 2004, Thomas Lopatic (thomas@lopatic.de)
4 * IPv4 performance optimization (c) 2006, sven-ola(gmx.de)
5 * SPF implementation (c) 2007, Hannes Gredler (hannes@gredler.at)
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
18 * * Neither the name of olsr.org, olsrd nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
35 * Visit http://www.olsr.org for more information.
37 * If you find this software useful feel free to make a donation
38 * to the project. For more information see the website or contact
39 * the copyright holders.
41 * $Id: lq_route.c,v 1.54 2007/10/20 12:59:08 bernd67 Exp $
47 #include "neighbor_table.h"
48 #include "two_hop_neighbor_table.h"
50 #include "routing_table.h"
60 * compare two etx metrics.
61 * return 0 if there is an exact match and
62 * -1 / +1 depending on being smaller or bigger.
63 * note that this results in the most optimal code
64 * after compiler optimization.
67 avl_comp_etx (void *etx1, void *etx2)
69 if (*(float *)etx1 < *(float *)etx2) {
73 if (*(float *)etx1 > *(float *)etx2) {
81 * olsr_spf_add_cand_tree
83 * Key an existing vertex to a candidate tree.
86 olsr_spf_add_cand_tree (struct avl_tree *tree,
87 struct tc_entry *vert)
89 vert->cand_tree_node.key = &vert->path_etx;
90 vert->cand_tree_node.data = vert;
93 OLSR_PRINTF(1, "SPF: insert candidate %s, cost %f\n",
94 olsr_ip_to_string(&(vert->addr)),
98 avl_insert(tree, &vert->cand_tree_node, AVL_DUP);
102 * olsr_spf_del_cand_tree
104 * Unkey an existing vertex from a candidate tree.
107 olsr_spf_del_cand_tree (struct avl_tree *tree,
108 struct tc_entry *vert)
112 OLSR_PRINTF(1, "SPF: delete candidate %s, cost %f\n",
113 olsr_ip_to_string(&(vert->addr)),
117 avl_delete(tree, &vert->cand_tree_node);
121 * olsr_spf_add_path_list
123 * Insert an SPF result at the end of the path list.
126 olsr_spf_add_path_list (struct list_node *head,
128 struct tc_entry *vert)
130 vert->path_list_node.data = vert;
133 OLSR_PRINTF(1, "SPF: append path %s, cost %f, via %s\n",
134 olsr_ip_to_string(&(vert->addr)),
136 olsr_ip_to_string(vert->next_hop ? &vert->next_hop->neighbor_iface_addr : NULL));
139 list_add_before(head, &vert->path_list_node);
140 *path_count = *path_count + 1;
144 * olsr_spf_extract_best
146 * return the node with the minimum pathcost.
148 static struct tc_entry *
149 olsr_spf_extract_best (struct avl_tree *tree)
151 struct avl_node *node;
153 node = avl_walk_first(tree);
155 return (node ? node->data : NULL);
159 char *olsr_etx_to_string(float etx)
161 static char buff[20];
163 if (etx == INFINITE_ETX)
166 snprintf(buff, 20, "%.6f", etx);
174 * Explore all edges of a node and add the node
175 * to the candidate tree if the if the aggregate
176 * path cost is better.
179 olsr_spf_relax (struct avl_tree *cand_tree, struct tc_entry *vert)
181 struct tc_entry *new_vert;
182 struct tc_edge_entry *tc_edge;
183 struct avl_node *edge_node;
187 OLSR_PRINTF(1, "SPF: exploring node %s, cost %f\n",
188 olsr_ip_to_string(&(vert->addr)),
193 * loop through all edges of this vertex.
195 for (edge_node = avl_walk_first(&vert->edge_tree);
197 edge_node = avl_walk_next(edge_node)) {
199 tc_edge = edge_node->data;
202 * We are not interested in dead-end or dying edges.
204 if (!tc_edge->edge_inv || (tc_edge->flags & OLSR_TC_EDGE_DOWN)) {
206 OLSR_PRINTF(1, "SPF: ignoring edge %s\n",
207 olsr_ip_to_string(&tc_edge->T_dest_addr));
208 if (tc_edge->flags & OLSR_TC_EDGE_DOWN) {
209 OLSR_PRINTF(1, "SPF: edge down\n");
211 if (!tc_edge->edge_inv) {
212 OLSR_PRINTF(1, "SPF: no inverse edge\n");
219 * total quality of the path through this vertex
220 * to the destination of this edge
222 new_etx = vert->path_etx + tc_edge->etx;
225 OLSR_PRINTF(1, "SPF: exploring edge %s, cost %s\n",
226 olsr_ip_to_string(&(tc_edge->T_dest_addr)),
227 olsr_etx_to_string(new_vert->path_etx));
231 * if it's better than the current path quality of this edge's
232 * destination node, then we've found a better path to this node.
234 new_vert = tc_edge->edge_inv->tc;
235 if (new_etx < new_vert->path_etx) {
237 /* if this node has been on the candidate tree delete it */
238 if (new_vert->path_etx != INFINITE_ETX) {
239 olsr_spf_del_cand_tree(cand_tree, new_vert);
242 /* re-insert on candidate tree with the better metric */
243 new_vert->path_etx = new_etx;
244 olsr_spf_add_cand_tree(cand_tree, new_vert);
246 /* pull-up the next-hop and bump the hop count */
247 if (vert->next_hop) {
248 new_vert->next_hop = vert->next_hop;
250 new_vert->hops = vert->hops + 1;
253 OLSR_PRINTF(1, "SPF: better path to %s, cost %s -> %s, via %s, hops %u\n",
254 olsr_ip_to_string(&new_vert->addr),
255 olsr_etx_to_string(new_vert->path_etx),
256 olsr_etx_to_string(new_etx),
257 olsr_ip_to_string(vert->next_hop ?
258 &vert->next_hop->neighbor_iface_addr : NULL),
269 * Run the Dijkstra algorithm.
271 * A node gets added to the candidate tree when one of its edges has
272 * an overall better root path cost than the node itself.
273 * The node with the shortest metric gets moved from the candidate to
274 * the path list every pass.
275 * The SPF computation is completed when there are no more nodes
276 * on the candidate tree.
279 olsr_spf_run_full (struct avl_tree *cand_tree, struct list_node *path_list,
282 struct tc_entry *vert;
286 while ((vert = olsr_spf_extract_best(cand_tree))) {
288 olsr_spf_relax(cand_tree, vert);
291 * move the best path from the candidate tree
294 olsr_spf_del_cand_tree(cand_tree, vert);
295 olsr_spf_add_path_list(path_list, path_count, vert);
300 olsr_calculate_routing_table (void)
302 struct avl_tree cand_tree;
303 struct list_node path_list;
304 int i, plen, path_count = 0;
306 struct tc_edge_entry *tc_edge;
307 struct tc_entry *vert;
308 struct neighbor_entry *neigh;
309 struct mid_address *mid_walker;
310 struct hna_entry *hna_gw;
312 struct interface *inter;
313 struct link_entry *link;
316 struct timeval t1, t2, t3, t4, t5, spf_init, spf_run, route, kernel, total;
318 gettimeofday(&t1, NULL);
322 * Prepare the candidate tree and result list.
324 avl_init(&cand_tree, avl_comp_etx);
325 list_head_init(&path_list);
326 olsr_bump_routingtree_version();
329 * Initialize vertices in the lsdb.
331 OLSR_FOR_ALL_TC_ENTRIES(tc) {
333 tc->path_etx = INFINITE_ETX;
335 } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
338 * zero ourselves and add us to the candidate tree.
340 olsr_change_myself_tc();
341 tc_myself->path_etx = ZERO_ETX;
342 olsr_spf_add_cand_tree(&cand_tree, tc_myself);
345 * add edges to and from our neighbours.
347 for (i = 0; i < HASHSIZE; i++)
348 for (neigh = neighbortable[i].next; neigh != &neighbortable[i];
349 neigh = neigh->next) {
351 if (neigh->status == SYM) {
353 tc_edge = olsr_lookup_tc_edge(tc_myself, &neigh->neighbor_main_addr);
354 link = get_best_link_to_neighbor(&neigh->neighbor_main_addr);
358 * If there is no best link to this neighbor
359 * and we had an edge before then flush the edge.
362 olsr_delete_tc_edge_entry(tc_edge);
368 * Set the next-hops of our neighbors.
371 tc_edge = olsr_add_tc_edge_entry(tc_myself, &neigh->neighbor_main_addr,
373 link->loss_link_quality2,
374 link->neigh_link_quality2);
376 tc_edge->link_quality = link->loss_link_quality2;
377 tc_edge->inverse_link_quality = link->neigh_link_quality2;
378 olsr_calc_tc_edge_entry_etx(tc_edge);
380 if (tc_edge->edge_inv) {
381 tc_edge->edge_inv->tc->next_hop = link;
387 gettimeofday(&t2, NULL);
391 * Run the SPF calculation.
393 olsr_spf_run_full(&cand_tree, &path_list, &path_count);
395 OLSR_PRINTF(2, "\n--- %02d:%02d:%02d.%02d ------------------------------------------------- DIJKSTRA\n\n",
399 (int)now.tv_usec/10000);
402 gettimeofday(&t3, NULL);
406 * In the path tree we have all the reachable nodes in our topology.
408 for (; !list_is_empty(&path_list); list_remove(path_list.next)) {
410 vert = path_list.next->data;
411 link = vert->next_hop;
414 OLSR_PRINTF(2, "%s no next-hop\n", olsr_ip_to_string(&vert->addr));
418 /* find the interface for the found link */
419 inter = link->if_name ? if_ifwithname(link->if_name)
420 : if_ifwithaddr(&link->local_iface_addr);
422 /* interface is up ? */
425 /* add a route to the main address of the destination node */
426 olsr_insert_routing_table(&vert->addr, olsr_cnf->maxplen, &vert->addr,
427 &link->neighbor_iface_addr, inter->if_index,
428 vert->hops, vert->path_etx);
430 /* add routes to the remaining interfaces of the destination node */
431 for (mid_walker = mid_lookup_aliases(&vert->addr);
433 mid_walker = mid_walker->next_alias) {
435 olsr_insert_routing_table(&mid_walker->alias, olsr_cnf->maxplen, &vert->addr,
436 &link->neighbor_iface_addr, inter->if_index,
437 vert->hops, vert->path_etx);
440 /* find the node's HNAs */
441 hna_gw = olsr_lookup_hna_gw(&vert->addr);
443 /* node doesn't announce any HNAs */
448 /* loop through the node's HNAs */
449 for (hna = hna_gw->networks.next;
450 hna != &hna_gw->networks;
453 plen = olsr_get_hna_prefix_len(hna);
454 if (vert->path_etx != INFINITE_ETX)
455 olsr_insert_routing_table(&hna->A_network_addr, plen, &vert->addr,
456 &link->neighbor_iface_addr, inter->if_index,
457 vert->hops, vert->path_etx);
463 gettimeofday(&t4, NULL);
466 /* move the route changes into the kernel */
468 olsr_update_kernel_routes();
471 gettimeofday(&t5, NULL);
475 timersub(&t2, &t1, &spf_init);
476 timersub(&t3, &t2, &spf_run);
477 timersub(&t4, &t3, &route);
478 timersub(&t5, &t4, &kernel);
479 timersub(&t5, &t1, &total);
480 olsr_printf(1, "\n--- SPF-stats for %d nodes, %d routes (total/init/run/route/kern): "
481 "%d, %d, %d, %d, %d\n",
482 path_count, routingtree.count,
483 (int)total.tv_usec, (int)spf_init.tv_usec, (int)spf_run.tv_usec,
484 (int)route.tv_usec, (int)kernel.tv_usec);