For HNA routes search the correct routing table, i.e. the HNA routing
[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.23 2005/11/16 23:55:54 tlopatic 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
52
53 struct rt_entry routingtable[HASHSIZE];
54 struct rt_entry hna_routes[HASHSIZE];
55
56
57 /* Begin:
58  * Prototypes for internal functions 
59  */
60
61 static int
62 olsr_fill_routing_table_with_neighbors(void);
63
64 static struct destination_n *
65 olsr_fill_routing_table_with_two_hop_neighbors(void);
66
67 static struct rt_entry *
68 olsr_check_for_higher_hopcount(struct rt_entry *, struct hna_net *, olsr_u16_t);
69
70 struct rt_entry *
71 olsr_check_for_lower_hopcount(struct rt_entry *, struct hna_net *, olsr_u16_t);
72
73 static olsr_bool
74 two_hop_neighbor_reachable(struct neighbor_2_list_entry *);
75
76 /* End:
77  * Prototypes for internal functions 
78  */
79
80
81 /**
82  *Initialize the routing table
83  */
84 int
85 olsr_init_routing_table()
86 {
87   int index;
88
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;
120
121   hash = olsr_hashing(dst);
122
123   for(rt_table = routingtable[hash].next;
124       rt_table != &routingtable[hash];
125       rt_table = rt_table->next)
126     {
127       if (COMP_IP(&rt_table->rt_dst, dst))
128         {
129           return(rt_table);
130         }
131     }
132   return(NULL);
133   
134 }
135
136 /**
137  * Look up an entry in the HNA routing table.
138  *
139  * @param dst the address of the entry
140  *
141  * @return a pointer to a rt_entry struct 
142  * representing the route entry.
143  */
144
145 struct rt_entry *
146 olsr_lookup_hna_routing_table(union olsr_ip_addr *dst)
147 {
148   struct rt_entry *walker;
149   olsr_u32_t hash = olsr_hashing(dst);
150
151   for (walker = hna_routes[hash].next; walker != &hna_routes[hash];
152        walker = walker->next)
153     if (COMP_IP(&walker->rt_dst, dst))
154       return walker;
155
156   return NULL;
157 }
158
159 /**
160  *Delete all the entries in the routing table hash
161  *
162  *@param table the routing hash table
163  *
164  *@return nada
165  */
166 void
167 olsr_free_routing_table(struct rt_entry *table)
168 {
169   olsr_u8_t       index;
170   
171   for(index=0;index<HASHSIZE;index++)
172     {
173       struct rt_entry *destination = table[index].next;
174
175       while(destination != &table[index])
176         {
177           struct rt_entry *dst_to_delete = destination;
178           destination = destination->next;
179
180           DEQUEUE_ELEM(dst_to_delete);
181           free(dst_to_delete);
182         }
183     }
184
185 }
186
187
188 /**
189  *Insert an 1 or 2 neighbor-entry into the routing table.
190  *
191  *@param dst the destination
192  *
193  *@return the new rt_entry struct
194  */
195 struct rt_entry *
196 olsr_insert_routing_table(union olsr_ip_addr *dst, 
197                           union olsr_ip_addr *router, 
198                           struct interface *iface, 
199                           int metric,
200                           float etx)
201 {
202   struct rt_entry *new_route_entry, *rt_list;
203   olsr_u32_t       hash;
204
205   hash = olsr_hashing(dst);
206   rt_list = &routingtable[hash];
207
208   new_route_entry = olsr_malloc(sizeof(struct rt_entry), "Insert routing table");
209
210   COPY_IP(&new_route_entry->rt_dst, dst);
211   COPY_IP(&new_route_entry->rt_router, router);
212   new_route_entry->rt_if = iface;
213
214   new_route_entry->rt_metric = metric;
215   new_route_entry->rt_etx = etx;
216   
217   if(COMP_IP(dst, router))
218     /* Not GW */
219     new_route_entry->rt_flags = (RTF_UP|RTF_HOST);
220   else
221     new_route_entry->rt_flags = (RTF_UP|RTF_HOST|RTF_GATEWAY);
222
223   if(olsr_cnf->ip_version == AF_INET)
224     /* IPv4 */
225     new_route_entry->rt_mask.v4 = NETMASK_HOST;
226   else
227     /* IPv6 */
228     new_route_entry->rt_mask.v6 = 128;
229
230   /* queue */
231   rt_list->next->prev = new_route_entry;
232   new_route_entry->next = rt_list->next;
233   rt_list->next = new_route_entry;
234   new_route_entry->prev = rt_list;
235   
236   return(new_route_entry);
237 }
238
239
240
241 /**
242  *Insert all the one hop neighbors in the routing table.
243  *
244  *@return
245  */
246 static int
247 olsr_fill_routing_table_with_neighbors()
248 {
249   olsr_u8_t              index;
250
251 #ifdef DEBUG
252   OLSR_PRINTF(7, "FILL ROUTING TABLE WITH NEIGHBORS\n")
253 #endif
254
255   for(index=0;index<HASHSIZE;index++)
256     {
257       struct neighbor_entry *neighbor;
258
259       for(neighbor = neighbortable[index].next;
260           neighbor != &neighbortable[index];
261           neighbor=neighbor->next)     
262         {
263
264           if(neighbor->status == SYM)
265             {
266               static struct mid_address addrs;
267               struct mid_address *addrs2;
268
269               /*
270                *Insert all the neighbors addresses
271                */
272
273               COPY_IP(&addrs.alias, &neighbor->neighbor_main_addr);
274               addrs.next_alias = mid_lookup_aliases(&neighbor->neighbor_main_addr);
275               addrs2 = &addrs;
276
277               while(addrs2!=NULL)
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 = if_ifwithaddr(&link->local_iface_addr);
286                       if(iface)
287                         {
288                           olsr_insert_routing_table(&addrs2->alias, 
289                                                     &link->neighbor_iface_addr,
290                                                     iface,
291                                                     1,
292                                                     0);
293                         }
294                     }
295               
296                   addrs2 = addrs2->next_alias;
297                 }
298             }
299         }
300     }
301
302
303   return 1;
304 }
305
306
307 /**
308  * Check if a two hop neighbor is reachable trough
309  * a one hop neighbor with willingness != WILL_NEVER
310  *
311  * @return OLSR_TRUE if reachable OLSR_FALSE if not
312  */
313 static olsr_bool
314 two_hop_neighbor_reachable(struct neighbor_2_list_entry *neigh_2_list)
315 {
316   struct neighbor_list_entry *neighbors;
317
318   for(neighbors = neigh_2_list->neighbor_2->neighbor_2_nblist.next;
319       neighbors != &neigh_2_list->neighbor_2->neighbor_2_nblist;
320       neighbors = neighbors->next)
321     {
322       if((neighbors->neighbor->status != NOT_NEIGH) &&
323          (neighbors->neighbor->willingness != WILL_NEVER))
324         return OLSR_TRUE;
325     }
326
327   return OLSR_FALSE;  
328 }
329
330
331 /**
332  *Insert all the two hop neighbors that is not already added
333  *in the routing table.
334  *
335  *@return a pointer to a destination_n linked-list of the neighbors.
336  */
337
338 static struct destination_n *
339 olsr_fill_routing_table_with_two_hop_neighbors()
340 {
341   struct destination_n *list_destination_n=NULL;
342   olsr_u8_t            index;
343
344   //printf("FILL ROUTING TABLE WITH TWO HOP NEIGHBORS\n");
345
346   for(index=0;index<HASHSIZE;index++)
347     {
348       struct neighbor_entry *neighbor;
349
350       for(neighbor = neighbortable[index].next;
351           neighbor != &neighbortable[index];
352           neighbor=neighbor->next)     
353         {
354           struct neighbor_2_list_entry *neigh_2_list; 
355
356           if(neighbor->status != SYM)
357             continue;
358           
359           /*
360            *Insert all the two hop neighbors
361            */
362           for(neigh_2_list = neighbor->neighbor_2_list.next;
363               neigh_2_list != &neighbor->neighbor_2_list;
364               neigh_2_list = neigh_2_list->next)
365             {
366               union olsr_ip_addr *n2_addr;
367               static struct mid_address addrs;
368               struct mid_address *addrsp;
369               
370               n2_addr = &neigh_2_list->neighbor_2->neighbor_2_addr;
371               
372               if(olsr_lookup_routing_table(n2_addr))
373                 {
374 #ifdef DEBUG
375                   OLSR_PRINTF(7, "2hop: %s already added\n", olsr_ip_to_string(n2_addr))
376 #endif
377                   continue;
378                 }           
379
380               if(!two_hop_neighbor_reachable(neigh_2_list))
381                 {
382                   OLSR_PRINTF(1, "Two hop neighbor %s not added - no one hop neighbors.\n",
383                               olsr_ip_to_string(n2_addr))
384                   continue;
385                 }
386
387               COPY_IP(&addrs.alias, n2_addr);
388               addrs.next_alias = mid_lookup_aliases(n2_addr);
389               addrsp = &addrs;
390
391               while(addrsp!=NULL)
392                 {
393                   struct link_entry *link = get_best_link_to_neighbor(&neighbor->neighbor_main_addr);
394 #ifdef DEBUG
395                   OLSR_PRINTF(7, "(ROUTE)Adding neighbor %s\n", olsr_ip_to_string(&addrsp->alias))
396 #endif
397                   if(link)
398                     {
399                       struct interface *iface = if_ifwithaddr(&link->local_iface_addr);
400                       if(iface)
401                         {
402                           struct rt_entry *new_route_entry = 
403                             olsr_insert_routing_table(&addrsp->alias, 
404                                                       &link->neighbor_iface_addr,
405                                                       iface,
406                                                       2,
407                                                       0);
408                           
409                           if(new_route_entry != NULL)
410                             {
411                               struct destination_n *list_destination_tmp;
412                               list_destination_tmp = olsr_malloc(sizeof(struct destination_n), 
413                                                                  "Fill rt table 2 hop tmp");
414                               
415                               list_destination_tmp->destination = new_route_entry;
416                               list_destination_tmp->next = list_destination_n;
417                               list_destination_n = list_destination_tmp;
418                             }
419                         }
420                     }
421                   addrsp = addrsp->next_alias; 
422                 }
423             }
424         }
425       
426     }
427   
428   return list_destination_n;
429 }
430
431
432
433
434 /**
435  *Recalculate the routing table
436  *
437  *@return nada
438  */
439 void 
440 olsr_calculate_routing_table()
441 {
442   struct destination_n *list_destination_n_1 = NULL;
443
444   olsr_move_route_table(routingtable, old_routes);
445
446   /* Add neighbors */
447   olsr_fill_routing_table_with_neighbors();
448   /* Add two hop enighbors - now they are the "outer rim" */
449   list_destination_n_1 = olsr_fill_routing_table_with_two_hop_neighbors();
450
451   while(list_destination_n_1!=NULL)
452     {
453       /* List_destination_n_1 holds the "outer rim" */
454       struct destination_n *list_destination_n = list_destination_n_1;
455
456       list_destination_n_1=NULL;
457
458       /* Next "outer rim" */
459       while(list_destination_n!=NULL)
460         {
461           struct destination_n *destination_n_1=NULL;
462           struct tc_entry      *topo_entry;
463
464           if((topo_entry = olsr_lookup_tc_entry(&list_destination_n->destination->rt_dst)) != NULL)
465             {
466               struct topo_dst *topo_dest = topo_entry->destinations.next;
467
468               /* Loop trough this nodes MPR selectors */
469               while(topo_dest != &topo_entry->destinations)
470                 {
471                   static struct mid_address tmp_addrs;
472                   struct mid_address *tmp_addrsp;
473                   
474                   /* Do not add ourselves */
475                   if(if_ifwithaddr(&topo_dest->T_dest_addr))
476                     {
477                       topo_dest=topo_dest->next;
478                       continue;
479                     }
480                   
481                   /* Find mid nodes */            
482                   COPY_IP(&tmp_addrs.alias, &topo_dest->T_dest_addr);
483                   tmp_addrs.next_alias = mid_lookup_aliases(&topo_dest->T_dest_addr);
484                   tmp_addrsp = &tmp_addrs;
485                   
486                   while(tmp_addrsp!=NULL)
487                     {
488                       if(NULL==olsr_lookup_routing_table(&tmp_addrsp->alias))
489                         {
490                           /* PRINT OUT: Last Hop to Final Destination */
491                           /* The function ip_to_string has to be seperately */
492                           OLSR_PRINTF(3, "%s -> ", olsr_ip_to_string(&list_destination_n->destination->rt_dst))
493                           OLSR_PRINTF(3, "%s\n", olsr_ip_to_string(&tmp_addrsp->alias))
494                           
495                           destination_n_1 = olsr_malloc(sizeof(struct destination_n), 
496                                                         "Calculate routing table 2");
497                           
498                           /* Add this entry to the "outer rim" */
499                           destination_n_1->destination = 
500                             olsr_insert_routing_table(&tmp_addrsp->alias, 
501                                                       &list_destination_n->destination->rt_router, 
502                                                       list_destination_n->destination->rt_if,
503                                                       list_destination_n->destination->rt_metric+1,
504                                                       0);
505                           if(destination_n_1->destination != NULL)
506                             {
507                               destination_n_1->next=list_destination_n_1;
508                               list_destination_n_1=destination_n_1;
509                             }
510                         }
511                       tmp_addrsp = tmp_addrsp->next_alias;
512                     }
513                   
514                   /* Next MPR selector */
515                   topo_dest=topo_dest->next;
516                   
517                 } /* End loop trought MPR selectors */
518               
519             } /* End check if already added */
520           
521           /* Delete this entry - do next */ 
522           destination_n_1 = list_destination_n;
523           list_destination_n = list_destination_n->next;
524           free(destination_n_1);
525           
526         }
527
528     }
529
530
531   if(olsr_cnf->debug_level > 5)
532     {
533       printf("************** TABLES ****************\n");
534       printf("Routing table:\n");
535       olsr_print_routing_table(routingtable);
536       printf("Old table:\n");
537       olsr_print_routing_table(old_routes);
538       printf("**************************************\n");
539     }
540
541   
542   /* Update routes */
543   olsr_update_kernel_routes();
544
545   olsr_free_routing_table(old_routes);
546 }
547
548
549
550
551 /**
552  *Check for a entry with a higher hopcount than
553  *a given value in a routing table
554  *
555  *@param routes the routingtable to look in
556  *@param net the network entry to look for
557  *@param metric the metric to check for
558  *
559  *@return the localted entry if found. NULL if not
560  */
561 static struct rt_entry *
562 olsr_check_for_higher_hopcount(struct rt_entry *routes, struct hna_net *net, olsr_u16_t metric)
563 {
564   int index;
565
566   for(index=0;index<HASHSIZE;index++)
567     {
568       struct rt_entry *tmp_routes;
569       /* All entries */
570       for(tmp_routes = routes[index].next;
571           tmp_routes != &routes[index];
572           tmp_routes = tmp_routes->next)
573         {
574           if(COMP_IP(&tmp_routes->rt_dst, &net->A_network_addr) &&
575              (memcmp(&tmp_routes->rt_mask, &net->A_netmask, netmask_size) == 0))
576             {
577               /* Found a entry */
578               if(tmp_routes->rt_metric > metric)
579                 return tmp_routes;
580               else
581                 return NULL;
582             }
583         }
584     }
585
586   return NULL;
587 }
588
589
590
591 /**
592  *Check for a entry with a lower or equal hopcount than
593  *a given value in a routing table
594  *
595  *@param routes the routingtable to look in
596  *@param net the network entry to look for
597  *@param metric the metric to check for
598  *
599  *@return the localted entry if found. NULL if not
600  */
601 struct rt_entry *
602 olsr_check_for_lower_hopcount(struct rt_entry *routes, struct hna_net *net, olsr_u16_t metric)
603 {
604   int index;
605
606   for(index=0;index<HASHSIZE;index++)
607     {
608       struct rt_entry *tmp_routes;
609       /* All entries */
610       for(tmp_routes = routes[index].next;
611           tmp_routes != &routes[index];
612           tmp_routes = tmp_routes->next)
613         {
614           if(COMP_IP(&tmp_routes->rt_dst, &net->A_network_addr) &&
615              (memcmp(&tmp_routes->rt_mask, &net->A_netmask, netmask_size) == 0))
616             {
617               /* Found a entry */
618               if(tmp_routes->rt_metric <= metric)
619                 return tmp_routes;
620               else
621                 return NULL;
622             }
623         }
624     }
625
626
627
628   return NULL;
629 }
630
631
632
633
634 /**
635  *Calculate the HNA routes
636  *
637  */
638 void
639 olsr_calculate_hna_routes()
640 {
641   olsr_u32_t index;
642
643 #ifdef DEBUG
644   OLSR_PRINTF(3, "Calculating HNA routes\n")
645 #endif
646
647   olsr_move_route_table(hna_routes, old_hna);
648
649   
650   for(index=0;index<HASHSIZE;index++)
651     {
652       struct hna_entry *tmp_hna;
653       /* All entries */
654       for(tmp_hna = hna_set[index].next;
655           tmp_hna != &hna_set[index];
656           tmp_hna = tmp_hna->next)
657         {
658           struct hna_net *tmp_net;
659           /* All networks */
660           for(tmp_net = tmp_hna->networks.next;
661               tmp_net != &tmp_hna->networks;
662               tmp_net = tmp_net->next)
663             {
664               struct rt_entry *tmp_rt, *new_rt;
665               //printf("HNA: checking %s -> ", olsr_ip_to_string(&tmp_hna->A_gateway_addr));
666               //printf("%s", olsr_ip_to_string(&tmp_net->A_network_addr));
667
668               /* If no route to gateway - skip */
669               if((tmp_rt = olsr_lookup_routing_table(&tmp_hna->A_gateway_addr)) == NULL)
670                 {
671                   continue;
672                 }
673
674               /* If there exists a better or equal entry - skip */
675               if(olsr_check_for_lower_hopcount(hna_routes, tmp_net, tmp_rt->rt_metric) != NULL)
676                 {
677                   continue;
678                 }
679
680               /* If we find an entry with higher hopcount we just edit it */
681               if((new_rt = olsr_check_for_higher_hopcount(hna_routes, tmp_net, tmp_rt->rt_metric)) != NULL)
682                 {
683                   /* Fill struct */
684                   /* Net */
685                   COPY_IP(&new_rt->rt_dst, &tmp_net->A_network_addr);
686                   new_rt->rt_mask = tmp_net->A_netmask;
687                   /* Gateway */
688                   COPY_IP(&new_rt->rt_router, &tmp_rt->rt_router);
689                   /* Metric */
690                   new_rt->rt_metric = tmp_rt->rt_metric;
691                   /* Flags */
692                   new_rt->rt_flags = RTF_UP | RTF_GATEWAY;
693                   /* Interface */
694                   new_rt->rt_if = tmp_rt->rt_if;
695                 }
696               /* If not - create a new one */
697               else
698                 {
699                   olsr_u32_t  hna_hash;
700
701                   new_rt = olsr_malloc(sizeof(struct rt_entry), "New rt entry");
702
703                   /* Fill struct */
704                   /* Net */
705                   COPY_IP(&new_rt->rt_dst, &tmp_net->A_network_addr);
706                   new_rt->rt_mask = tmp_net->A_netmask;
707                   /* Gateway */
708                   COPY_IP(&new_rt->rt_router, &tmp_rt->rt_router);
709                   /* Metric */
710                   new_rt->rt_metric = tmp_rt->rt_metric;
711                   /* Flags */
712                   new_rt->rt_flags = RTF_UP | RTF_GATEWAY;
713
714                   /* Interface */
715                   new_rt->rt_if = tmp_rt->rt_if;
716                           
717                   /* Queue HASH will almost always be 0 */
718                   hna_hash = olsr_hashing(&tmp_net->A_network_addr);
719                   hna_routes[hna_hash].next->prev = new_rt;
720                   new_rt->next = hna_routes[hna_hash].next;
721                   hna_routes[hna_hash].next = new_rt;
722                   new_rt->prev = &hna_routes[hna_hash];
723                 }
724             }
725         }
726     }
727
728   /* Update kernel */
729   olsr_update_kernel_hna_routes();
730
731   if(olsr_cnf->debug_level > 2)
732     {
733       OLSR_PRINTF(3, "HNA table:\n")
734       olsr_print_routing_table(hna_routes);
735     }
736
737   olsr_free_routing_table(old_hna);
738
739 }
740
741
742
743
744
745 /**
746  *Print the routingtable to STDOUT
747  *
748  */
749
750 void
751 olsr_print_routing_table(struct rt_entry *table)
752 {
753
754   olsr_u8_t index;
755
756   printf("ROUTING TABLE\n");
757   printf("DESTINATION\tNEXT HOP\tHOPCNT\tINTERFACE\n");
758   for(index=0;index<HASHSIZE;index++)
759     {
760       struct rt_entry *destination;
761       for(destination = table[index].next;
762           destination != &table[index];
763           destination = destination->next)
764         {
765           printf("%s\t", olsr_ip_to_string(&destination->rt_dst));
766           printf("%s\t%d\t%s\n", 
767                  olsr_ip_to_string(&destination->rt_router),
768                  destination->rt_metric,
769                  destination->rt_if->int_name);
770         }
771     }
772 }
773
774
775
776
777