* applied http://gredler.at/download/olsrd/neighbor_routes3.diff
[olsrd.git] / src / lq_route.c
1 /*
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)
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without 
9  * modification, are permitted provided that the following conditions 
10  * are met:
11  *
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 
17  *   distribution.
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.
21  *
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.
34  *
35  * Visit http://www.olsr.org for more information.
36  *
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.
40  *
41  * $Id: lq_route.c,v 1.53 2007/10/16 09:54:43 bernd67 Exp $
42  */
43
44 #include "defs.h"
45 #include "olsr.h"
46 #include "tc_set.h"
47 #include "neighbor_table.h"
48 #include "two_hop_neighbor_table.h"
49 #include "link_set.h"
50 #include "routing_table.h"
51 #include "mid_set.h"
52 #include "hna_set.h"
53 #include "lq_list.h"
54 #include "lq_avl.h"
55 #include "lq_route.h"
56
57 /*
58  * avl_comp_etx
59  *
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.
65  */
66 static int
67 avl_comp_etx (void *etx1, void *etx2)
68 {       
69   if (*(float *)etx1 < *(float *)etx2) {
70     return -1;
71   }
72
73   if (*(float *)etx1 > *(float *)etx2) {
74     return +1;
75   }
76
77   return 0;
78 }
79
80 /*
81  * olsr_spf_add_cand_tree
82  *
83  * Key an existing vertex to a candidate tree.
84  */
85 static void
86 olsr_spf_add_cand_tree (struct avl_tree *tree,
87                         struct tc_entry *vert)
88 {
89   vert->cand_tree_node.key = &vert->path_etx;
90   vert->cand_tree_node.data = vert;
91
92 #ifdef DEBUG
93   OLSR_PRINTF(1, "SPF: insert candidate %s, cost %f\n",
94               olsr_ip_to_string(&(vert->addr)),
95               vert->path_etx);
96 #endif
97
98   avl_insert(tree, &vert->cand_tree_node, AVL_DUP);
99 }
100
101 /*
102  * olsr_spf_del_cand_tree
103  *
104  * Unkey an existing vertex from a candidate tree.
105  */
106 static void
107 olsr_spf_del_cand_tree (struct avl_tree *tree,
108                         struct tc_entry *vert)
109 {
110
111 #ifdef DEBUG
112   OLSR_PRINTF(1, "SPF: delete candidate %s, cost %f\n",
113               olsr_ip_to_string(&(vert->addr)),
114               vert->path_etx);
115 #endif
116
117   avl_delete(tree, &vert->cand_tree_node);
118 }
119
120 /*
121  * olsr_spf_add_path_list
122  *
123  * Insert an SPF result at the end of the path list.
124  */
125 static void
126 olsr_spf_add_path_list (struct list_node *head,
127                         int *path_count,
128                         struct tc_entry *vert)
129 {
130   vert->path_list_node.data = vert;
131
132 #ifdef DEBUG
133   OLSR_PRINTF(1, "SPF: append path %s, cost %f, via %s\n",
134               olsr_ip_to_string(&(vert->addr)),
135               vert->path_etx,
136               olsr_ip_to_string(vert->next_hop ? &vert->next_hop->neighbor_iface_addr : NULL));
137 #endif
138
139   list_add_before(head, &vert->path_list_node);
140   *path_count = *path_count + 1;
141 }
142
143 /*
144  * olsr_spf_extract_best
145  *
146  * return the node with the minimum pathcost.
147  */
148 static struct tc_entry *
149 olsr_spf_extract_best (struct avl_tree *tree)
150 {
151   struct avl_node *node;
152
153   node = avl_walk_first(tree);
154
155   return (node ? node->data :  NULL);
156 }
157
158
159 char *olsr_etx_to_string(float etx)
160 {
161   static char buff[20];
162
163   if (etx == INFINITE_ETX)
164     return "INF";
165
166   snprintf(buff, 20, "%.6f", etx);
167   return buff;
168 }
169
170
171 /*
172  * olsr_spf_relax
173  *
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.
177  */
178 static void
179 olsr_spf_relax (struct avl_tree *cand_tree, struct tc_entry *vert)
180 {
181   struct tc_entry *new_vert;
182   struct tc_edge_entry *tc_edge;
183   struct avl_node *edge_node;
184   float new_etx;
185
186 #ifdef DEBUG
187   OLSR_PRINTF(1, "SPF: exploring node %s, cost %f\n",
188               olsr_ip_to_string(&(vert->addr)),
189               vert->path_etx);
190 #endif
191
192   /*
193    * loop through all edges of this vertex.
194    */
195   for (edge_node = avl_walk_first(&vert->edge_tree);
196        edge_node;
197        edge_node = avl_walk_next(edge_node)) {
198
199     tc_edge = edge_node->data;
200
201     /*
202      * We are not interested in dead-end or dying edges.
203      */
204     if (!tc_edge->edge_inv || (tc_edge->flags & OLSR_TC_EDGE_DOWN)) {
205 #ifdef DEBUG
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");
210       }
211       if (!tc_edge->edge_inv) {
212         OLSR_PRINTF(1, "SPF:     no inverse edge\n");
213       }
214 #endif
215       continue;
216     }
217
218     /*
219      * total quality of the path through this vertex
220      * to the destination of this edge
221      */
222     new_etx = vert->path_etx + tc_edge->etx;
223
224 #ifdef DEBUG
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));
228 #endif
229
230       /* 
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.
233        */
234     new_vert = tc_edge->edge_inv->tc;
235     if (new_etx < new_vert->path_etx) {
236
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);
240       }
241
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);
245
246       /* pull-up the next-hop and bump the hop count */
247       if (vert->next_hop) {
248         new_vert->next_hop = vert->next_hop;
249       }
250       new_vert->hops = vert->hops + 1;
251
252 #ifdef DEBUG
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),
259                   new_vert->->hops);
260 #endif
261
262     }
263   }
264 }
265
266 /*
267  * olsr_spf_run_full
268  *
269  * Run the Dijkstra algorithm.
270  * 
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. 
277  */ 
278 static void
279 olsr_spf_run_full (struct avl_tree *cand_tree, struct list_node *path_list,
280                    int *path_count)
281 {
282   struct tc_entry *vert;
283
284   *path_count = 0;
285
286   while ((vert = olsr_spf_extract_best(cand_tree))) {
287
288     olsr_spf_relax(cand_tree, vert);
289
290     /*
291      * move the best path from the candidate tree
292      * to the path list.
293      */
294     olsr_spf_del_cand_tree(cand_tree, vert);
295     olsr_spf_add_path_list(path_list, path_count, vert);
296   }
297 }
298
299 void
300 olsr_calculate_routing_table (void)
301 {
302   struct avl_tree cand_tree;
303   struct list_node path_list;
304   int i, plen, path_count = 0;
305   struct tc_entry *tc;
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;
311   struct hna_net *hna;
312   struct interface *inter;
313   struct link_entry *link;
314
315 #ifdef SPF_PROFILING
316   struct timeval t1, t2, t3, t4, t5, spf_init, spf_run, route, kernel, total;
317
318   gettimeofday(&t1, NULL);
319 #endif
320
321   /*
322    * Prepare the candidate tree and result list.
323    */
324   avl_init(&cand_tree, avl_comp_etx);
325   list_head_init(&path_list);
326   olsr_bump_routingtree_version();
327
328   /*
329    * Initialize vertices in the lsdb.
330    */
331   OLSR_FOR_ALL_TC_ENTRIES(tc) {
332     tc->next_hop = NULL;
333     tc->path_etx = INFINITE_ETX;
334     tc->hops = 0;
335   } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
336
337   /*
338    * zero ourselves and add us to the candidate tree.
339    */
340   olsr_change_myself_tc();
341   tc_myself->path_etx = ZERO_ETX;
342   olsr_spf_add_cand_tree(&cand_tree, tc_myself);
343
344   /*
345    * add edges to and from our neighbours.
346    */
347   for (i = 0; i < HASHSIZE; i++)
348     for (neigh = neighbortable[i].next; neigh != &neighbortable[i];
349          neigh = neigh->next) {
350
351       if (neigh->status == SYM) {
352
353         tc_edge = olsr_lookup_tc_edge(tc_myself, &neigh->neighbor_main_addr);
354         link = get_best_link_to_neighbor(&neigh->neighbor_main_addr);
355         if (!link) {
356
357           /*
358            * If there is no best link to this neighbor
359            * and we had an edge before then flush the edge.
360            */
361           if (tc_edge) {
362             olsr_delete_tc_edge_entry(tc_edge);
363           }
364           continue;
365         }
366
367         /*
368          * Set the next-hops of our neighbors. 
369          */
370         if (!tc_edge) {
371           tc_edge = olsr_add_tc_edge_entry(tc_myself, &neigh->neighbor_main_addr,
372                                            0, link->last_htime,
373                                            link->loss_link_quality2,
374                                            link->neigh_link_quality2);
375         } else {
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);
379         }
380         if (tc_edge->edge_inv) {
381           tc_edge->edge_inv->tc->next_hop = link;
382         }
383       }
384     }
385
386 #ifdef SPF_PROFILING
387   gettimeofday(&t2, NULL);
388 #endif
389
390   /*
391    * Run the SPF calculation.
392    */
393   olsr_spf_run_full(&cand_tree, &path_list, &path_count);
394
395   OLSR_PRINTF(2, "\n--- %02d:%02d:%02d.%02d ------------------------------------------------- DIJKSTRA\n\n",
396               nowtm->tm_hour,
397               nowtm->tm_min,
398               nowtm->tm_sec,
399               (int)now.tv_usec/10000);
400
401 #ifdef SPF_PROFILING
402   gettimeofday(&t3, NULL);
403 #endif
404
405   /*
406    * In the path tree we have all the reachable nodes in our topology.
407    */
408   for (; !list_is_empty(&path_list); list_remove(path_list.next)) {
409
410     vert = path_list.next->data;
411     link = vert->next_hop;
412
413     if (!link) {
414       OLSR_PRINTF(2, "%s no next-hop\n", olsr_ip_to_string(&vert->addr));
415       continue;
416     }
417
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);
421
422     /* interface is up ? */
423     if (inter) {
424
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);
429
430       /* add routes to the remaining interfaces of the destination node */
431       for (mid_walker = mid_lookup_aliases(&vert->addr);
432            mid_walker != NULL;
433            mid_walker = mid_walker->next_alias) {
434
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);
438       }
439
440       /* find the node's HNAs */
441       hna_gw = olsr_lookup_hna_gw(&vert->addr);
442
443       /* node doesn't announce any HNAs */
444       if (!hna_gw) {
445         continue;
446       }
447
448       /* loop through the node's HNAs */
449       for (hna = hna_gw->networks.next;
450            hna != &hna_gw->networks;
451            hna = hna->next) {
452
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);
458       }
459     }
460   }
461
462 #ifdef SPF_PROFILING
463   gettimeofday(&t4, NULL);
464 #endif
465
466   /* move the route changes into the kernel */
467
468   olsr_update_kernel_routes();
469
470 #ifdef SPF_PROFILING
471   gettimeofday(&t5, NULL);
472 #endif
473
474 #ifdef SPF_PROFILING
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);
485 #endif
486 }
487
488 /*
489  * Local Variables:
490  * c-basic-offset: 2
491  * End:
492  */