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