reduce CPU load in dot_draw
[olsrd.git] / lib / dot_draw / src / olsrd_dot_draw.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004, Andreas Tonnesen(andreto@olsr.org)
5  *                     includes code by Bruno Randolf
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * * Redistributions of source code must retain the above copyright
13  *   notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above copyright
15  *   notice, this list of conditions and the following disclaimer in
16  *   the documentation and/or other materials provided with the
17  *   distribution.
18  * * Neither the name of olsr.org, olsrd nor the names of its
19  *   contributors may be used to endorse or promote products derived
20  *   from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  *
35  * Visit http://www.olsr.org for more information.
36  *
37  * If you find this software useful feel free to make a donation
38  * to the project. For more information see the website or contact
39  * the copyright holders.
40  *
41  */
42
43 /*
44  * Dynamic linked library for the olsr.org olsr daemon
45  */
46
47 #ifdef _WRS_KERNEL
48 #include <vxWorks.h>
49 #include <sockLib.h>
50 #include <wrn/coreip/netinet/in.h>
51 #else
52 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
56 #include <sys/time.h>
57 #include <time.h>
58 #include <math.h>
59 #include <stdio.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <errno.h>
64 #include <stdarg.h>
65 #endif
66
67 #include "olsr.h"
68 #include "ipcalc.h"
69 #include "olsr_types.h"
70 #include "neighbor_table.h"
71 #include "two_hop_neighbor_table.h"
72 #include "tc_set.h"
73 #include "hna_set.h"
74 #include "mid_set.h"
75 #include "link_set.h"
76 #include "net_olsr.h"
77 #include "lq_plugin.h"
78 #include "common/autobuf.h"
79
80 #include "olsrd_dot_draw.h"
81 #include "olsrd_plugin.h"
82
83 #ifdef WIN32
84 #define close(x) closesocket(x)
85 #endif
86
87 #ifdef _WRS_KERNEL
88 static int ipc_open;
89 static int ipc_socket_up;
90 #define DOT_DRAW_PORT 2004
91 #endif
92
93 static int ipc_socket = -1;
94
95 struct autobuf outbuffer;
96 static int outbuffer_socket = -1;
97
98 static struct timer_entry *writetimer_entry = NULL;
99
100 /* IPC initialization function */
101 static int plugin_ipc_init(void);
102
103 /* Event function to register with the sceduler */
104 static int pcf_event(int, int, int);
105
106 static void ipc_action(int, void *, unsigned int);
107
108 static void ipc_print_neigh_link(struct autobuf *abuf, const struct neighbor_entry *neighbor);
109
110 static void ipc_print_tc_link(struct autobuf *abuf, const struct tc_entry *, const struct tc_edge_entry *);
111
112 static void ipc_print_net(struct autobuf *abuf, const union olsr_ip_addr *, const union olsr_ip_addr *, uint8_t);
113
114 /**
115  *Do initialization here
116  *
117  *This function is called by the my_init
118  *function in uolsrd_plugin.c
119  */
120 #ifdef _WRS_KERNEL
121 int
122 olsrd_dotdraw_init(void)
123 #else
124 int
125 olsrd_plugin_init(void)
126 #endif
127 {
128   /* Initial IPC */
129   plugin_ipc_init();
130
131   /* Register the "ProcessChanges" function */
132   register_pcf(&pcf_event);
133
134   return 1;
135 }
136
137 /**
138  * destructor - called at unload
139  */
140 #ifdef _WRS_KERNEL
141 void
142 olsrd_dotdraw_exit(void)
143 #else
144 void
145 olsr_plugin_exit(void)
146 #endif
147 {
148   if (writetimer_entry) {
149     close(outbuffer_socket);
150     abuf_free(&outbuffer);
151     olsr_stop_timer(writetimer_entry);
152   }
153   if (ipc_socket != -1) {
154     CLOSE(ipc_socket);
155   }
156 }
157
158 static void
159 ipc_print_neigh_link(struct autobuf *abuf, const struct neighbor_entry *neighbor)
160 {
161   static const char DASHED[] = "dashed";
162   static const char SOLID[] = "solid";
163
164   struct ipaddr_str mainaddrstrbuf, strbuf;
165   olsr_linkcost etx = 0.0;
166   const char *style;
167   const char *adr = olsr_ip_to_string(&mainaddrstrbuf, &olsr_cnf->main_addr);
168   struct link_entry *the_link;
169   struct lqtextbuffer lqbuffer;
170
171   if (neighbor->status == 0) {  /* non SYM */
172     style = DASHED;
173   } else {
174     the_link = get_best_link_to_neighbor(&neighbor->neighbor_main_addr);
175     if (the_link) {
176       etx = the_link->linkcost;
177     }
178     style = SOLID;
179   }
180
181   abuf_appendf(abuf, "\"%s\" -> \"%s\"[label=\"%s\", style=%s];\n", adr, olsr_ip_to_string(&strbuf, &neighbor->neighbor_main_addr),
182                get_linkcost_text(etx, false, &lqbuffer), style);
183
184   if (neighbor->is_mpr) {
185     abuf_appendf(abuf, "\"%s\"[shape=box];\n", adr);
186   }
187 }
188
189 static int
190 plugin_ipc_init(void)
191 {
192   struct sockaddr_in sock_in;
193   uint32_t yes = 1;
194
195   if (ipc_socket != -1) {
196     close(ipc_socket);
197   }
198
199   /* Init ipc socket */
200   ipc_socket = socket(AF_INET, SOCK_STREAM, 0);
201   if (ipc_socket == -1) {
202     olsr_printf(1, "(DOT DRAW)IPC socket %s\n", strerror(errno));
203     return 0;
204   }
205
206   if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) {
207     perror("SO_REUSEADDR failed");
208     CLOSE(ipc_socket);
209     return 0;
210   }
211 #if (defined __FreeBSD__ || __FreeBSD_kernel__) && defined SO_NOSIGPIPE
212   if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) {
213     perror("SO_REUSEADDR failed");
214     CLOSE(ipc_socket);
215     return 0;
216   }
217 #endif
218
219   /* Bind the socket */
220
221   /* complete the socket structure */
222   memset(&sock_in, 0, sizeof(sock_in));
223   sock_in.sin_family = AF_INET;
224   sock_in.sin_addr.s_addr = ipc_listen_ip.v4.s_addr;
225   sock_in.sin_port = htons(ipc_port);
226
227   /* bind the socket to the port number */
228   if (bind(ipc_socket, (struct sockaddr *)&sock_in, sizeof(sock_in)) == -1) {
229     olsr_printf(1, "(DOT DRAW)IPC bind %s\n", strerror(errno));
230     CLOSE(ipc_socket);
231     return 0;
232   }
233
234   /* show that we are willing to listen */
235   if (listen(ipc_socket, 1) == -1) {
236     olsr_printf(1, "(DOT DRAW)IPC listen %s\n", strerror(errno));
237     CLOSE(ipc_socket);
238     return 0;
239   }
240
241   /* Register with olsrd */
242 #if 0
243   printf("Adding socket with olsrd\n");
244 #endif
245   add_olsr_socket(ipc_socket, &ipc_action, NULL, NULL, SP_PR_READ);
246
247   return 1;
248 }
249
250 static void
251 ipc_action(int fd __attribute__ ((unused)), void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused)))
252 {
253   struct sockaddr_in pin;
254   socklen_t addrlen = sizeof(struct sockaddr_in);
255   int ipc_connection;
256
257   ipc_connection = accept(ipc_socket, (struct sockaddr *)&pin, &addrlen);
258   if (ipc_connection == -1) {
259     olsr_printf(1, "(DOT DRAW)IPC accept: %s\n", strerror(errno));
260     return;
261   }
262
263   if (outbuffer_socket != -1) {
264     olsr_printf(1, "(DOT DRAW)Only one connection at once allowed.\n");
265     close(ipc_connection);
266     return;
267   }
268 #ifndef _WRS_KERNEL
269   if (!ip4equal(&pin.sin_addr, &ipc_accept_ip.v4) && ipc_accept_ip.v4.s_addr != INADDR_ANY) {
270     olsr_printf(0, "Front end-connection from foreign host (%s) not allowed!\n", inet_ntoa(pin.sin_addr));
271     CLOSE(ipc_connection);
272     return;
273   }
274 #endif
275   olsr_printf(1, "(DOT DRAW)IPC: Connection from %s\n", inet_ntoa(pin.sin_addr));
276
277   abuf_init(&outbuffer, 4096);
278   outbuffer_socket = ipc_connection;
279
280   pcf_event(1, 1, 1);
281 }
282
283 static void
284 dotdraw_write_data(void *foo __attribute__ ((unused))) {
285   fd_set set;
286   int result;
287   struct timeval tv;
288
289   FD_ZERO(&set);
290   /* prevent warning on WIN32 */
291   FD_SET((unsigned int)outbuffer_socket, &set);
292
293   tv.tv_sec = 0;
294   tv.tv_usec = 0;
295
296   result = select(outbuffer_socket + 1, NULL, &set, NULL, &tv);
297   if (result <= 0) {
298     return;
299   }
300
301   if (FD_ISSET(outbuffer_socket, &set)) {
302     result = send(outbuffer_socket, outbuffer.buf, outbuffer.len, 0);
303     if (result > 0)
304       abuf_pull(&outbuffer, result);
305
306     if (result <= 0) {
307       /* close this socket and cleanup*/
308       close(outbuffer_socket);
309       abuf_free(&outbuffer);
310       olsr_stop_timer(writetimer_entry);
311       writetimer_entry = NULL;
312       outbuffer_socket = -1;
313     }
314   }
315   if (outbuffer.len == 0) {
316     /* close this socket and cleanup*/
317     close(outbuffer_socket);
318     abuf_free(&outbuffer);
319     olsr_stop_timer(writetimer_entry);
320     writetimer_entry = NULL;
321     outbuffer_socket = -1;
322   }
323 }
324
325 /**
326  *Scheduled event
327  */
328 static int
329 pcf_event(int my_changes_neighborhood, int my_changes_topology, int my_changes_hna)
330 {
331   struct neighbor_entry *neighbor_table_tmp;
332   struct tc_entry *tc;
333   struct tc_edge_entry *tc_edge;
334   struct hna_entry *tmp_hna;
335   struct hna_net *tmp_net;
336   struct ip_prefix_list *hna;
337   int res = 0;
338
339   /* nothing to do */
340   if (outbuffer_socket == -1) {
341     return 1;
342   }
343
344   if (my_changes_neighborhood || my_changes_topology || my_changes_hna) {
345     /* Print tables to IPC socket */
346     abuf_puts(&outbuffer, "digraph topology\n{\n");
347
348     /* Neighbors */
349     OLSR_FOR_ALL_NBR_ENTRIES(neighbor_table_tmp) {
350       ipc_print_neigh_link(&outbuffer, neighbor_table_tmp);
351     }
352     OLSR_FOR_ALL_NBR_ENTRIES_END(neighbor_table_tmp);
353
354     /* Topology */
355     OLSR_FOR_ALL_TC_ENTRIES(tc) {
356       OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
357         if (tc_edge->edge_inv) {
358           ipc_print_tc_link(&outbuffer, tc, tc_edge);
359         }
360       }
361       OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
362     }
363     OLSR_FOR_ALL_TC_ENTRIES_END(tc);
364
365     /* HNA entries */
366     OLSR_FOR_ALL_HNA_ENTRIES(tmp_hna) {
367
368       /* Check all networks */
369       for (tmp_net = tmp_hna->networks.next; tmp_net != &tmp_hna->networks; tmp_net = tmp_net->next) {
370         ipc_print_net(&outbuffer, &tmp_hna->A_gateway_addr,
371             &tmp_net->hna_prefix.prefix, tmp_net->hna_prefix.prefix_len);
372       }
373     }
374     OLSR_FOR_ALL_HNA_ENTRIES_END(tmp_hna);
375
376     /* Local HNA entries */
377     for (hna = olsr_cnf->hna_entries; hna != NULL; hna = hna->next) {
378       ipc_print_net(&outbuffer, &olsr_cnf->main_addr, &hna->net.prefix, hna->net.prefix_len);
379     }
380     abuf_puts(&outbuffer, "}\n\n");
381
382     res = 1;
383   }
384
385   if (writetimer_entry == NULL) {
386     writetimer_entry = olsr_start_timer(100, 0, OLSR_TIMER_PERIODIC, &dotdraw_write_data, NULL, 0);
387   }
388
389   return res;
390 }
391
392 static void
393 ipc_print_tc_link(struct autobuf *abuf, const struct tc_entry *entry, const struct tc_edge_entry *dst_entry)
394 {
395   struct ipaddr_str strbuf1, strbuf2;
396   struct lqtextbuffer lqbuffer;
397
398   abuf_appendf(abuf, "\"%s\" -> \"%s\"[label=\"%s\"];\n", olsr_ip_to_string(&strbuf1, &entry->addr),
399                olsr_ip_to_string(&strbuf2, &dst_entry->T_dest_addr), get_linkcost_text(dst_entry->cost, false, &lqbuffer));
400 }
401
402 static void
403 ipc_print_net(struct autobuf *abuf, const union olsr_ip_addr *gw, const union olsr_ip_addr *net, uint8_t prefixlen)
404 {
405   struct ipaddr_str gwbuf, netbuf;
406
407   abuf_appendf(abuf, "\"%s\" -> \"%s/%d\"[label=\"HNA\"];\n", olsr_ip_to_string(&gwbuf, gw), olsr_ip_to_string(&netbuf, net), prefixlen);
408
409   abuf_appendf(abuf, "\"%s/%d\"[shape=diamond];\n", olsr_ip_to_string(&netbuf, net), prefixlen);
410 }
411
412 /*
413  * Local Variables:
414  * c-basic-offset: 2
415  * indent-tabs-mode: nil
416  * End:
417  */