* applied patches from the most recent FreiFunkFirmware (and fixed compile errors...
[olsrd.git] / lib / txtinfo / src / olsrd_txtinfo.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  *                     includes code by Andreas Lopatic
6  *                     includes code by Sven-Ola Tuecke
7  *                     includes code by Lorenz Schori
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without 
11  * modification, are permitted provided that the following conditions 
12  * are met:
13  *
14  * * Redistributions of source code must retain the above copyright 
15  *   notice, this list of conditions and the following disclaimer.
16  * * Redistributions in binary form must reproduce the above copyright 
17  *   notice, this list of conditions and the following disclaimer in 
18  *   the documentation and/or other materials provided with the 
19  *   distribution.
20  * * Neither the name of olsr.org, olsrd nor the names of its 
21  *   contributors may be used to endorse or promote products derived 
22  *   from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  * Visit http://www.olsr.org for more information.
38  *
39  * If you find this software useful feel free to make a donation
40  * to the project. For more information see the website or contact
41  * the copyright holders.
42  *
43  * $Id: olsrd_txtinfo.c,v 1.1 2007/01/31 12:38:26 bernd67 Exp $
44  */
45
46 /*
47  * Dynamic linked library for the olsr.org olsr daemon
48  */
49
50  
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <sys/select.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 <stdarg.h>
63 #include <unistd.h>
64 #include <errno.h>
65
66 #include "olsr.h"
67 #include "olsr_types.h"
68 #include "neighbor_table.h"
69 #include "two_hop_neighbor_table.h"
70 #include "mpr_selector_set.h"
71 #include "tc_set.h"
72 #include "hna_set.h"
73 #include "mid_set.h"
74 #include "link_set.h"
75 #include "socket_parser.h"
76
77 #include "olsrd_txtinfo.h"
78 #include "olsrd_plugin.h"
79
80
81 #ifdef WIN32
82 #define close(x) closesocket(x)
83 #endif
84
85
86 static int ipc_socket;
87 static int ipc_open;
88 static int ipc_connection;
89 static int ipc_socket_up;
90
91
92 /* IPC initialization function */
93 static int
94 plugin_ipc_init(void);
95
96 static void 
97 send_info(int neighonly);
98
99 static void
100 ipc_action(int);
101
102 static void inline
103 ipc_print_neigh_link(void);
104
105 static void inline
106 ipc_print_routes(void);
107
108 static void inline
109 ipc_print_topology(void);
110
111 static void inline
112 ipc_print_hna(void);
113
114 #define TXT_IPC_BUFSIZE 256
115 static int 
116 ipc_sendf(const char* format, ...);
117
118 /**
119  *Do initialization here
120  *
121  *This function is called by the my_init
122  *function in uolsrd_plugin.c
123  */
124 int
125 olsrd_plugin_init()
126 {
127   /* Initial IPC value */
128   ipc_open = 0;
129   ipc_socket_up = 0;
130
131   plugin_ipc_init();
132   return 1;
133 }
134
135
136 /**
137  * destructor - called at unload
138  */
139 void
140 olsr_plugin_exit()
141 {
142   if(ipc_open)
143     close(ipc_socket);
144 }
145
146
147
148 static int
149 plugin_ipc_init()
150 {
151   struct sockaddr_in sin;
152   olsr_u32_t yes = 1;
153
154   /* Init ipc socket */
155   if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
156     {
157 #ifndef NODEBUG
158       olsr_printf(1, "(TXTINFO) socket()=%s\n", strerror(errno));
159 #endif
160       return 0;
161     }
162   else
163     {
164       if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) 
165       {
166 #ifndef NODEBUG
167         olsr_printf(1, "(TXTINFO) setsockopt()=%s\n", strerror(errno));
168 #endif
169         return 0;
170       }
171
172 #if defined __FreeBSD__ && defined SO_NOSIGPIPE
173       if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) 
174       {
175         perror("SO_REUSEADDR failed");
176         return 0;
177       }
178 #endif
179
180       /* Bind the socket */
181       
182       /* complete the socket structure */
183       memset(&sin, 0, sizeof(sin));
184       sin.sin_family = AF_INET;
185       sin.sin_addr.s_addr = INADDR_ANY;
186       sin.sin_port = htons(ipc_port);
187       
188       /* bind the socket to the port number */
189       if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) 
190         {
191 #ifndef NODEBUG
192           olsr_printf(1, "(TXTINFO) bind()=%s\n", strerror(errno));
193 #endif
194           return 0;
195         }
196       
197       /* show that we are willing to listen */
198       if (listen(ipc_socket, 1) == -1) 
199         {
200 #ifndef NODEBUG
201           olsr_printf(1, "(TXTINFO) listen()=%s\n", strerror(errno));
202 #endif
203           return 0;
204         }
205         
206       /* Register with olsrd */
207       add_olsr_socket(ipc_socket, &ipc_action);
208
209 #ifndef NODEBUG
210       olsr_printf(2, "(TXTINFO) listening on port %d\n",ipc_port);
211 #endif
212       ipc_socket_up = 1;
213     }
214
215   return 1;
216 }
217
218
219 static void
220 ipc_action(int fd)
221 {
222   struct sockaddr_in pin;
223   socklen_t addrlen;
224   char *addr;  
225
226   addrlen = sizeof(struct sockaddr_in);
227
228   if(ipc_open)
229     return;
230
231   if ((ipc_connection = accept(fd, (struct sockaddr *)  &pin, &addrlen)) == -1)
232     {
233 #ifndef NODEBUG
234       olsr_printf(1, "(TXTINFO) accept()=%s\n", strerror(errno));
235 #endif
236       exit(1);
237     }
238   else
239     {
240       addr = inet_ntoa(pin.sin_addr);
241       if(ntohl(pin.sin_addr.s_addr) != ntohl(ipc_accept_ip.s_addr))
242         {
243           olsr_printf(1, "(TXTINFO) From host(%s) not allowed!\n", addr);
244           close(ipc_connection);
245           return;
246         }
247       else
248         {
249       ipc_open = 1;
250 #ifndef NODEBUG
251       olsr_printf(2, "(TXTINFO) Connect from %s\n",addr);
252 #endif
253       
254       /* purge read buffer to prevent blocking on linux*/
255       fd_set rfds;
256       FD_ZERO(&rfds);
257       FD_SET(ipc_connection, &rfds);
258       struct timeval tv = {0,0};
259       int neighonly = 0;
260       if(select(ipc_connection+1, &rfds, NULL, NULL, &tv)) {
261         char requ[128];
262         ssize_t s = recv(ipc_connection, &requ, sizeof(requ), 0);
263         if (0 < s) {
264           requ[s] = 0;
265           /* To print out neighbours only on the Freifunk Status
266            * page the normal output is somewhat lengthy. The
267            * header parsing is sufficient for standard wget.
268            */
269           neighonly = (0 != strstr(requ, "/neighbours"));
270         }
271       }
272
273       send_info(neighonly);
274           
275       close(ipc_connection);
276       ipc_open = 0;
277     }
278   }
279 }
280
281 static void inline
282 ipc_print_neigh_link(void)
283 {
284   struct neighbor_entry *neigh;
285   struct neighbor_2_list_entry *list_2;
286   struct link_entry *link = NULL;
287   int index, thop_cnt;
288
289         ipc_sendf("Table: Links\nLocal IP\tremote IP\tHysteresis\tLinkQuality\tlost\ttotal\tNLQ\tETX\n");
290
291   /* Link set */
292   link = link_set;
293         while(link)
294         {
295         ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%d\t%d\t%0.2f\t%0.2f\t\n",
296                         olsr_ip_to_string(&link->local_iface_addr),
297                         olsr_ip_to_string(&link->neighbor_iface_addr),
298                         link->L_link_quality, 
299                         link->loss_link_quality,
300                         link->lost_packets, 
301                         link->total_packets,
302                         link->neigh_link_quality, 
303                         (link->loss_link_quality * link->neigh_link_quality) ? 1.0 / (link->loss_link_quality * link->neigh_link_quality) : 0.0);
304                 link = link->next;
305       }
306         ipc_sendf("\nTable: Neighbors\nIP address\tSYM\tMPR\tMPRS\tWillingness\t2 Hop Neighbors\n");
307
308   /* Neighbors */
309   for(index=0;index<HASHSIZE;index++)
310     {
311       for(neigh = neighbortable[index].next;
312           neigh != &neighbortable[index];
313           neigh = neigh->next)
314         {
315                 ipc_sendf( 
316                           "%s\t%s\t%s\t%s\t%d\t", 
317                           olsr_ip_to_string(&neigh->neighbor_main_addr),
318                           (neigh->status == SYM) ? "YES" : "NO",
319                           neigh->is_mpr ? "YES" : "NO",
320                           olsr_lookup_mprs_set(&neigh->neighbor_main_addr) ? "YES" : "NO",
321                           neigh->willingness);
322
323           thop_cnt = 0;
324
325           for(list_2 = neigh->neighbor_2_list.next;
326               list_2 != &neigh->neighbor_2_list;
327               list_2 = list_2->next)
328             {
329               //size += sprintf(&buf[size], "<option>%s</option>\n", olsr_ip_to_string(&list_2->neighbor_2->neighbor_2_addr));
330               thop_cnt ++;
331             }
332                 ipc_sendf("%d\n", thop_cnt);
333         }
334     }
335
336         ipc_sendf("\n");
337 }
338
339
340 static void inline
341 ipc_print_routes(void)
342 {
343   int size = 0, index;
344   struct rt_entry *routes;
345
346         ipc_sendf("Table: Routes\nDestination\tGateway\tMetric\tETX\tInterface\tType\n");
347
348   /* Neighbors */
349   for(index = 0;index < HASHSIZE;index++)
350     {
351       for(routes = routingtable[index].next;
352           routes != &routingtable[index];
353           routes = routes->next)
354         {
355                 size = 0;
356                 ipc_sendf( "%s\t%s\t%d\t%.2f\t%s\tHOST\n",
357                           olsr_ip_to_string(&routes->rt_dst),
358                           olsr_ip_to_string(&routes->rt_router),
359                           routes->rt_metric,
360                           routes->rt_etx,
361                           routes->rt_if->int_name);
362         }
363     }
364
365   /* HNA */
366   for(index = 0;index < HASHSIZE;index++)
367     {
368       for(routes = hna_routes[index].next;
369           routes != &hna_routes[index];
370           routes = routes->next)
371         {
372                 ipc_sendf("%s\t%s\t%d\t%s\t\tHNA\n",
373                           olsr_ip_to_string(&routes->rt_dst),
374                           olsr_ip_to_string(&routes->rt_router),
375                           routes->rt_metric,
376                           routes->rt_if->int_name);
377         }
378     }
379
380         ipc_sendf("\n");
381
382 }
383
384 static void inline
385 ipc_print_topology(void)
386 {
387   olsr_u8_t index;
388   struct tc_entry *entry;
389   struct topo_dst *dst_entry;
390
391
392   ipc_sendf("Table: Topology\nDestination IP\tLast hop IP\tLQ\tILQ\tETX\n");
393
394   /* Topology */  
395   for(index=0;index<HASHSIZE;index++)
396     {
397       /* For all TC entries */
398       entry = tc_table[index].next;
399       while(entry != &tc_table[index])
400         {
401           /* For all destination entries of that TC entry */
402           dst_entry = entry->destinations.next;
403           while(dst_entry != &entry->destinations)
404             {
405                         ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%0.2f\n", 
406                               olsr_ip_to_string(&dst_entry->T_dest_addr),
407                               olsr_ip_to_string(&entry->T_last_addr), 
408                               dst_entry->link_quality,
409                               dst_entry->inverse_link_quality,
410                               (dst_entry->link_quality * dst_entry->inverse_link_quality) ? 1.0 / (dst_entry->link_quality * dst_entry->inverse_link_quality) : 0.0);
411                 
412               dst_entry = dst_entry->next;
413             }
414           entry = entry->next;
415         }
416     }
417
418   ipc_sendf("\n");
419 }
420
421 static void inline
422 ipc_print_hna(void)
423 {
424   int size;
425   olsr_u8_t index;
426   struct hna_entry *tmp_hna;
427   struct hna_net *tmp_net;
428
429   size = 0;
430
431   ipc_sendf("Table: HNA\nNetwork\tNetmask\tGateway\n");
432
433   /* Announced HNA entries */
434         struct hna4_entry *hna4;
435         for(hna4 = olsr_cnf->hna4_entries; hna4; hna4 = hna4->next)
436           {
437                         ipc_sendf("%s\t%s\t%s\n",
438                           olsr_ip_to_string(&hna4->net),
439                           olsr_ip_to_string(&hna4->netmask),
440                                 olsr_ip_to_string(&olsr_cnf->main_addr));
441           }
442         struct hna6_entry *hna6;
443         for(hna6 = olsr_cnf->hna6_entries; hna6; hna6 = hna6->next)
444           {
445                         ipc_sendf("%s\t%d\t%s\n",
446                           olsr_ip_to_string(&hna6->net),
447                                 hna6->prefix_len,
448                         olsr_ip_to_string(&olsr_cnf->main_addr));
449                 }
450
451   /* HNA entries */
452   for(index=0;index<HASHSIZE;index++)
453     {
454       tmp_hna = hna_set[index].next;
455       /* Check all entrys */
456       while(tmp_hna != &hna_set[index])
457         {
458           /* Check all networks */
459           tmp_net = tmp_hna->networks.next;
460               
461           while(tmp_net != &tmp_hna->networks)
462             {
463                 if (AF_INET == olsr_cnf->ip_version) {
464                         ipc_sendf("%s\t%s\t%s\n",
465                                 olsr_ip_to_string(&tmp_net->A_network_addr),
466                                 olsr_ip_to_string((union olsr_ip_addr *)&tmp_net->A_netmask.v4),
467                                 olsr_ip_to_string(&tmp_hna->A_gateway_addr));
468                 }
469                 else {
470                         ipc_sendf("%s\t%d\t%s\n",
471                                 olsr_ip_to_string(&tmp_net->A_network_addr),
472                                 tmp_net->A_netmask.v6,
473                                 olsr_ip_to_string(&tmp_hna->A_gateway_addr));
474                 }
475               tmp_net = tmp_net->next;
476             }
477               
478           tmp_hna = tmp_hna->next;
479         }
480     }
481
482         ipc_sendf("\n");
483
484 }
485
486
487 static void 
488 send_info(int neighonly)
489 {
490         
491         /* Print minimal http header */
492         ipc_sendf("HTTP/1.0 200 OK\n");
493         ipc_sendf("Content-type: text/plain\n\n");
494
495         /* Print tables to IPC socket */
496         
497         /* links + Neighbors */
498         ipc_print_neigh_link();
499         
500         /* topology */
501         if (!neighonly) ipc_print_topology();
502         
503         /* hna */
504         if (!neighonly) ipc_print_hna();
505
506         /* routes */
507         if (!neighonly) ipc_print_routes();
508 }
509
510 /*
511  * In a bigger mesh, there are probs with the fixed
512  * bufsize. Because the Content-Length header is
513  * optional, the sprintf() is changed to a more
514  * scalable solution here.
515  */
516  
517 static int 
518 ipc_sendf(const char* format, ...)
519 {
520         char txtnetbuf[TXT_IPC_BUFSIZE];
521
522         va_list arg;
523         int rv;
524 #if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __MacOSX__
525         int flags = 0;
526 #else
527         int flags = MSG_NOSIGNAL;
528 #endif
529         va_start(arg, format);
530         rv = vsnprintf(txtnetbuf, sizeof(txtnetbuf), format, arg);
531         va_end(arg);
532         if(ipc_socket_up) {
533                 if (0 > send(ipc_connection, txtnetbuf, rv, flags)) {
534 #ifndef NODEBUG
535                         olsr_printf(1, "(TXTINFO) Failed sending data to client!\n");
536 #endif
537                         close(ipc_connection);
538                         return - 1;
539                 }
540         }
541         return rv;
542 }