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