The plugin now compiles for FreeBSD
[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.10 2005/01/30 15:45:11 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 #ifdef __FreeBSD__
103       if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) 
104       {
105         perror("SO_REUSEADDR failed");
106         return 0;
107       }
108 #endif
109
110       /* Bind the socket */
111       
112       /* complete the socket structure */
113       memset(&sin, 0, sizeof(sin));
114       sin.sin_family = AF_INET;
115       sin.sin_addr.s_addr = INADDR_ANY;
116       sin.sin_port = htons(ipc_port);
117       
118       /* bind the socket to the port number */
119       if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) 
120         {
121           olsr_printf(1, "(DOT DRAW)IPC bind %s\n", strerror(errno));
122           return 0;
123         }
124       
125       /* show that we are willing to listen */
126       if (listen(ipc_socket, 1) == -1) 
127         {
128           olsr_printf(1, "(DOT DRAW)IPC listen %s\n", strerror(errno));
129           return 0;
130         }
131
132
133       /* Register with olsrd */
134       add_olsr_socket(ipc_socket, &ipc_action);
135       ipc_socket_up = 1;
136     }
137
138   return 1;
139 }
140
141 void
142 ipc_action(int fd)
143 {
144   struct sockaddr_in pin;
145   socklen_t addrlen;
146   char *addr;  
147
148   addrlen = sizeof(struct sockaddr_in);
149
150   if ((ipc_connection = accept(ipc_socket, (struct sockaddr *)  &pin, &addrlen)) == -1)
151     {
152       olsr_printf(1, "(DOT DRAW)IPC accept: %s\n", strerror(errno));
153       exit(1);
154     }
155   else
156     {
157       addr = inet_ntoa(pin.sin_addr);
158       if(ntohl(pin.sin_addr.s_addr) != ntohl(ipc_accept_ip.s_addr))
159         {
160           olsr_printf(1, "Front end-connection from foregin host(%s) not allowed!\n", addr);
161           close(ipc_connection);
162           return;
163         }
164       else
165         {
166           ipc_open = 1;
167           olsr_printf(1, "(DOT DRAW)IPC: Connection from %s\n",addr);
168           pcf_event(1, 1, 1);
169         }
170     }
171
172 }
173
174 /*
175  * destructor - called at unload
176  */
177 void
178 olsr_plugin_exit()
179 {
180   if(ipc_open)
181     close(ipc_socket);
182 }
183
184
185
186 /* Mulitpurpose funtion */
187 int
188 plugin_io(int cmd, void *data, size_t size)
189 {
190
191   switch(cmd)
192     {
193     default:
194       return 0;
195     }
196   
197   return 1;
198 }
199
200
201
202
203 /**
204  *Scheduled event
205  */
206 int
207 pcf_event(int changes_neighborhood,
208           int changes_topology,
209           int changes_hna)
210 {
211   int res;
212   olsr_u8_t index;
213   struct neighbor_entry *neighbor_table_tmp;
214   struct neighbor_2_list_entry *list_2;
215   struct tc_entry *entry;
216   struct topo_dst *dst_entry;
217   struct hna_entry *tmp_hna;
218   struct hna_net *tmp_net;
219
220   res = 0;
221
222   if(changes_neighborhood || changes_topology || changes_hna)
223     {
224       /* Print tables to IPC socket */
225
226       ipc_send("digraph topology\n{\n", strlen("digraph topology\n{\n"));
227
228       /* Neighbors */
229       for(index=0;index<HASHSIZE;index++)
230         {
231           
232           for(neighbor_table_tmp = neighbortable[index].next;
233               neighbor_table_tmp != &neighbortable[index];
234               neighbor_table_tmp = neighbor_table_tmp->next)
235             {
236               ipc_print_neigh_link( neighbor_table_tmp );
237             }
238         }
239
240       /* Topology */  
241       for(index=0;index<HASHSIZE;index++)
242         {
243           /* For all TC entries */
244           entry = tc_table[index].next;
245           while(entry != &tc_table[index])
246             {
247               /* For all destination entries of that TC entry */
248               dst_entry = entry->destinations.next;
249               while(dst_entry != &entry->destinations)
250                 {
251                   ipc_print_tc_link(entry, dst_entry);
252                   dst_entry = dst_entry->next;
253                 }
254               entry = entry->next;
255             }
256         }
257
258       /* HNA entries */
259       for(index=0;index<HASHSIZE;index++)
260         {
261           tmp_hna = hna_set[index].next;
262           /* Check all entrys */
263           while(tmp_hna != &hna_set[index])
264             {
265               /* Check all networks */
266               tmp_net = tmp_hna->networks.next;
267               
268               while(tmp_net != &tmp_hna->networks)
269                 {
270                   ipc_print_net(&tmp_hna->A_gateway_addr, 
271                                 &tmp_net->A_network_addr, 
272                                 &tmp_net->A_netmask);
273                   tmp_net = tmp_net->next;
274                 }
275               
276               tmp_hna = tmp_hna->next;
277             }
278         }
279
280
281       ipc_send("}\n\n", strlen("}\n\n"));
282
283       res = 1;
284     }
285
286
287   if(!ipc_socket_up)
288     plugin_ipc_init();
289
290   return res;
291 }
292
293 #define MIN_LINK_QUALITY 0.01
294 double calc_etx( double loss, double neigh_loss ) {
295         if (loss < MIN_LINK_QUALITY || neigh_loss < MIN_LINK_QUALITY)
296                 return 0.0;
297         else
298                 return 1.0 / (loss * neigh_loss);
299 }
300
301 static void inline
302 ipc_print_neigh_link(struct neighbor_entry *neighbor)
303 {
304   char buf[256];
305   int len;
306   char* adr;
307   double etx=0.0;
308   char* style = "solid";
309   struct link_entry* link;
310   adr = olsr_ip_to_string(main_addr);
311   len = sprintf( buf, "\"%s\" -> ", adr );
312   ipc_send(buf, len);
313   
314   adr = olsr_ip_to_string(&neighbor->neighbor_main_addr);
315   
316   if (neighbor->status == 0) { // non SYM
317         style = "dashed";
318   }
319   else {
320     /* find best link to neighbor for the ETX */
321     //? why cant i just get it one time at fetch_olsrd_data??? (br1)
322     if(olsr_plugin_io(GETD__LINK_SET, &link, sizeof(link)) && link)
323     {
324       link_set = link; // for olsr_neighbor_best_link    
325       link = olsr_neighbor_best_link(&neighbor->neighbor_main_addr);
326       if (link) {
327         etx = calc_etx( link->loss_link_quality, link->neigh_link_quality);
328       }
329     }
330   }
331     
332   len = sprintf( buf, "\"%s\"[label=\"%.2f\", style=%s];\n", adr, etx, style );
333   ipc_send(buf, len);
334   
335    if (neighbor->is_mpr) {
336         len = sprintf( buf, "\"%s\"[shape=box];\n", adr );
337         ipc_send(buf, len);
338   }
339 }
340
341 static void inline
342 ipc_print_tc_link(struct tc_entry *entry, struct topo_dst *dst_entry)
343 {
344   char buf[256];
345   int len;
346   char* adr;
347   double etx = calc_etx( dst_entry->link_quality, dst_entry->inverse_link_quality );
348
349   adr = olsr_ip_to_string(&entry->T_last_addr);
350   len = sprintf( buf, "\"%s\" -> ", adr );
351   ipc_send(buf, len);
352   
353   adr = olsr_ip_to_string(&dst_entry->T_dest_addr);
354   len = sprintf( buf, "\"%s\"[label=\"%.2f\"];\n", adr, etx );
355   ipc_send(buf, len);
356 }
357
358 static void inline
359 ipc_print_net(union olsr_ip_addr *gw, union olsr_ip_addr *net, union hna_netmask *mask)
360 {
361   char *adr;
362
363   adr = olsr_ip_to_string(gw);
364   ipc_send("\"", 1);
365   ipc_send(adr, strlen(adr));
366   ipc_send("\" -> \"", strlen("\" -> \""));
367   adr = olsr_ip_to_string(net);
368   ipc_send(adr, strlen(adr));
369   ipc_send("/", 1);
370   adr = olsr_netmask_to_string(mask);
371   ipc_send(adr, strlen(adr));
372   ipc_send("\"[label=\"HNA\"];\n", strlen("\"[label=\"HNA\"];\n"));
373   ipc_send("\"", 1);
374   adr = olsr_ip_to_string(net);
375   ipc_send(adr, strlen(adr));
376   ipc_send("/", 1);
377   adr = olsr_netmask_to_string(mask);
378   ipc_send(adr, strlen(adr));
379   ipc_send("\"", 1);
380   ipc_send("[shape=diamond];\n", strlen("[shape=diamond];\n"));
381 }
382
383
384
385 int
386 ipc_send(char *data, int size)
387 {
388   if(!ipc_open)
389     return 0;
390
391 #ifdef __FreeBSD__
392   if (send(ipc_connection, data, size, 0) < 0) 
393 #else
394   if (send(ipc_connection, data, size, MSG_NOSIGNAL) < 0) 
395 #endif
396     {
397       olsr_printf(1, "(DOT DRAW)IPC connection lost!\n");
398       close(ipc_connection);
399       ipc_open = 0;
400       return -1;
401     }
402
403   return 1;
404 }
405
406
407
408
409
410 /**
411  *Converts a olsr_ip_addr to a string
412  *Goes for both IPv4 and IPv6
413  *
414  *@param the IP to convert
415  *@return a pointer to a static string buffer
416  *representing the address in "dots and numbers"
417  *
418  */
419 char *
420 olsr_ip_to_string(union olsr_ip_addr *addr)
421 {
422   static int index = 0;
423   static char buff[4][100];
424   char *ret;
425   struct in_addr in;
426  
427   if(ipversion == AF_INET)
428     {
429       in.s_addr=addr->v4;
430       ret = inet_ntoa(in);
431     }
432   else
433     {
434       /* IPv6 */
435       ret = (char *)inet_ntop(AF_INET6, &addr->v6, ipv6_buf, sizeof(ipv6_buf));
436     }
437
438   strncpy(buff[index], ret, 100);
439
440   ret = buff[index];
441
442   index = (index + 1) & 3;
443
444   return ret;
445 }
446
447
448 /**
449  *This function is just as bad as the previous one :-(
450  */
451 char *
452 olsr_netmask_to_string(union hna_netmask *mask)
453 {
454   char *ret;
455   struct in_addr in;
456   
457   if(ipversion == AF_INET)
458     {
459       in.s_addr = mask->v4;
460       ret = inet_ntoa(in);
461       return ret;
462
463     }
464   else
465     {
466       /* IPv6 */
467       sprintf(netmask, "%d", mask->v6);
468       return netmask;
469     }
470
471   return ret;
472 }
473
474 #define COMP_IP(ip1, ip2) (!memcmp(ip1, ip2, ipsize))
475 struct link_entry *olsr_neighbor_best_link(union olsr_ip_addr *main)
476 {
477   struct link_entry *walker;
478   double best = 0.0;
479   double curr;
480   struct link_entry *res = NULL;
481
482   // loop through all links
483
484   for (walker = link_set; walker != NULL; walker = walker->next)
485   {
486     // check whether it's a link to the requested neighbor and
487     // whether the link's quality is better than what we have
488     if(COMP_IP(main, &walker->neighbor->neighbor_main_addr))
489     {
490       curr = walker->loss_link_quality * walker->neigh_link_quality;
491
492       if (curr >= best)
493       {
494         best = curr;
495         res = walker;
496       }
497     }
498   }
499
500   return res;
501 }
502