b8aeb939a10805834a373cac68f8cae533eed94b
[olsrd.git] / src / routing_table.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004, Andreas T√łnnesen(andreto@olsr.org)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright 
11  *   notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright 
13  *   notice, this list of conditions and the following disclaimer in 
14  *   the documentation and/or other materials provided with the 
15  *   distribution.
16  * * Neither the name of olsr.org, olsrd nor the names of its 
17  *   contributors may be used to endorse or promote products derived 
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
24  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Visit http://www.olsr.org for more information.
34  *
35  * If you find this software useful feel free to make a donation
36  * to the project. For more information see the website or contact
37  * the copyright holders.
38  *
39  * $Id: routing_table.c,v 1.27 2007/04/25 22:08:14 bernd67 Exp $
40  */
41
42
43
44 #include "defs.h"
45 #include "two_hop_neighbor_table.h"
46 #include "tc_set.h"
47 #include "mid_set.h"
48 #include "neighbor_table.h"
49 #include "olsr.h"
50 #include "link_set.h"
51 #include "routing_table.h"
52
53
54 struct rt_entry routingtable[HASHSIZE];
55 struct rt_entry hna_routes[HASHSIZE];
56
57
58 /* Begin:
59  * Prototypes for internal functions 
60  */
61
62 static int
63 olsr_fill_routing_table_with_neighbors(void);
64
65 static struct destination_n *
66 olsr_fill_routing_table_with_two_hop_neighbors(void);
67
68 static struct rt_entry *
69 olsr_check_for_higher_quality(struct rt_entry *, struct hna_net *, float);
70
71 struct rt_entry *
72 olsr_check_for_lower_quality(struct rt_entry *, struct hna_net *, float);
73
74 static olsr_bool
75 two_hop_neighbor_reachable(struct neighbor_2_list_entry *);
76
77 /* End:
78  * Prototypes for internal functions 
79  */
80
81
82 /**
83  *Initialize the routing table
84  */
85 int
86 olsr_init_routing_table(void)
87 {
88   int index;
89   /*
90    *The hna routes hash will almost always
91    *be indexed to 0
92    *But it is kept as a hash to be compatible
93    *with the functions used on the regular
94    *routing table
95    */
96   for(index=0;index<HASHSIZE;index++)
97     {
98       routingtable[index].next = &routingtable[index];
99       routingtable[index].prev = &routingtable[index];
100       hna_routes[index].next = &hna_routes[index];
101       hna_routes[index].prev = &hna_routes[index];
102     }
103   return 1;
104 }
105
106 /**
107  *Look up an entry in the routing table.
108  *
109  *@param dst the address of the entry
110  *
111  *@return a pointer to a rt_entry struct 
112  *representing the route entry.
113  */
114 struct rt_entry *
115 olsr_lookup_routing_table(union olsr_ip_addr *dst)
116 {
117
118   struct rt_entry *rt_table;
119   olsr_u32_t      hash = olsr_hashing(dst);
120
121   for(rt_table = routingtable[hash].next;
122       rt_table != &routingtable[hash];
123       rt_table = rt_table->next)
124     {
125       if (COMP_IP(&rt_table->rt_dst, dst))
126         {
127           return rt_table;
128         }
129     }
130   return NULL;
131   
132 }
133
134 /**
135  * Look up an entry in the HNA routing table.
136  *
137  * @param dst the address of the entry
138  *
139  * @return a pointer to a rt_entry struct 
140  * representing the route entry.
141  */
142
143 struct rt_entry *
144 olsr_lookup_hna_routing_table(union olsr_ip_addr *dst)
145 {
146   struct rt_entry *walker;
147   olsr_u32_t hash = olsr_hashing(dst);
148
149   for (walker = hna_routes[hash].next; walker != &hna_routes[hash];
150        walker = walker->next)
151     if (COMP_IP(&walker->rt_dst, dst))
152       return walker;
153
154   return NULL;
155 }
156
157 /**
158  *Delete all the entries in the routing table hash
159  *
160  *@param table the routing hash table
161  *
162  *@return nada
163  */
164 void
165 olsr_free_routing_table(struct rt_entry *table)
166 {
167   olsr_u8_t       index;
168   
169   for(index=0;index<HASHSIZE;index++)
170     {
171       struct rt_entry *destination = table[index].next;
172
173       while(destination != &table[index])
174         {
175           struct rt_entry *dst_to_delete = destination;
176           destination = destination->next;
177
178           DEQUEUE_ELEM(dst_to_delete);
179           free(dst_to_delete);
180         }
181     }
182
183 }
184
185
186 /**
187  *Insert an 1 or 2 neighbor-entry into the routing table.
188  *
189  *@param dst the destination
190  *
191  *@return the new rt_entry struct
192  */
193 struct rt_entry *
194 olsr_insert_routing_table(union olsr_ip_addr *dst, 
195                           union olsr_ip_addr *router, 
196                           struct interface *iface, 
197                           int metric,
198                           float etx)
199 {
200   struct rt_entry *new_route_entry, *rt_list;
201   olsr_u32_t       hash;
202
203   hash = olsr_hashing(dst);
204   rt_list = &routingtable[hash];
205
206   new_route_entry = olsr_malloc(sizeof(struct rt_entry), "Insert routing table");
207
208   COPY_IP(&new_route_entry->rt_dst, dst);
209   COPY_IP(&new_route_entry->rt_router, router);
210   new_route_entry->rt_if = iface;
211
212   new_route_entry->rt_metric = metric;
213   if (etx< 0.0)
214     /* non-LQ case */
215     new_route_entry->rt_etx = (float)metric;
216   else
217     /* LQ case */
218     new_route_entry->rt_etx = etx;
219   
220   if(COMP_IP(dst, router))
221     /* Not GW */
222     new_route_entry->rt_flags = (RTF_UP|RTF_HOST);
223   else
224     new_route_entry->rt_flags = (RTF_UP|RTF_HOST|RTF_GATEWAY);
225
226   if(olsr_cnf->ip_version == AF_INET)
227     /* IPv4 */
228     new_route_entry->rt_mask.v4 = NETMASK_HOST;
229   else
230     /* IPv6 */
231     new_route_entry->rt_mask.v6 = 128;
232
233   /* queue */
234   rt_list->next->prev = new_route_entry;
235   new_route_entry->next = rt_list->next;
236   rt_list->next = new_route_entry;
237   new_route_entry->prev = rt_list;
238   
239   return(new_route_entry);
240 }
241
242
243
244 /**
245  *Insert all the one hop neighbors in the routing table.
246  *
247  *@return
248  */
249 static int
250 olsr_fill_routing_table_with_neighbors(void)
251 {
252   int index;
253
254 #ifdef DEBUG
255   OLSR_PRINTF(7, "FILL ROUTING TABLE WITH NEIGHBORS\n");
256 #endif
257
258   for(index=0;index<HASHSIZE;index++)
259     {
260       struct neighbor_entry *neighbor;
261       for(neighbor = neighbortable[index].next;
262           neighbor != &neighbortable[index];
263           neighbor=neighbor->next)     
264         {
265           if(neighbor->status == SYM)
266             {
267               static struct mid_address addrs;
268               struct mid_address *addrs2;
269
270               /*
271                *Insert all the neighbors addresses
272                */
273
274               COPY_IP(&addrs.alias, &neighbor->neighbor_main_addr);
275               addrs.next_alias = mid_lookup_aliases(&neighbor->neighbor_main_addr);
276
277               for(addrs2 = &addrs;addrs2!=NULL;addrs2 = addrs2->next_alias)
278                 {
279                   struct link_entry *link = get_best_link_to_neighbor(&addrs2->alias);
280 #ifdef DEBUG
281                   OLSR_PRINTF(7, "(ROUTE)Adding neighbor %s\n", olsr_ip_to_string(&addrs.alias));
282 #endif
283                   if(link)
284                     {
285                       struct interface *iface = link->if_name ? if_ifwithname(link->if_name) :
286                                                 if_ifwithaddr(&link->local_iface_addr);
287                       if(iface)
288                         {
289                           olsr_insert_routing_table(&addrs2->alias, 
290                                                     &link->neighbor_iface_addr,
291                                                     iface,
292                                                     1,
293                                                     -1.0);
294                         }
295                     }
296                 }
297             }
298         }
299     }
300   return 1;
301 }
302
303
304 /**
305  * Check if a two hop neighbor is reachable trough
306  * a one hop neighbor with willingness != WILL_NEVER
307  *
308  * @return OLSR_TRUE if reachable OLSR_FALSE if not
309  */
310 static olsr_bool
311 two_hop_neighbor_reachable(struct neighbor_2_list_entry *neigh_2_list)
312 {
313   struct neighbor_list_entry *neighbors;
314
315   for(neighbors = neigh_2_list->neighbor_2->neighbor_2_nblist.next;
316       neighbors != &neigh_2_list->neighbor_2->neighbor_2_nblist;
317       neighbors = neighbors->next)
318     {
319       if((neighbors->neighbor->status != NOT_NEIGH) &&
320          (neighbors->neighbor->willingness != WILL_NEVER))
321         return OLSR_TRUE;
322     }
323
324   return OLSR_FALSE;  
325 }
326
327
328 /**
329  *Insert all the two hop neighbors that is not already added
330  *in the routing table.
331  *
332  *@return a pointer to a destination_n linked-list of the neighbors.
333  */
334
335 static struct destination_n *
336 olsr_fill_routing_table_with_two_hop_neighbors(void)
337 {
338   struct destination_n *list_destination_n=NULL;
339   int            index;
340
341   //printf("FILL ROUTING TABLE WITH TWO HOP NEIGHBORS\n");
342
343   for(index=0;index<HASHSIZE;index++)
344     {
345       struct neighbor_entry *neighbor;
346
347       for(neighbor = neighbortable[index].next;
348           neighbor != &neighbortable[index];
349           neighbor=neighbor->next)     
350         {
351           struct neighbor_2_list_entry *neigh_2_list; 
352
353           if(neighbor->status != SYM)
354             continue;
355           
356           /*
357            *Insert all the two hop neighbors
358            */
359           for(neigh_2_list = neighbor->neighbor_2_list.next;
360               neigh_2_list != &neighbor->neighbor_2_list;
361               neigh_2_list = neigh_2_list->next)
362             {
363               union olsr_ip_addr *n2_addr;
364               static struct mid_address addrs;
365               struct mid_address *addrsp;
366               
367               n2_addr = &neigh_2_list->neighbor_2->neighbor_2_addr;
368               
369               if(olsr_lookup_routing_table(n2_addr))
370                 {
371 #ifdef DEBUG
372                   OLSR_PRINTF(7, "2hop: %s already added\n", olsr_ip_to_string(n2_addr));
373 #endif
374                   continue;
375                 }           
376
377               if(!two_hop_neighbor_reachable(neigh_2_list))
378                 {
379                   OLSR_PRINTF(1, "Two hop neighbor %s not added - no one hop neighbors.\n",
380                               olsr_ip_to_string(n2_addr));
381                   continue;
382                 }
383
384               COPY_IP(&addrs.alias, n2_addr);
385               addrs.next_alias = mid_lookup_aliases(n2_addr);
386
387               for(addrsp = &addrs; addrsp; addrsp = addrsp->next_alias)
388                 {
389                   struct link_entry *link = get_best_link_to_neighbor(&neighbor->neighbor_main_addr);
390 #ifdef DEBUG
391                   OLSR_PRINTF(7, "(ROUTE)Adding neighbor %s\n", olsr_ip_to_string(&addrsp->alias));
392 #endif
393                   if(link)
394                     {
395                       struct interface *iface = link->if_name ? if_ifwithname(link->if_name) :
396                                                 if_ifwithaddr(&link->local_iface_addr);
397                       if(iface)
398                         {
399                           struct rt_entry *new_route_entry = 
400                             olsr_insert_routing_table(&addrsp->alias, 
401                                                       &link->neighbor_iface_addr,
402                                                       iface,
403                                                       2,
404                                                       -1.0);
405                           
406                           if(new_route_entry != NULL)
407                             {
408                               struct destination_n *tmp = olsr_malloc(sizeof(struct destination_n), 
409                                                                       "Fill rt table 2 hop tmp");
410                               tmp->destination = new_route_entry;
411                               tmp->next = list_destination_n;
412                               list_destination_n = tmp;
413                             }
414                         }
415                     }
416                 }
417             }
418         }      
419     }
420   return list_destination_n;
421 }
422
423 /**
424  *Recalculate the routing table
425  *
426  *@return nada
427  */
428 void 
429 olsr_calculate_routing_table(void)
430 {
431   struct destination_n *list_destination_n_1;
432
433   olsr_move_route_table(routingtable, old_routes);
434
435   /* Add neighbors */
436   olsr_fill_routing_table_with_neighbors();
437   /* Add two hop enighbors - now they are the "outer rim" */
438   
439   list_destination_n_1 = olsr_fill_routing_table_with_two_hop_neighbors();
440   while(list_destination_n_1)
441     {
442       /* List_destination_n_1 holds the "outer rim" */
443       struct destination_n *list_destination_n = list_destination_n_1;
444
445       list_destination_n_1=NULL;
446
447       /* Next "outer rim" */
448       while(list_destination_n!=NULL)
449         {
450           struct destination_n *destination_n_1=NULL;
451           struct tc_entry      *topo_entry;
452
453           if((topo_entry = olsr_lookup_tc_entry(&list_destination_n->destination->rt_dst)) != NULL)
454             {
455               struct topo_dst *topo_dest = topo_entry->destinations.next;
456
457               /* Loop trough this nodes MPR selectors */
458               while(topo_dest != &topo_entry->destinations)
459                 {
460                   static struct mid_address tmp_addrs;
461                   struct mid_address *tmp_addrsp;
462                   
463                   /* Do not add ourselves */
464                   if(if_ifwithaddr(&topo_dest->T_dest_addr))
465                     {
466                       topo_dest=topo_dest->next;
467                       continue;
468                     }
469                   
470                   /* Find mid nodes */            
471                   COPY_IP(&tmp_addrs.alias, &topo_dest->T_dest_addr);
472                   tmp_addrs.next_alias = mid_lookup_aliases(&topo_dest->T_dest_addr);
473                   tmp_addrsp = &tmp_addrs;
474                   
475                   while(tmp_addrsp!=NULL)
476                     {
477                       if(NULL==olsr_lookup_routing_table(&tmp_addrsp->alias))
478                         {
479                           /* PRINT OUT: Last Hop to Final Destination */
480                           /* The function ip_to_string has to be seperately */
481                           OLSR_PRINTF(3, "%s -> ", olsr_ip_to_string(&list_destination_n->destination->rt_dst));
482                           OLSR_PRINTF(3, "%s\n", olsr_ip_to_string(&tmp_addrsp->alias));
483                           
484                           destination_n_1 = olsr_malloc(sizeof(struct destination_n), 
485                                                         "Calculate routing table 2");
486                           
487                           /* Add this entry to the "outer rim" */
488                           destination_n_1->destination = 
489                             olsr_insert_routing_table(&tmp_addrsp->alias, 
490                                                       &list_destination_n->destination->rt_router, 
491                                                       list_destination_n->destination->rt_if,
492                                                       list_destination_n->destination->rt_metric+1,
493                                                       -1.0);
494                           if(destination_n_1->destination != NULL)
495                             {
496                               destination_n_1->next=list_destination_n_1;
497                               list_destination_n_1=destination_n_1;
498                             }
499                         }
500                       tmp_addrsp = tmp_addrsp->next_alias;
501                     }
502                   
503                   /* Next MPR selector */
504                   topo_dest=topo_dest->next;
505                   
506                 } /* End loop trought MPR selectors */
507               
508             } /* End check if already added */
509           
510           /* Delete this entry - do next */ 
511           destination_n_1 = list_destination_n;
512           list_destination_n = list_destination_n->next;
513           free(destination_n_1);
514           
515         }
516
517     }
518
519
520   if(olsr_cnf->debug_level > 5)
521     {
522       printf("************** TABLES ****************\n");
523       printf("Routing table:\n");
524       olsr_print_routing_table(routingtable);
525       printf("Old table:\n");
526       olsr_print_routing_table(old_routes);
527       printf("**************************************\n");
528     }
529
530   
531   /* Update routes */
532   olsr_update_kernel_routes();
533
534   olsr_free_routing_table(old_routes);
535 }
536
537
538
539
540 /**
541  *Check for an entry with a higher quality (lower etx) than
542  *a given value in a routing table
543  *
544  *@param routes the routingtable to look in
545  *@param net the network entry to look for
546  *@param etx the metric to check for
547  *
548  *@return the located entry if found. NULL if not
549  */
550 static struct rt_entry *
551 olsr_check_for_higher_quality(struct rt_entry *routes, struct hna_net *net, float etx)
552 {
553   int index;
554
555   for(index=0;index<HASHSIZE;index++)
556     {
557       struct rt_entry *tmp_routes;
558       /* All entries */
559       for(tmp_routes = routes[index].next;
560           tmp_routes != &routes[index];
561           tmp_routes = tmp_routes->next)
562         {
563           if(COMP_IP(&tmp_routes->rt_dst, &net->A_network_addr) &&
564              (memcmp(&tmp_routes->rt_mask, &net->A_netmask, netmask_size) == 0))
565             {
566               /* Found an entry */
567               if(tmp_routes->rt_etx < etx)
568                 return tmp_routes;
569               else
570                 return NULL;
571             }
572         }
573     }
574
575   return NULL;
576 }
577
578
579
580 /**
581  *Check for an entry with a lower or equal quality (higher or equal etx) than
582  *a given value in a routing table
583  *
584  *@param routes the routingtable to look in
585  *@param net the network entry to look for
586  *@param etx the metric to check for
587  *
588  *@return the located entry if found. NULL if not
589  */
590 struct rt_entry *
591 olsr_check_for_lower_quality(struct rt_entry *routes, struct hna_net *net, float etx)
592 {
593   int index;
594
595   for(index=0;index<HASHSIZE;index++)
596     {
597       struct rt_entry *tmp_routes;
598       /* All entries */
599       for(tmp_routes = routes[index].next;
600           tmp_routes != &routes[index];
601           tmp_routes = tmp_routes->next)
602         {
603           if(COMP_IP(&tmp_routes->rt_dst, &net->A_network_addr) &&
604              (memcmp(&tmp_routes->rt_mask, &net->A_netmask, netmask_size) == 0))
605             {
606               /* Found an entry */
607               if(tmp_routes->rt_etx >= etx)
608                 return tmp_routes;
609               else
610                 return NULL;
611             }
612         }
613     }
614
615
616
617   return NULL;
618 }
619
620
621
622
623 /**
624  *Calculate the HNA routes
625  *
626  */
627 void
628 olsr_calculate_hna_routes(void)
629 {
630   int index;
631
632 #ifdef DEBUG
633   OLSR_PRINTF(3, "Calculating HNA routes\n");
634 #endif
635
636   olsr_move_route_table(hna_routes, old_hna);
637
638   for(index=0;index<HASHSIZE;index++)
639     {
640       struct hna_entry *tmp_hna;
641       /* All entries */
642       for(tmp_hna = hna_set[index].next;
643           tmp_hna != &hna_set[index];
644           tmp_hna = tmp_hna->next)
645         {
646           struct hna_net *tmp_net;
647           /* All networks */
648           for(tmp_net = tmp_hna->networks.next;
649               tmp_net != &tmp_hna->networks;
650               tmp_net = tmp_net->next)
651             {
652               struct rt_entry *tmp_rt, *new_rt;
653               //printf("HNA: checking %s -> ", olsr_ip_to_string(&tmp_hna->A_gateway_addr));
654               //printf("%s", olsr_ip_to_string(&tmp_net->A_network_addr));
655
656               /* If no route to gateway - skip */
657               if((tmp_rt = olsr_lookup_routing_table(&tmp_hna->A_gateway_addr)) == NULL)
658                   continue;
659
660               /* If there exists a better or equal entry - skip */
661               if(olsr_check_for_higher_quality(hna_routes, tmp_net, tmp_rt->rt_etx) != NULL)
662                   continue;
663
664               /* If we find an entry with lower quality we just edit it */
665               if((new_rt = olsr_check_for_lower_quality(hna_routes, tmp_net, tmp_rt->rt_etx)) != NULL)
666                 {
667                   /* Fill struct */
668                   /* Net */
669                   COPY_IP(&new_rt->rt_dst, &tmp_net->A_network_addr);
670                   new_rt->rt_mask = tmp_net->A_netmask;
671                   /* Gateway */
672                   COPY_IP(&new_rt->rt_router, &tmp_rt->rt_router);
673                   /* Metric */
674                   new_rt->rt_etx = tmp_rt->rt_etx;
675                   new_rt->rt_metric = tmp_rt->rt_metric;
676                   /* Flags */
677                   new_rt->rt_flags = RTF_UP | RTF_GATEWAY;
678                   /* Interface */
679                   new_rt->rt_if = tmp_rt->rt_if;
680                 }
681               /* If not - create a new one */
682               else
683                 {
684                   olsr_u32_t  hna_hash;
685
686                   new_rt = olsr_malloc(sizeof(struct rt_entry), "New rt entry");
687
688                   /* Fill struct */
689                   /* Net */
690                   COPY_IP(&new_rt->rt_dst, &tmp_net->A_network_addr);
691                   new_rt->rt_mask = tmp_net->A_netmask;
692                   /* Gateway */
693                   COPY_IP(&new_rt->rt_router, &tmp_rt->rt_router);
694                   /* Metric */
695                   new_rt->rt_etx = tmp_rt->rt_etx;
696                   new_rt->rt_metric = tmp_rt->rt_metric;
697                   /* Flags */
698                   new_rt->rt_flags = RTF_UP | RTF_GATEWAY;
699
700                   /* Interface */
701                   new_rt->rt_if = tmp_rt->rt_if;
702                           
703                   /* Queue HASH will almost always be 0 */
704                   hna_hash = olsr_hashing(&tmp_net->A_network_addr);
705                   hna_routes[hna_hash].next->prev = new_rt;
706                   new_rt->next = hna_routes[hna_hash].next;
707                   hna_routes[hna_hash].next = new_rt;
708                   new_rt->prev = &hna_routes[hna_hash];
709                 }
710             }
711         }
712     }
713
714   /* Update kernel */
715   olsr_update_kernel_hna_routes();
716
717   if(olsr_cnf->debug_level > 2)
718     {
719       OLSR_PRINTF(3, "HNA table:\n");
720       olsr_print_routing_table(hna_routes);
721     }
722
723   olsr_free_routing_table(old_hna);
724 }
725
726
727
728
729
730 /**
731  *Print the routingtable to STDOUT
732  *
733  */
734
735 void
736 olsr_print_routing_table(struct rt_entry *table)
737 {
738   int index;
739
740   printf("ROUTING TABLE\n");
741   printf("DESTINATION\tNEXT HOP\tHOPCNT\tINTERFACE\n");
742   for(index = 0; index < HASHSIZE; index++)
743     {
744       struct rt_entry *destination;
745       for(destination = table[index].next;
746           destination != &table[index];
747           destination = destination->next)
748         {
749           printf("%s\t", olsr_ip_to_string(&destination->rt_dst));
750           printf("%s\t%d\t%s\n", 
751                  olsr_ip_to_string(&destination->rt_router),
752                  destination->rt_metric,
753                  destination->rt_if->int_name);
754         }
755     }
756 }