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