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