* committed txtinfo patch by Jo-Philipp Wich <freifunk@wwsnet.net>
[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.4 2007/04/22 19:54:31 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 #if !defined WIN32
54 #include <sys/select.h>
55 #endif
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <sys/time.h>
59 #include <time.h>
60 #include <math.h>
61 #include <stdio.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <stdarg.h>
65 #include <unistd.h>
66 #include <errno.h>
67
68 #include "olsr.h"
69 #include "olsr_types.h"
70 #include "neighbor_table.h"
71 #include "two_hop_neighbor_table.h"
72 #include "mpr_selector_set.h"
73 #include "tc_set.h"
74 #include "hna_set.h"
75 #include "mid_set.h"
76 #include "link_set.h"
77 #include "socket_parser.h"
78
79 #include "olsrd_txtinfo.h"
80 #include "olsrd_plugin.h"
81
82
83 #ifdef WIN32
84 #define close(x) closesocket(x)
85 #endif
86
87
88 static int ipc_socket;
89 static int ipc_open;
90 static int ipc_connection;
91 static int ipc_socket_up;
92
93
94 /* IPC initialization function */
95 static int
96 plugin_ipc_init(void);
97
98 static void 
99 send_info(int neighonly);
100
101 static void
102 ipc_action(int);
103
104 static void
105 ipc_print_neigh_link(void);
106
107 static void
108 ipc_print_routes(void);
109
110 static void
111 ipc_print_topology(void);
112
113 static void
114 ipc_print_hna(void);
115
116 static void
117 ipc_print_mid(void);
118
119 #define TXT_IPC_BUFSIZE 256
120 static int 
121 ipc_sendf(const char* format, ...) __attribute__((format(printf, 1, 2)));
122
123 /**
124  *Do initialization here
125  *
126  *This function is called by the my_init
127  *function in uolsrd_plugin.c
128  */
129 int
130 olsrd_plugin_init(void)
131 {
132   /* Initial IPC value */
133   ipc_open = 0;
134   ipc_socket_up = 0;
135
136   plugin_ipc_init();
137   return 1;
138 }
139
140
141 /**
142  * destructor - called at unload
143  */
144 void
145 olsr_plugin_exit(void)
146 {
147   if(ipc_open)
148     close(ipc_socket);
149 }
150
151
152
153 static int
154 plugin_ipc_init(void)
155 {
156   struct sockaddr_in sin;
157   olsr_u32_t yes = 1;
158
159   /* Init ipc socket */
160   if ((ipc_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
161     {
162 #ifndef NODEBUG
163       olsr_printf(1, "(TXTINFO) socket()=%s\n", strerror(errno));
164 #endif
165       return 0;
166     }
167   else
168     {
169       if (setsockopt(ipc_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) 
170       {
171 #ifndef NODEBUG
172         olsr_printf(1, "(TXTINFO) setsockopt()=%s\n", strerror(errno));
173 #endif
174         return 0;
175       }
176
177 #if defined __FreeBSD__ && defined SO_NOSIGPIPE
178       if (setsockopt(ipc_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) 
179       {
180         perror("SO_REUSEADDR failed");
181         return 0;
182       }
183 #endif
184
185       /* Bind the socket */
186       
187       /* complete the socket structure */
188       memset(&sin, 0, sizeof(sin));
189       sin.sin_family = AF_INET;
190       sin.sin_addr.s_addr = INADDR_ANY;
191       sin.sin_port = htons(ipc_port);
192       
193       /* bind the socket to the port number */
194       if (bind(ipc_socket, (struct sockaddr *) &sin, sizeof(sin)) == -1) 
195         {
196 #ifndef NODEBUG
197           olsr_printf(1, "(TXTINFO) bind()=%s\n", strerror(errno));
198 #endif
199           return 0;
200         }
201       
202       /* show that we are willing to listen */
203       if (listen(ipc_socket, 1) == -1) 
204         {
205 #ifndef NODEBUG
206           olsr_printf(1, "(TXTINFO) listen()=%s\n", strerror(errno));
207 #endif
208           return 0;
209         }
210         
211       /* Register with olsrd */
212       add_olsr_socket(ipc_socket, &ipc_action);
213
214 #ifndef NODEBUG
215       olsr_printf(2, "(TXTINFO) listening on port %d\n",ipc_port);
216 #endif
217       ipc_socket_up = 1;
218     }
219
220   return 1;
221 }
222
223
224 static void
225 ipc_action(int fd)
226 {
227   struct sockaddr_in pin;
228   socklen_t addrlen;
229   char *addr;  
230
231   addrlen = sizeof(struct sockaddr_in);
232
233   if(ipc_open)
234     return;
235
236   if ((ipc_connection = accept(fd, (struct sockaddr *)  &pin, &addrlen)) == -1)
237     {
238 #ifndef NODEBUG
239       olsr_printf(1, "(TXTINFO) accept()=%s\n", strerror(errno));
240 #endif
241       exit(1);
242     }
243   else
244     {
245       addr = inet_ntoa(pin.sin_addr);
246       if(ntohl(pin.sin_addr.s_addr) != ntohl(ipc_accept_ip.s_addr))
247         {
248           olsr_printf(1, "(TXTINFO) From host(%s) not allowed!\n", addr);
249           close(ipc_connection);
250           return;
251         }
252       else
253         {
254       ipc_open = 1;
255 #ifndef NODEBUG
256       olsr_printf(2, "(TXTINFO) Connect from %s\n",addr);
257 #endif
258       
259       /* purge read buffer to prevent blocking on linux*/
260       fd_set rfds;
261       FD_ZERO(&rfds);
262       FD_SET(ipc_connection, &rfds);
263       struct timeval tv = {0,0};
264       int neighonly = 0;
265       if(select(ipc_connection+1, &rfds, NULL, NULL, &tv)) {
266         char requ[128];
267         ssize_t s = recv(ipc_connection, &requ, sizeof(requ), 0);
268         if (0 < s) {
269           requ[s] = 0;
270           /* To print out neighbours only on the Freifunk Status
271            * page the normal output is somewhat lengthy. The
272            * header parsing is sufficient for standard wget.
273            */
274           neighonly = (0 != strstr(requ, "/neighbours"));
275         }
276       }
277
278       send_info(neighonly);
279           
280       close(ipc_connection);
281       ipc_open = 0;
282     }
283   }
284 }
285
286 static void
287 ipc_print_neigh_link(void)
288 {
289   struct neighbor_entry *neigh;
290   struct neighbor_2_list_entry *list_2;
291   struct link_entry *link = NULL;
292   int index, thop_cnt;
293
294         ipc_sendf("Table: Links\nLocal IP\tremote IP\tHysteresis\tLinkQuality\tlost\ttotal\tNLQ\tETX\n");
295
296   /* Link set */
297   link = link_set;
298         while(link)
299         {
300         ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%d\t%d\t%0.2f\t%0.2f\t\n",
301                         olsr_ip_to_string(&link->local_iface_addr),
302                         olsr_ip_to_string(&link->neighbor_iface_addr),
303                         link->L_link_quality, 
304                         link->loss_link_quality,
305                         link->lost_packets, 
306                         link->total_packets,
307                         link->neigh_link_quality, 
308                         (link->loss_link_quality * link->neigh_link_quality) ? 1.0 / (link->loss_link_quality * link->neigh_link_quality) : 0.0);
309                 link = link->next;
310       }
311         ipc_sendf("\nTable: Neighbors\nIP address\tSYM\tMPR\tMPRS\tWillingness\t2 Hop Neighbors\n");
312
313   /* Neighbors */
314   for(index=0;index<HASHSIZE;index++)
315     {
316       for(neigh = neighbortable[index].next;
317           neigh != &neighbortable[index];
318           neigh = neigh->next)
319         {
320                 ipc_sendf( 
321                           "%s\t%s\t%s\t%s\t%d\t", 
322                           olsr_ip_to_string(&neigh->neighbor_main_addr),
323                           (neigh->status == SYM) ? "YES" : "NO",
324                           neigh->is_mpr ? "YES" : "NO",
325                           olsr_lookup_mprs_set(&neigh->neighbor_main_addr) ? "YES" : "NO",
326                           neigh->willingness);
327
328           thop_cnt = 0;
329
330           for(list_2 = neigh->neighbor_2_list.next;
331               list_2 != &neigh->neighbor_2_list;
332               list_2 = list_2->next)
333             {
334               //size += sprintf(&buf[size], "<option>%s</option>\n", olsr_ip_to_string(&list_2->neighbor_2->neighbor_2_addr));
335               thop_cnt ++;
336             }
337                 ipc_sendf("%d\n", thop_cnt);
338         }
339     }
340
341         ipc_sendf("\n");
342 }
343
344
345 static void
346 ipc_print_routes(void)
347 {
348   int size = 0, index;
349   struct rt_entry *routes;
350
351         ipc_sendf("Table: Routes\nDestination\tGateway\tMetric\tETX\tInterface\tType\n");
352
353   /* Neighbors */
354   for(index = 0;index < HASHSIZE;index++)
355     {
356       for(routes = routingtable[index].next;
357           routes != &routingtable[index];
358           routes = routes->next)
359         {
360                 size = 0;
361                 ipc_sendf( "%s\t%s\t%d\t%.2f\t%s\tHOST\n",
362                           olsr_ip_to_string(&routes->rt_dst),
363                           olsr_ip_to_string(&routes->rt_router),
364                           routes->rt_metric,
365                           routes->rt_etx,
366                           routes->rt_if->int_name);
367         }
368     }
369
370   /* HNA */
371   for(index = 0;index < HASHSIZE;index++)
372     {
373       for(routes = hna_routes[index].next;
374           routes != &hna_routes[index];
375           routes = routes->next)
376         {
377                 ipc_sendf("%s\t%s\t%d\t%s\t\tHNA\n",
378                           olsr_ip_to_string(&routes->rt_dst),
379                           olsr_ip_to_string(&routes->rt_router),
380                           routes->rt_metric,
381                           routes->rt_if->int_name);
382         }
383     }
384
385         ipc_sendf("\n");
386
387 }
388
389 static void
390 ipc_print_topology(void)
391 {
392   olsr_u8_t index;
393   struct tc_entry *entry;
394   struct topo_dst *dst_entry;
395
396
397   ipc_sendf("Table: Topology\nDestination IP\tLast hop IP\tLQ\tILQ\tETX\n");
398
399   /* Topology */  
400   for(index=0;index<HASHSIZE;index++)
401     {
402       /* For all TC entries */
403       entry = tc_table[index].next;
404       while(entry != &tc_table[index])
405         {
406           /* For all destination entries of that TC entry */
407           dst_entry = entry->destinations.next;
408           while(dst_entry != &entry->destinations)
409             {
410                         ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%0.2f\n", 
411                               olsr_ip_to_string(&dst_entry->T_dest_addr),
412                               olsr_ip_to_string(&entry->T_last_addr), 
413                               dst_entry->link_quality,
414                               dst_entry->inverse_link_quality,
415                               (dst_entry->link_quality * dst_entry->inverse_link_quality) ? 1.0 / (dst_entry->link_quality * dst_entry->inverse_link_quality) : 0.0);
416                 
417               dst_entry = dst_entry->next;
418             }
419           entry = entry->next;
420         }
421     }
422
423   ipc_sendf("\n");
424 }
425
426 static void
427 ipc_print_hna(void)
428 {
429   int size;
430   olsr_u8_t index;
431   struct hna_entry *tmp_hna;
432   struct hna_net *tmp_net;
433
434   size = 0;
435
436   ipc_sendf("Table: HNA\nNetwork\tNetmask\tGateway\n");
437
438   /* Announced HNA entries */
439         struct hna4_entry *hna4;
440         for(hna4 = olsr_cnf->hna4_entries; hna4; hna4 = hna4->next)
441           {
442                         ipc_sendf("%s\t%s\t%s\n",
443                           olsr_ip_to_string(&hna4->net),
444                           olsr_ip_to_string(&hna4->netmask),
445                                 olsr_ip_to_string(&olsr_cnf->main_addr));
446           }
447         struct hna6_entry *hna6;
448         for(hna6 = olsr_cnf->hna6_entries; hna6; hna6 = hna6->next)
449           {
450                         ipc_sendf("%s\t%d\t%s\n",
451                           olsr_ip_to_string(&hna6->net),
452                                 hna6->prefix_len,
453                         olsr_ip_to_string(&olsr_cnf->main_addr));
454                 }
455
456   /* HNA entries */
457   for(index=0;index<HASHSIZE;index++)
458     {
459       tmp_hna = hna_set[index].next;
460       /* Check all entrys */
461       while(tmp_hna != &hna_set[index])
462         {
463           /* Check all networks */
464           tmp_net = tmp_hna->networks.next;
465               
466           while(tmp_net != &tmp_hna->networks)
467             {
468                 if (AF_INET == olsr_cnf->ip_version) {
469                         ipc_sendf("%s\t%s\t%s\n",
470                                 olsr_ip_to_string(&tmp_net->A_network_addr),
471                                 olsr_ip_to_string((union olsr_ip_addr *)&tmp_net->A_netmask.v4),
472                                 olsr_ip_to_string(&tmp_hna->A_gateway_addr));
473                 }
474                 else {
475                         ipc_sendf("%s\t%d\t%s\n",
476                                 olsr_ip_to_string(&tmp_net->A_network_addr),
477                                 tmp_net->A_netmask.v6,
478                                 olsr_ip_to_string(&tmp_hna->A_gateway_addr));
479                 }
480               tmp_net = tmp_net->next;
481             }
482               
483           tmp_hna = tmp_hna->next;
484         }
485     }
486
487         ipc_sendf("\n");
488
489 }
490
491 static void
492 ipc_print_mid(void)
493 {
494     olsr_u8_t index;
495     unsigned short is_first;
496     struct mid_entry *entry;
497     struct mid_address *alias;
498
499     ipc_sendf("Table: MID\nIP\tAliases\n");
500
501     /* MID */
502     for( index = 0; index < HASHSIZE; index++ )
503     {
504         entry = mid_set[index].next;
505
506         while( entry != &mid_set[index] )
507         {
508             ipc_sendf( olsr_ip_to_string( &entry->main_addr ) );
509             alias = entry->aliases;
510             is_first = 1;
511
512             while( alias )
513             {
514                 ipc_sendf( "%s%s",
515                     ( is_first ? "\t" : ";" ),
516                     olsr_ip_to_string( &alias->alias )
517                 );
518
519                 alias = alias->next_alias;
520                 is_first = 0;
521             }
522
523             entry = entry->next;
524             ipc_sendf("\n");
525         }
526     }
527
528         ipc_sendf("\n");
529 }
530
531
532 static void 
533 send_info(int neighonly)
534 {
535         
536         /* Print minimal http header */
537         ipc_sendf("HTTP/1.0 200 OK\n");
538         ipc_sendf("Content-type: text/plain\n\n");
539
540         /* Print tables to IPC socket */
541         
542         /* links + Neighbors */
543         ipc_print_neigh_link();
544         
545         /* topology */
546         if (!neighonly) ipc_print_topology();
547         
548         /* hna */
549         if (!neighonly) ipc_print_hna();
550
551         /* mid */
552         if (!neighonly) ipc_print_mid();
553
554         /* routes */
555         if (!neighonly) ipc_print_routes();
556 }
557
558 /*
559  * In a bigger mesh, there are probs with the fixed
560  * bufsize. Because the Content-Length header is
561  * optional, the sprintf() is changed to a more
562  * scalable solution here.
563  */
564  
565 static int 
566 ipc_sendf(const char* format, ...)
567 {
568         char txtnetbuf[TXT_IPC_BUFSIZE];
569
570         va_list arg;
571         int rv;
572 #if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __MacOSX__
573         int flags = 0;
574 #else
575         int flags = MSG_NOSIGNAL;
576 #endif
577         va_start(arg, format);
578         rv = vsnprintf(txtnetbuf, sizeof(txtnetbuf), format, arg);
579         va_end(arg);
580         if(ipc_socket_up) {
581                 if (0 > send(ipc_connection, txtnetbuf, rv, flags)) {
582 #ifndef NODEBUG
583                         olsr_printf(1, "(TXTINFO) Failed sending data to client!\n");
584 #endif
585                         close(ipc_connection);
586                         return - 1;
587                 }
588         }
589         return rv;
590 }