Patch by Bruno applied. The link metric is now displayed
[olsrd.git] / lib / dot_draw / src / olsrd_dot_draw.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon(olsrd)
3  * Copyright (c) 2004, Andreas T√łnnesen(andreto@olsr.org)
4  *                     includes code by Bruno Randolf
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without 
8  * modification, are permitted provided that the following conditions 
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright 
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright 
14  *   notice, this list of conditions and the following disclaimer in 
15  *   the documentation and/or other materials provided with the 
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its 
18  *   contributors may be used to endorse or promote products derived 
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  * $Id: olsrd_dot_draw.c,v 1.9 2005/01/01 17:58:34 kattemat Exp $
41  */
42
43 /*
44  * Dynamic linked library for the olsr.org olsr daemon
45  */
46
47 #include "olsrd_dot_draw.h"
48 #include <stdio.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 #include <errno.h>
53 #ifdef WIN32
54 #define close(x) closesocket(x)
55 #endif
56
57 int ipc_socket;
58 int ipc_open;
59 int ipc_connection;
60 int ipc_socket_up;
61
62 /**
63  *Do initialization here
64  *
65  *This function is called by the my_init
66  *function in uolsrd_plugin.c
67  */
68 int
69 olsr_plugin_init()
70 {
71
72   /* Initial IPC value */
73   ipc_open = 0;
74   ipc_socket_up = 0;
75
76   /* Register the "ProcessChanges" function */
77   register_pcf(&pcf_event);
78
79   return 1;
80 }
81
82 int
83 plugin_ipc_init()
84 {
85   struct sockaddr_in sin;
86   olsr_u32_t yes = 1;
87
88   /* Init ipc socket */
89   if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
90     {
91       olsr_printf(1, "(DOT DRAW)IPC socket %s\n", strerror(errno));
92       return 0;
93     }
94   else
95     {
96       if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) 
97       {
98         perror("SO_REUSEADDR failed");
99         return 0;
100       }
101
102       /* Bind the socket */
103       
104       /* complete the socket structure */
105       memset(&sin, 0, sizeof(sin));
106       sin.sin_family = AF_INET;
107       sin.sin_addr.s_addr = INADDR_ANY;
108       sin.sin_port = htons(ipc_port);
109       
110       /* bind the socket to the port number */
111       if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) 
112         {
113           olsr_printf(1, "(DOT DRAW)IPC bind %s\n", strerror(errno));
114           return 0;
115         }
116       
117       /* show that we are willing to listen */
118       if (listen(ipc_socket, 1) == -1) 
119         {
120           olsr_printf(1, "(DOT DRAW)IPC listen %s\n", strerror(errno));
121           return 0;
122         }
123
124
125       /* Register with olsrd */
126       add_olsr_socket(ipc_socket, &ipc_action);
127       ipc_socket_up = 1;
128     }
129
130   return 1;
131 }
132
133 void
134 ipc_action(int fd)
135 {
136   struct sockaddr_in pin;
137   socklen_t addrlen;
138   char *addr;  
139
140   addrlen = sizeof(struct sockaddr_in);
141
142   if ((ipc_connection = accept(ipc_socket, (struct sockaddr *)  &pin, &addrlen)) == -1)
143     {
144       olsr_printf(1, "(DOT DRAW)IPC accept: %s\n", strerror(errno));
145       exit(1);
146     }
147   else
148     {
149       addr = inet_ntoa(pin.sin_addr);
150       if(ntohl(pin.sin_addr.s_addr) != ntohl(ipc_accept_ip.s_addr))
151         {
152           olsr_printf(1, "Front end-connection from foregin host(%s) not allowed!\n", addr);
153           close(ipc_connection);
154           return;
155         }
156       else
157         {
158           ipc_open = 1;
159           olsr_printf(1, "(DOT DRAW)IPC: Connection from %s\n",addr);
160           pcf_event(1, 1, 1);
161         }
162     }
163
164 }
165
166 /*
167  * destructor - called at unload
168  */
169 void
170 olsr_plugin_exit()
171 {
172   if(ipc_open)
173     close(ipc_socket);
174 }
175
176
177
178 /* Mulitpurpose funtion */
179 int
180 plugin_io(int cmd, void *data, size_t size)
181 {
182
183   switch(cmd)
184     {
185     default:
186       return 0;
187     }
188   
189   return 1;
190 }
191
192
193
194
195 /**
196  *Scheduled event
197  */
198 int
199 pcf_event(int changes_neighborhood,
200           int changes_topology,
201           int changes_hna)
202 {
203   int res;
204   olsr_u8_t index;
205   struct neighbor_entry *neighbor_table_tmp;
206   struct neighbor_2_list_entry *list_2;
207   struct tc_entry *entry;
208   struct topo_dst *dst_entry;
209   struct hna_entry *tmp_hna;
210   struct hna_net *tmp_net;
211
212   res = 0;
213
214   if(changes_neighborhood || changes_topology || changes_hna)
215     {
216       /* Print tables to IPC socket */
217
218       ipc_send("digraph topology\n{\n", strlen("digraph topology\n{\n"));
219
220       /* Neighbors */
221       for(index=0;index<HASHSIZE;index++)
222         {
223           
224           for(neighbor_table_tmp = neighbortable[index].next;
225               neighbor_table_tmp != &neighbortable[index];
226               neighbor_table_tmp = neighbor_table_tmp->next)
227             {
228               ipc_print_neigh_link( neighbor_table_tmp );
229             }
230         }
231
232       /* Topology */  
233       for(index=0;index<HASHSIZE;index++)
234         {
235           /* For all TC entries */
236           entry = tc_table[index].next;
237           while(entry != &tc_table[index])
238             {
239               /* For all destination entries of that TC entry */
240               dst_entry = entry->destinations.next;
241               while(dst_entry != &entry->destinations)
242                 {
243                   ipc_print_tc_link(entry, dst_entry);
244                   dst_entry = dst_entry->next;
245                 }
246               entry = entry->next;
247             }
248         }
249
250       /* HNA entries */
251       for(index=0;index<HASHSIZE;index++)
252         {
253           tmp_hna = hna_set[index].next;
254           /* Check all entrys */
255           while(tmp_hna != &hna_set[index])
256             {
257               /* Check all networks */
258               tmp_net = tmp_hna->networks.next;
259               
260               while(tmp_net != &tmp_hna->networks)
261                 {
262                   ipc_print_net(&tmp_hna->A_gateway_addr, 
263                                 &tmp_net->A_network_addr, 
264                                 &tmp_net->A_netmask);
265                   tmp_net = tmp_net->next;
266                 }
267               
268               tmp_hna = tmp_hna->next;
269             }
270         }
271
272
273       ipc_send("}\n\n", strlen("}\n\n"));
274
275       res = 1;
276     }
277
278
279   if(!ipc_socket_up)
280     plugin_ipc_init();
281
282   return res;
283 }
284
285 #define MIN_LINK_QUALITY 0.01
286 double calc_etx( double loss, double neigh_loss ) {
287         if (loss < MIN_LINK_QUALITY || neigh_loss < MIN_LINK_QUALITY)
288                 return 0.0;
289         else
290                 return 1.0 / (loss * neigh_loss);
291 }
292
293 static void inline
294 ipc_print_neigh_link(struct neighbor_entry *neighbor)
295 {
296   char buf[256];
297   int len;
298   char* adr;
299   double etx=0.0;
300   char* style = "solid";
301   struct link_entry* link;
302   adr = olsr_ip_to_string(main_addr);
303   len = sprintf( buf, "\"%s\" -> ", adr );
304   ipc_send(buf, len);
305   
306   adr = olsr_ip_to_string(&neighbor->neighbor_main_addr);
307   
308   if (neighbor->status == 0) { // non SYM
309         style = "dashed";
310   }
311   else {
312     /* find best link to neighbor for the ETX */
313     //? why cant i just get it one time at fetch_olsrd_data??? (br1)
314     if(olsr_plugin_io(GETD__LINK_SET, &link, sizeof(link)) && link)
315     {
316       link_set = link; // for olsr_neighbor_best_link    
317       link = olsr_neighbor_best_link(&neighbor->neighbor_main_addr);
318       if (link) {
319         etx = calc_etx( link->loss_link_quality, link->neigh_link_quality);
320       }
321     }
322   }
323     
324   len = sprintf( buf, "\"%s\"[label=\"%.2f\", style=%s];\n", adr, etx, style );
325   ipc_send(buf, len);
326   
327    if (neighbor->is_mpr) {
328         len = sprintf( buf, "\"%s\"[shape=box];\n", adr );
329         ipc_send(buf, len);
330   }
331 }
332
333 static void inline
334 ipc_print_tc_link(struct tc_entry *entry, struct topo_dst *dst_entry)
335 {
336   char buf[256];
337   int len;
338   char* adr;
339   double etx = calc_etx( dst_entry->link_quality, dst_entry->inverse_link_quality );
340
341   adr = olsr_ip_to_string(&entry->T_last_addr);
342   len = sprintf( buf, "\"%s\" -> ", adr );
343   ipc_send(buf, len);
344   
345   adr = olsr_ip_to_string(&dst_entry->T_dest_addr);
346   len = sprintf( buf, "\"%s\"[label=\"%.2f\"];\n", adr, etx );
347   ipc_send(buf, len);
348 }
349
350 static void inline
351 ipc_print_net(union olsr_ip_addr *gw, union olsr_ip_addr *net, union hna_netmask *mask)
352 {
353   char *adr;
354
355   adr = olsr_ip_to_string(gw);
356   ipc_send("\"", 1);
357   ipc_send(adr, strlen(adr));
358   ipc_send("\" -> \"", strlen("\" -> \""));
359   adr = olsr_ip_to_string(net);
360   ipc_send(adr, strlen(adr));
361   ipc_send("/", 1);
362   adr = olsr_netmask_to_string(mask);
363   ipc_send(adr, strlen(adr));
364   ipc_send("\"[label=\"HNA\"];\n", strlen("\"[label=\"HNA\"];\n"));
365   ipc_send("\"", 1);
366   adr = olsr_ip_to_string(net);
367   ipc_send(adr, strlen(adr));
368   ipc_send("/", 1);
369   adr = olsr_netmask_to_string(mask);
370   ipc_send(adr, strlen(adr));
371   ipc_send("\"", 1);
372   ipc_send("[shape=diamond];\n", strlen("[shape=diamond];\n"));
373 }
374
375
376
377 int
378 ipc_send(char *data, int size)
379 {
380   if(!ipc_open)
381     return 0;
382
383   if (send(ipc_connection, data, size, MSG_NOSIGNAL) < 0) 
384     {
385       olsr_printf(1, "(DOT DRAW)IPC connection lost!\n");
386       close(ipc_connection);
387       ipc_open = 0;
388       return -1;
389     }
390
391   return 1;
392 }
393
394
395
396
397
398 /**
399  *Converts a olsr_ip_addr to a string
400  *Goes for both IPv4 and IPv6
401  *
402  *@param the IP to convert
403  *@return a pointer to a static string buffer
404  *representing the address in "dots and numbers"
405  *
406  */
407 char *
408 olsr_ip_to_string(union olsr_ip_addr *addr)
409 {
410   static int index = 0;
411   static char buff[4][100];
412   char *ret;
413   struct in_addr in;
414  
415   if(ipversion == AF_INET)
416     {
417       in.s_addr=addr->v4;
418       ret = inet_ntoa(in);
419     }
420   else
421     {
422       /* IPv6 */
423       ret = (char *)inet_ntop(AF_INET6, &addr->v6, ipv6_buf, sizeof(ipv6_buf));
424     }
425
426   strncpy(buff[index], ret, 100);
427
428   ret = buff[index];
429
430   index = (index + 1) & 3;
431
432   return ret;
433 }
434
435
436 /**
437  *This function is just as bad as the previous one :-(
438  */
439 char *
440 olsr_netmask_to_string(union hna_netmask *mask)
441 {
442   char *ret;
443   struct in_addr in;
444   
445   if(ipversion == AF_INET)
446     {
447       in.s_addr = mask->v4;
448       ret = inet_ntoa(in);
449       return ret;
450
451     }
452   else
453     {
454       /* IPv6 */
455       sprintf(netmask, "%d", mask->v6);
456       return netmask;
457     }
458
459   return ret;
460 }
461
462 #define COMP_IP(ip1, ip2) (!memcmp(ip1, ip2, ipsize))
463 struct link_entry *olsr_neighbor_best_link(union olsr_ip_addr *main)
464 {
465   struct link_entry *walker;
466   double best = 0.0;
467   double curr;
468   struct link_entry *res = NULL;
469
470   // loop through all links
471
472   for (walker = link_set; walker != NULL; walker = walker->next)
473   {
474     // check whether it's a link to the requested neighbor and
475     // whether the link's quality is better than what we have
476     if(COMP_IP(main, &walker->neighbor->neighbor_main_addr))
477     {
478       curr = walker->loss_link_quality * walker->neigh_link_quality;
479
480       if (curr >= best)
481       {
482         best = curr;
483         res = walker;
484       }
485     }
486   }
487
488   return res;
489 }
490