Bug Fixes (of stupid and forgotten bugs) from Sven-Ola Tuecke <sven-ola@gmx.de> from
[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.16 2007/11/29 00:10:17 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 #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     link = link_set;
310     while(link) {
311         ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%d\t%d\t%0.2f\t%0.2f\t\n",
312                    olsr_ip_to_string(&buf1, &link->local_iface_addr),
313                    olsr_ip_to_string(&buf2, &link->neighbor_iface_addr),
314                    link->L_link_quality, 
315                    link->loss_link_quality,
316                    link->lost_packets, 
317                    link->total_packets,
318                    link->neigh_link_quality, 
319                    olsr_calc_link_etx(link));
320         link = link->next;
321     }
322     ipc_sendf("\nTable: Neighbors\nIP address\tSYM\tMPR\tMPRS\tWillingness\t2 Hop Neighbors\n");
323
324     /* Neighbors */
325     for(index = 0; index < HASHSIZE; index++) {
326         for(neigh = neighbortable[index].next;
327             neigh != &neighbortable[index];
328             neigh = neigh->next) {
329             ipc_sendf("%s\t%s\t%s\t%s\t%d\t", 
330                       olsr_ip_to_string(&buf1, &neigh->neighbor_main_addr),
331                       (neigh->status == SYM) ? "YES" : "NO",
332                       neigh->is_mpr ? "YES" : "NO",
333                       olsr_lookup_mprs_set(&neigh->neighbor_main_addr) ? "YES" : "NO",
334                       neigh->willingness);
335             thop_cnt = 0;
336
337             for(list_2 = neigh->neighbor_2_list.next;
338                 list_2 != &neigh->neighbor_2_list;
339                 list_2 = list_2->next)
340                 {
341                     //size += sprintf(&buf[size], "<option>%s</option>\n", olsr_ip_to_string(&buf1, &list_2->neighbor_2->neighbor_2_addr));
342                     thop_cnt ++;
343                 }
344             ipc_sendf("%d\n", thop_cnt);
345         }
346     }
347     ipc_sendf("\n");
348 }
349
350 static void ipc_print_routes(void)
351 {
352     struct ipaddr_str buf1, buf2;
353     struct rt_entry *rt;
354     struct avl_node *rt_tree_node;
355
356     ipc_sendf("Table: Routes\nDestination\tGateway\tMetric\tETX\tInterface\n");
357
358     /* Walk the route table */
359     for (rt_tree_node = avl_walk_first(&routingtree);
360          rt_tree_node;
361          rt_tree_node = avl_walk_next(rt_tree_node)) {
362
363         rt = rt_tree_node->data;
364
365         ipc_sendf( "%s/%d\t%s\t%d\t%.3f\t%s\t\n",
366                    olsr_ip_to_string(&buf1, &rt->rt_dst.prefix),
367                    rt->rt_dst.prefix_len,
368                    olsr_ip_to_string(&buf2, &rt->rt_best->rtp_nexthop.gateway),
369                    rt->rt_best->rtp_metric.hops,
370                    rt->rt_best->rtp_metric.etx,
371                    if_ifwithindex_name(rt->rt_best->rtp_nexthop.iif_index));
372     }
373     ipc_sendf("\n");
374
375 }
376
377 static void ipc_print_topology(void)
378 {
379     struct tc_entry *tc;
380     
381     ipc_sendf("Table: Topology\nDestination IP\tLast hop IP\tLQ\tILQ\tETX\n");
382
383     /* Topology */  
384     OLSR_FOR_ALL_TC_ENTRIES(tc) {
385         struct tc_edge_entry *tc_edge;
386         OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
387             struct ipaddr_str dstbuf, addrbuf;
388             ipc_sendf( "%s\t%s\t%0.2f\t%0.2f\t%0.2f\n", 
389                        olsr_ip_to_string(&dstbuf, &tc_edge->T_dest_addr),
390                        olsr_ip_to_string(&addrbuf, &tc->addr), 
391                        tc_edge->link_quality,
392                        tc_edge->inverse_link_quality,
393                        olsr_calc_tc_etx(tc_edge));
394
395         } OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
396     } OLSR_FOR_ALL_TC_ENTRIES_END(tc);
397
398     ipc_sendf("\n");
399 }
400
401 static void ipc_print_hna(void)
402 {
403     int size;
404     int index;
405     struct local_hna_entry *hna;
406
407     size = 0;
408
409     ipc_sendf("Table: HNA\nNetwork\tNetmask\tGateway\n");
410
411     /* Announced HNA entries */
412     if (olsr_cnf->ip_version == AF_INET) {
413         for(hna = olsr_cnf->hna_entries; hna != NULL; hna = hna->next) {
414             struct ipaddr_str addrbuf, maskbuf, mainaddrbuf;
415             union olsr_ip_addr netmask;
416             olsr_prefix_to_netmask(&netmask, hna->net.prefix_len);
417             ipc_sendf("%s\t%s\t%s\n",
418                       olsr_ip_to_string(&addrbuf, &hna->net.prefix),
419                       olsr_ip_to_string(&maskbuf, &netmask),
420                       olsr_ip_to_string(&mainaddrbuf, &olsr_cnf->main_addr));
421         }
422     } else {
423         for(hna = olsr_cnf->hna_entries; hna != NULL; hna = hna->next) {
424             struct ipaddr_str addrbuf, mainaddrbuf;
425             ipc_sendf("%s\t%d\t%s\n",
426                       olsr_ip_to_string(&addrbuf, &hna->net.prefix),
427                       hna->net.prefix_len,
428                       olsr_ip_to_string(&mainaddrbuf, &olsr_cnf->main_addr));
429         }
430     }
431
432     /* HNA entries */
433     for(index = 0; index < HASHSIZE; index++) {
434         struct hna_entry *tmp_hna;
435         /* Check all entrys */
436         for (tmp_hna = hna_set[index].next; tmp_hna != &hna_set[index]; tmp_hna = tmp_hna->next) {
437             /* Check all networks */
438             struct hna_net *tmp_net;
439             for (tmp_net = tmp_hna->networks.next; tmp_net != &tmp_hna->networks; tmp_net = tmp_net->next) {
440                 if (olsr_cnf->ip_version == AF_INET) {
441                     struct ipaddr_str addrbuf, maskbuf, mainaddrbuf;
442                     const union olsr_ip_addr netmask = { .v4 = { .s_addr = tmp_net->A_netmask.v4 } };
443                     ipc_sendf("%s\t%s\t%s\n",
444                               olsr_ip_to_string(&addrbuf, &tmp_net->A_network_addr),
445                               olsr_ip_to_string(&maskbuf, &netmask),
446                               olsr_ip_to_string(&mainaddrbuf, &tmp_hna->A_gateway_addr));
447                 } else {
448                     struct ipaddr_str addrbuf, mainaddrbuf;
449                     ipc_sendf("%s\t%d\t%s\n",
450                               olsr_ip_to_string(&addrbuf, &tmp_net->A_network_addr),
451                               tmp_net->A_netmask.v6,
452                               olsr_ip_to_string(&mainaddrbuf, &tmp_hna->A_gateway_addr));
453                 }
454             }            
455         }
456     }
457     ipc_sendf("\n");
458 }
459
460 static void ipc_print_mid(void)
461 {
462     int index;
463     unsigned short is_first;
464     struct mid_entry *entry;
465     struct mid_address *alias;
466
467     ipc_sendf("Table: MID\nIP\tAliases\n");
468
469     /* MID */
470     for(index = 0; index < HASHSIZE; index++) {
471         entry = mid_set[index].next;
472         
473         while( entry != &mid_set[index] ) {
474             struct ipaddr_str buf;
475             ipc_sendf( olsr_ip_to_string(&buf,  &entry->main_addr ) );
476             alias = entry->aliases;
477             is_first = 1;
478
479             while( alias ) {
480                 ipc_sendf( "%s%s",
481                            ( is_first ? "\t" : ";" ),
482                            olsr_ip_to_string(&buf,  &alias->alias )
483                            );
484
485                 alias = alias->next_alias;
486                 is_first = 0;
487             }
488             entry = entry->next;
489             ipc_sendf("\n");
490         }
491     }
492     ipc_sendf("\n");
493 }
494
495
496 static void  send_info(int neighonly)
497 {
498     /* Print minimal http header */
499     ipc_sendf("HTTP/1.0 200 OK\n");
500     ipc_sendf("Content-type: text/plain\n\n");
501
502     /* Print tables to IPC socket */
503         
504     /* links + Neighbors */
505     ipc_print_neigh_link();
506         
507     /* topology */
508     if (!neighonly) ipc_print_topology();
509         
510     /* hna */
511     if (!neighonly) ipc_print_hna();
512
513     /* mid */
514     if (!neighonly) ipc_print_mid();
515
516     /* routes */
517     if (!neighonly) ipc_print_routes();
518 }
519
520 /*
521  * In a bigger mesh, there are probs with the fixed
522  * bufsize. Because the Content-Length header is
523  * optional, the sprintf() is changed to a more
524  * scalable solution here.
525  */
526  
527 static int  ipc_sendf(const char* format, ...)
528 {
529     char txtnetbuf[TXT_IPC_BUFSIZE];
530
531     va_list arg;
532     int rv;
533 #if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __MacOSX__
534     int flags = 0;
535 #else
536     int flags = MSG_NOSIGNAL;
537 #endif
538     va_start(arg, format);
539     rv = vsnprintf(txtnetbuf, sizeof(txtnetbuf), format, arg);
540     va_end(arg);
541     if(ipc_socket_up) {
542         if (0 > send(ipc_connection, txtnetbuf, rv, flags)) {
543 #ifndef NODEBUG
544             olsr_printf(1, "(TXTINFO) Failed sending data to client!\n");
545 #endif
546             close(ipc_connection);
547             return - 1;
548         }
549     }
550     return rv;
551 }
552
553 /*
554  * Local Variables:
555  * mode: c
556  * style: linux
557  * c-basic-offset: 4
558  * indent-tabs-mode: nil
559  * End:
560  */