94d7855cec40a52e49c4dd3fdc940a97e1c4fb92
[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     OLSR_FOR_ALL_LINK_ENTRIES(link) {
360         ipc_sendf( "%s\t%s\t%s\t%s\t%d\t%d\t%s\t%s\t\n",
361                    olsr_ip_to_string(&buf1, &link->local_iface_addr),
362                    olsr_ip_to_string(&buf2, &link->neighbor_iface_addr),
363                    fpmtoa(link->L_link_quality), 
364                    fpmtoa(link->loss_link_quality),
365                    link->lost_packets, 
366                    link->total_packets,
367                    fpmtoa(link->neigh_link_quality), 
368                    etxtoa(olsr_calc_link_etx(link)));
369     } OLSR_FOR_ALL_LINK_ENTRIES_END(link);
370
371     ipc_sendf("\n");
372 }
373
374 static void ipc_print_routes(void)
375 {
376     struct ipaddr_str buf1, buf2;
377     struct rt_entry *rt;
378     struct avl_node *rt_tree_node;
379
380     ipc_sendf("Table: Routes\nDestination\tGateway\tMetric\tETX\tInterface\n");
381
382     /* Walk the route table */
383     for (rt_tree_node = avl_walk_first(&routingtree);
384          rt_tree_node;
385          rt_tree_node = avl_walk_next(rt_tree_node)) {
386
387         rt = rt_tree_node->data;
388
389         ipc_sendf( "%s/%d\t%s\t%d\t%s\t%s\t\n",
390                    olsr_ip_to_string(&buf1, &rt->rt_dst.prefix),
391                    rt->rt_dst.prefix_len,
392                    olsr_ip_to_string(&buf2, &rt->rt_best->rtp_nexthop.gateway),
393                    rt->rt_best->rtp_metric.hops,
394                    etxtoa(rt->rt_best->rtp_metric.etx),
395                    if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
396     }
397     ipc_sendf("\n");
398
399 }
400
401 static void ipc_print_topology(void)
402 {
403     struct tc_entry *tc;
404     
405     ipc_sendf("Table: Topology\nDestination IP\tLast hop IP\tLQ\tILQ\tETX\n");
406
407     /* Topology */  
408     OLSR_FOR_ALL_TC_ENTRIES(tc) {
409         struct tc_edge_entry *tc_edge;
410         OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
411             struct ipaddr_str dstbuf, addrbuf;
412             ipc_sendf( "%s\t%s\t%s\t%s\t%s\n", 
413                        olsr_ip_to_string(&dstbuf, &tc_edge->T_dest_addr),
414                        olsr_ip_to_string(&addrbuf, &tc->addr), 
415                        fpmtoa(tc_edge->link_quality),
416                        fpmtoa(tc_edge->inverse_link_quality),
417                        etxtoa(olsr_calc_tc_etx(tc_edge)));
418
419         } OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
420     } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
421
422     ipc_sendf("\n");
423 }
424
425 static void ipc_print_hna(void)
426 {
427     int size;
428     int index;
429     struct ip_prefix_list *hna;
430
431     size = 0;
432
433     ipc_sendf("Table: HNA\nNetwork\tNetmask\tGateway\n");
434
435     /* Announced HNA entries */
436     if (olsr_cnf->ip_version == AF_INET) {
437         for(hna = olsr_cnf->hna_entries; hna != NULL; hna = hna->next) {
438             struct ipaddr_str addrbuf, mainaddrbuf;
439             ipc_sendf("%s\t%d\t%s\n",
440                       olsr_ip_to_string(&addrbuf, &hna->net.prefix),
441                       hna->net.prefix_len,
442                       olsr_ip_to_string(&mainaddrbuf, &olsr_cnf->main_addr));
443         }
444     } else {
445         for(hna = olsr_cnf->hna_entries; hna != NULL; hna = hna->next) {
446             struct ipaddr_str addrbuf, mainaddrbuf;
447             ipc_sendf("%s\t%d\t%s\n",
448                       olsr_ip_to_string(&addrbuf, &hna->net.prefix),
449                       hna->net.prefix_len,
450                       olsr_ip_to_string(&mainaddrbuf, &olsr_cnf->main_addr));
451         }
452     }
453
454     /* HNA entries */
455     for(index = 0; index < HASHSIZE; index++) {
456         struct hna_entry *tmp_hna;
457         /* Check all entrys */
458         for (tmp_hna = hna_set[index].next; tmp_hna != &hna_set[index]; tmp_hna = tmp_hna->next) {
459             /* Check all networks */
460             struct hna_net *tmp_net;
461             for (tmp_net = tmp_hna->networks.next; tmp_net != &tmp_hna->networks; tmp_net = tmp_net->next) {
462                 struct ipaddr_str addrbuf, mainaddrbuf;
463                 ipc_sendf("%s\t%d\t%s\n",
464                           olsr_ip_to_string(&addrbuf, &tmp_net->A_network_addr),
465                           tmp_net->prefixlen,
466                           olsr_ip_to_string(&mainaddrbuf, &tmp_hna->A_gateway_addr));
467             }
468         }
469     }
470     ipc_sendf("\n");
471 }
472
473 static void ipc_print_mid(void)
474 {
475     int index;
476     unsigned short is_first;
477     struct mid_entry *entry;
478     struct mid_address *alias;
479
480     ipc_sendf("Table: MID\nIP\tAliases\n");
481
482     /* MID */
483     for(index = 0; index < HASHSIZE; index++) {
484         entry = mid_set[index].next;
485         
486         while( entry != &mid_set[index] ) {
487             struct ipaddr_str buf;
488             ipc_sendf( olsr_ip_to_string(&buf,  &entry->main_addr ) );
489             alias = entry->aliases;
490             is_first = 1;
491
492             while( alias ) {
493                 ipc_sendf( "%s%s",
494                            ( is_first ? "\t" : ";" ),
495                            olsr_ip_to_string(&buf,  &alias->alias )
496                            );
497
498                 alias = alias->next_alias;
499                 is_first = 0;
500             }
501             entry = entry->next;
502             ipc_sendf("\n");
503         }
504     }
505     ipc_sendf("\n");
506 }
507
508
509 static void  send_info(int send_what)
510 {
511     /* Print minimal http header */
512     ipc_sendf("HTTP/1.0 200 OK\n");
513     ipc_sendf("Content-type: text/plain\n\n");
514
515     /* Print tables to IPC socket */
516         
517     /* links + Neighbors */
518     if ((send_what==SIW_ALL)||(send_what==SIW_NEIGHLINK)||(send_what==SIW_LINK)) ipc_print_link();
519
520     if ((send_what==SIW_ALL)||(send_what==SIW_NEIGHLINK)||(send_what==SIW_NEIGH)) ipc_print_neigh();
521         
522     /* topology */
523     if ((send_what==SIW_ALL)||(send_what==SIW_TOPO)) ipc_print_topology();
524         
525     /* hna */
526     if ((send_what==SIW_ALL)||(send_what==SIW_HNA)) ipc_print_hna();
527
528     /* mid */
529     if ((send_what==SIW_ALL)||(send_what==SIW_MID)) ipc_print_mid();
530
531     /* routes */
532     if ((send_what==SIW_ALL)||(send_what==SIW_ROUTE)) ipc_print_routes();
533 }
534
535 /*
536  * In a bigger mesh, there are probs with the fixed
537  * bufsize. Because the Content-Length header is
538  * optional, the sprintf() is changed to a more
539  * scalable solution here.
540  */
541  
542 static int  ipc_sendf(const char* format, ...)
543 {
544     char txtnetbuf[TXT_IPC_BUFSIZE];
545
546     va_list arg;
547     int rv;
548 #if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __MacOSX__
549     int flags = 0;
550 #else
551     int flags = MSG_NOSIGNAL;
552 #endif
553     va_start(arg, format);
554     rv = vsnprintf(txtnetbuf, sizeof(txtnetbuf), format, arg);
555     va_end(arg);
556     if(ipc_socket_up) {
557         if (0 > send(ipc_connection, txtnetbuf, rv, flags)) {
558 #ifndef NODEBUG
559             olsr_printf(1, "(TXTINFO) Failed sending data to client!\n");
560 #endif
561             close(ipc_connection);
562             return - 1;
563         }
564     }
565     return rv;
566 }
567
568 /*
569  * Local Variables:
570  * mode: c
571  * style: linux
572  * c-basic-offset: 4
573  * indent-tabs-mode: nil
574  * End:
575  */