* added -Wcast-qual: this killed lots of unnecessary type-casts
[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.60 2007/11/16 22:56:54 bernd67 Exp $
42  */
43
44 #define SPF_PROFILING 1
45
46 #include "defs.h"
47 #include "olsr.h"
48 #include "tc_set.h"
49 #include "neighbor_table.h"
50 #include "two_hop_neighbor_table.h"
51 #include "link_set.h"
52 #include "routing_table.h"
53 #include "mid_set.h"
54 #include "hna_set.h"
55 #include "lq_list.h"
56 #include "lq_avl.h"
57 #include "lq_route.h"
58 #include "net_olsr.h"
59
60 /*
61  * avl_comp_etx
62  *
63  * compare two etx metrics.
64  * return 0 if there is an exact match and
65  * -1 / +1 depending on being smaller or bigger.
66  * note that this results in the most optimal code
67  * after compiler optimization.
68  */
69 static int
70 avl_comp_etx (const void *etx1, const void *etx2)
71 {       
72   if (*(const float *)etx1 < *(const float *)etx2) {
73     return -1;
74   }
75
76   if (*(const float *)etx1 > *(const float *)etx2) {
77     return +1;
78   }
79
80   return 0;
81 }
82
83 /*
84  * olsr_spf_add_cand_tree
85  *
86  * Key an existing vertex to a candidate tree.
87  */
88 static void
89 olsr_spf_add_cand_tree (struct avl_tree *tree,
90                         struct tc_entry *vert)
91 {
92 #if !defined(NODEBUG) && defined(DEBUG)
93   struct ipaddr_str buf;
94 #endif
95   vert->cand_tree_node.key = &vert->path_etx;
96   vert->cand_tree_node.data = vert;
97
98 #ifdef DEBUG
99   OLSR_PRINTF(1, "SPF: insert candidate %s, cost %f\n",
100               olsr_ip_to_string(&buf, &vert->addr),
101               vert->path_etx);
102 #endif
103
104   avl_insert(tree, &vert->cand_tree_node, AVL_DUP);
105 }
106
107 /*
108  * olsr_spf_del_cand_tree
109  *
110  * Unkey an existing vertex from a candidate tree.
111  */
112 static void
113 olsr_spf_del_cand_tree (struct avl_tree *tree,
114                         struct tc_entry *vert)
115 {
116
117 #ifdef DEBUG
118 #ifndef NODEBUG
119   struct ipaddr_str buf;
120 #endif
121   OLSR_PRINTF(1, "SPF: delete candidate %s, cost %f\n",
122               olsr_ip_to_string(&buf, &vert->addr),
123               vert->path_etx);
124 #endif
125
126   avl_delete(tree, &vert->cand_tree_node);
127 }
128
129 /*
130  * olsr_spf_add_path_list
131  *
132  * Insert an SPF result at the end of the path list.
133  */
134 static void
135 olsr_spf_add_path_list (struct list_node *head,
136                         int *path_count,
137                         struct tc_entry *vert)
138 {
139 #if !defined(NODEBUG) && defined(DEBUG)
140   struct ipaddr_str pathbuf, nbuf;
141 #endif
142   vert->path_list_node.data = vert;
143
144 #ifdef DEBUG
145   OLSR_PRINTF(1, "SPF: append path %s, cost %f, via %s\n",
146               olsr_ip_to_string(&pathbuf, &vert->addr),
147               vert->path_etx,
148               vert->next_hop ? olsr_ip_to_string(&nbuf, &vert->next_hop->neighbor_iface_addr) : "-");
149 #endif
150
151   list_add_before(head, &vert->path_list_node);
152   *path_count = *path_count + 1;
153 }
154
155 /*
156  * olsr_spf_extract_best
157  *
158  * return the node with the minimum pathcost.
159  */
160 static struct tc_entry *
161 olsr_spf_extract_best (struct avl_tree *tree)
162 {
163   struct avl_node *node = avl_walk_first(tree);
164
165   return (node ? node->data :  NULL);
166 }
167
168
169 const char *olsr_etx_to_string(float etx)
170 {
171   static char buff[20];
172
173   if (etx == INFINITE_ETX) {
174     return "INF";
175   }
176   snprintf(buff, sizeof(buff), "%.6f", etx);
177   return buff;
178 }
179
180
181 /*
182  * olsr_spf_relax
183  *
184  * Explore all edges of a node and add the node
185  * to the candidate tree if the if the aggregate
186  * path cost is better.
187  */
188 static void
189 olsr_spf_relax (struct avl_tree *cand_tree, struct tc_entry *vert)
190 {
191   struct avl_node *edge_node;
192   float new_etx;
193
194 #ifdef DEBUG
195 #ifndef NODEBUG
196   struct ipaddr_str buf, nbuf;
197 #endif
198   OLSR_PRINTF(1, "SPF: exploring node %s, cost %f\n",
199               olsr_ip_to_string(&buf, &vert->addr),
200               vert->path_etx);
201 #endif
202
203   /*
204    * loop through all edges of this vertex.
205    */
206   for (edge_node = avl_walk_first(&vert->edge_tree);
207        edge_node;
208        edge_node = avl_walk_next(edge_node)) {
209     struct tc_entry *new_vert;
210     struct tc_edge_entry *tc_edge = edge_node->data;
211
212     /*
213      * We are not interested in dead-end or dying edges.
214      */
215     if (!tc_edge->edge_inv || (tc_edge->flags & OLSR_TC_EDGE_DOWN)) {
216 #ifdef DEBUG
217       OLSR_PRINTF(1, "SPF:   ignoring edge %s\n",
218                   olsr_ip_to_string(&buf, &tc_edge->T_dest_addr));
219       if (tc_edge->flags & OLSR_TC_EDGE_DOWN) {
220         OLSR_PRINTF(1, "SPF:     edge down\n");
221       }
222       if (!tc_edge->edge_inv) {
223         OLSR_PRINTF(1, "SPF:     no inverse edge\n");
224       }
225 #endif
226       continue;
227     }
228
229     /*
230      * total quality of the path through this vertex
231      * to the destination of this edge
232      */
233     new_etx = vert->path_etx + tc_edge->etx;
234
235 #ifdef DEBUG
236       OLSR_PRINTF(1, "SPF:   exploring edge %s, cost %s\n",
237                   olsr_ip_to_string(&buf, &tc_edge->T_dest_addr),
238                   olsr_etx_to_string(new_etx));
239 #endif
240
241       /* 
242        * if it's better than the current path quality of this edge's
243        * destination node, then we've found a better path to this node.
244        */
245     new_vert = tc_edge->edge_inv->tc;
246
247     if (new_etx < new_vert->path_etx) {
248
249       /* if this node has been on the candidate tree delete it */
250       if (new_vert->path_etx != INFINITE_ETX) {
251         olsr_spf_del_cand_tree(cand_tree, new_vert);
252       }
253
254       /* re-insert on candidate tree with the better metric */
255       new_vert->path_etx = new_etx;
256       olsr_spf_add_cand_tree(cand_tree, new_vert);
257
258       /* pull-up the next-hop and bump the hop count */
259       if (vert->next_hop) {
260         new_vert->next_hop = vert->next_hop;
261       }
262       new_vert->hops = vert->hops + 1;
263
264 #ifdef DEBUG
265       OLSR_PRINTF(1, "SPF:   better path to %s, cost %s -> %s, via %s, hops %u\n",
266                   olsr_ip_to_string(&buf, &new_vert->addr),
267                   olsr_etx_to_string(new_vert->path_etx),
268                   olsr_etx_to_string(new_etx),
269                   vert->next_hop ? olsr_ip_to_string(&nbuf, &vert->next_hop->neighbor_iface_addr) : "<none>",
270                   new_vert->hops);
271 #endif
272
273     }
274   }
275 }
276
277 /*
278  * olsr_spf_run_full
279  *
280  * Run the Dijkstra algorithm.
281  * 
282  * A node gets added to the candidate tree when one of its edges has
283  * an overall better root path cost than the node itself.
284  * The node with the shortest metric gets moved from the candidate to
285  * the path list every pass.
286  * The SPF computation is completed when there are no more nodes
287  * on the candidate tree. 
288  */ 
289 static void
290 olsr_spf_run_full (struct avl_tree *cand_tree, struct list_node *path_list,
291                    int *path_count)
292 {
293   struct tc_entry *vert;
294
295   *path_count = 0;
296
297   while ((vert = olsr_spf_extract_best(cand_tree))) {
298
299     olsr_spf_relax(cand_tree, vert);
300
301     /*
302      * move the best path from the candidate tree
303      * to the path list.
304      */
305     olsr_spf_del_cand_tree(cand_tree, vert);
306     olsr_spf_add_path_list(path_list, path_count, vert);
307   }
308 }
309
310 void
311 olsr_calculate_routing_table (void)
312 {
313   struct avl_tree cand_tree;
314   struct list_node path_list;
315   int i, plen, path_count = 0;
316   struct tc_entry *tc;
317   struct tc_edge_entry *tc_edge;
318   struct tc_entry *vert;
319   struct neighbor_entry *neigh;
320   struct mid_address *mid_walker;
321   struct hna_entry *hna_gw;
322   struct hna_net *hna;
323   struct interface *inter;
324   struct link_entry *link;
325
326 #ifdef SPF_PROFILING
327   struct timeval t1, t2, t3, t4, t5, spf_init, spf_run, route, kernel, total;
328
329   gettimeofday(&t1, NULL);
330 #endif
331
332   /*
333    * Prepare the candidate tree and result list.
334    */
335   avl_init(&cand_tree, avl_comp_etx);
336   list_head_init(&path_list);
337   olsr_bump_routingtree_version();
338
339   /*
340    * Initialize vertices in the lsdb.
341    */
342   OLSR_FOR_ALL_TC_ENTRIES(tc) {
343     tc->next_hop = NULL;
344     tc->path_etx = INFINITE_ETX;
345     tc->hops = 0;
346   } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
347
348   /*
349    * zero ourselves and add us to the candidate tree.
350    */
351   olsr_change_myself_tc();
352   tc_myself->path_etx = ZERO_ETX;
353   olsr_spf_add_cand_tree(&cand_tree, tc_myself);
354
355   /*
356    * add edges to and from our neighbours.
357    */
358   for (i = 0; i < HASHSIZE; i++)
359     for (neigh = neighbortable[i].next; neigh != &neighbortable[i];
360          neigh = neigh->next) {
361
362       if (neigh->status == SYM) {
363
364         tc_edge = olsr_lookup_tc_edge(tc_myself, &neigh->neighbor_main_addr);
365         link = get_best_link_to_neighbor(&neigh->neighbor_main_addr);
366         if (!link) {
367
368           /*
369            * If there is no best link to this neighbor
370            * and we had an edge before then flush the edge.
371            */
372           if (tc_edge) {
373             olsr_delete_tc_edge_entry(tc_edge);
374           }
375           continue;
376         }
377
378         /*
379          * Set the next-hops of our neighbors. 
380          */
381         if (!tc_edge) {
382           tc_edge = olsr_add_tc_edge_entry(tc_myself, &neigh->neighbor_main_addr,
383                                            0, link->last_htime,
384                                            link->loss_link_quality2,
385                                            link->neigh_link_quality2);
386         } else {
387           tc_edge->link_quality = link->loss_link_quality2;
388           tc_edge->inverse_link_quality = link->neigh_link_quality2;
389           olsr_calc_tc_edge_entry_etx(tc_edge);
390         }
391         if (tc_edge->edge_inv) {
392           tc_edge->edge_inv->tc->next_hop = link;
393         }
394       }
395     }
396
397 #ifdef SPF_PROFILING
398   gettimeofday(&t2, NULL);
399 #endif
400
401   /*
402    * Run the SPF calculation.
403    */
404   olsr_spf_run_full(&cand_tree, &path_list, &path_count);
405
406   OLSR_PRINTF(2, "\n--- %02d:%02d:%02d.%02d ------------------------------------------------- DIJKSTRA\n\n",
407               nowtm->tm_hour,
408               nowtm->tm_min,
409               nowtm->tm_sec,
410               (int)now.tv_usec/10000);
411
412 #ifdef SPF_PROFILING
413   gettimeofday(&t3, NULL);
414 #endif
415
416   /*
417    * In the path tree we have all the reachable nodes in our topology.
418    */
419   for (; !list_is_empty(&path_list); list_remove(path_list.next)) {
420
421     vert = path_list.next->data;
422     link = vert->next_hop;
423
424     if (!link) {
425 #ifndef NODEBUG
426       struct ipaddr_str buf;
427 #endif
428       OLSR_PRINTF(2, "%s no next-hop\n", olsr_ip_to_string(&buf, &vert->addr));
429       continue;
430     }
431
432     /* find the interface for the found link */
433     inter = link->if_name ? if_ifwithname(link->if_name)
434       : if_ifwithaddr(&link->local_iface_addr);
435
436     /* interface is up ? */
437     if (inter) {
438
439       /* add a route to the main address of the destination node */
440       olsr_insert_routing_table(&vert->addr, olsr_cnf->maxplen, &vert->addr,
441                                 &link->neighbor_iface_addr, inter->if_index,
442                                 vert->hops, vert->path_etx);
443
444       /* add routes to the remaining interfaces of the destination node */
445       for (mid_walker = mid_lookup_aliases(&vert->addr);
446            mid_walker != NULL;
447            mid_walker = mid_walker->next_alias) {
448
449         olsr_insert_routing_table(&mid_walker->alias, olsr_cnf->maxplen, &vert->addr,
450                                   &link->neighbor_iface_addr, inter->if_index,
451                                   vert->hops, vert->path_etx);
452       }
453
454       /* find the node's HNAs */
455       hna_gw = olsr_lookup_hna_gw(&vert->addr);
456
457       /* node doesn't announce any HNAs */
458       if (!hna_gw) {
459         continue;
460       }
461
462       /* loop through the node's HNAs */
463       for (hna = hna_gw->networks.next;
464            hna != &hna_gw->networks;
465            hna = hna->next) {
466
467         plen = olsr_get_hna_prefix_len(hna);
468         if (vert->path_etx != INFINITE_ETX)
469         olsr_insert_routing_table(&hna->A_network_addr, plen, &vert->addr,
470                                   &link->neighbor_iface_addr, inter->if_index,
471                                   vert->hops, vert->path_etx);
472       }
473     }
474   }
475
476 #ifdef SPF_PROFILING
477   gettimeofday(&t4, NULL);
478 #endif
479
480   /* move the route changes into the kernel */
481
482   olsr_update_kernel_routes();
483
484 #ifdef SPF_PROFILING
485   gettimeofday(&t5, NULL);
486 #endif
487
488 #ifdef SPF_PROFILING
489   timersub(&t2, &t1, &spf_init);
490   timersub(&t3, &t2, &spf_run);
491   timersub(&t4, &t3, &route);
492   timersub(&t5, &t4, &kernel);
493   timersub(&t5, &t1, &total);
494   OLSR_PRINTF(1, "\n--- SPF-stats for %d nodes, %d routes (total/init/run/route/kern): "
495               "%d, %d, %d, %d, %d\n",
496               path_count, routingtree.count,
497               (int)total.tv_usec, (int)spf_init.tv_usec, (int)spf_run.tv_usec,
498               (int)route.tv_usec, (int)kernel.tv_usec);
499 #endif
500 }
501
502 /*
503  * Local Variables:
504  * c-basic-offset: 2
505  * End:
506  */