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