remove CVS ID markers
[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     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 ip_prefix_list *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                 struct ipaddr_str addrbuf, mainaddrbuf;
441                 ipc_sendf("%s\t%d\t%s\n",
442                           olsr_ip_to_string(&addrbuf, &tmp_net->A_network_addr),
443                           tmp_net->prefixlen,
444                           olsr_ip_to_string(&mainaddrbuf, &tmp_hna->A_gateway_addr));
445             }
446         }
447     }
448     ipc_sendf("\n");
449 }
450
451 static void ipc_print_mid(void)
452 {
453     int index;
454     unsigned short is_first;
455     struct mid_entry *entry;
456     struct mid_address *alias;
457
458     ipc_sendf("Table: MID\nIP\tAliases\n");
459
460     /* MID */
461     for(index = 0; index < HASHSIZE; index++) {
462         entry = mid_set[index].next;
463         
464         while( entry != &mid_set[index] ) {
465             struct ipaddr_str buf;
466             ipc_sendf( olsr_ip_to_string(&buf,  &entry->main_addr ) );
467             alias = entry->aliases;
468             is_first = 1;
469
470             while( alias ) {
471                 ipc_sendf( "%s%s",
472                            ( is_first ? "\t" : ";" ),
473                            olsr_ip_to_string(&buf,  &alias->alias )
474                            );
475
476                 alias = alias->next_alias;
477                 is_first = 0;
478             }
479             entry = entry->next;
480             ipc_sendf("\n");
481         }
482     }
483     ipc_sendf("\n");
484 }
485
486
487 static void  send_info(int neighonly)
488 {
489     /* Print minimal http header */
490     ipc_sendf("HTTP/1.0 200 OK\n");
491     ipc_sendf("Content-type: text/plain\n\n");
492
493     /* Print tables to IPC socket */
494         
495     /* links + Neighbors */
496     ipc_print_neigh_link();
497         
498     /* topology */
499     if (!neighonly) ipc_print_topology();
500         
501     /* hna */
502     if (!neighonly) ipc_print_hna();
503
504     /* mid */
505     if (!neighonly) ipc_print_mid();
506
507     /* routes */
508     if (!neighonly) ipc_print_routes();
509 }
510
511 /*
512  * In a bigger mesh, there are probs with the fixed
513  * bufsize. Because the Content-Length header is
514  * optional, the sprintf() is changed to a more
515  * scalable solution here.
516  */
517  
518 static int  ipc_sendf(const char* format, ...)
519 {
520     char txtnetbuf[TXT_IPC_BUFSIZE];
521
522     va_list arg;
523     int rv;
524 #if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __MacOSX__
525     int flags = 0;
526 #else
527     int flags = MSG_NOSIGNAL;
528 #endif
529     va_start(arg, format);
530     rv = vsnprintf(txtnetbuf, sizeof(txtnetbuf), format, arg);
531     va_end(arg);
532     if(ipc_socket_up) {
533         if (0 > send(ipc_connection, txtnetbuf, rv, flags)) {
534 #ifndef NODEBUG
535             olsr_printf(1, "(TXTINFO) Failed sending data to client!\n");
536 #endif
537             close(ipc_connection);
538             return - 1;
539         }
540     }
541     return rv;
542 }
543
544 /*
545  * Local Variables:
546  * mode: c
547  * style: linux
548  * c-basic-offset: 4
549  * indent-tabs-mode: nil
550  * End:
551  */