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