7bc820d86cd931e6a369eacb0a16fb94d228f95d
[olsrd.git] / lib / txtinfo / src / olsrd_txtinfo.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /*
43  * Dynamic linked library for the olsr.org olsr daemon
44  */
45 #include "olsrd_txtinfo.h"
46 #include "olsr.h"
47 #include "ipcalc.h"
48 #include "neighbor_table.h"
49 #include "two_hop_neighbor_table.h"
50 #include "mpr_selector_set.h"
51 #include "tc_set.h"
52 #include "hna_set.h"
53 #include "mid_set.h"
54 #include "routing_table.h"
55 #include "log.h"
56 #include "misc.h"
57 #include "olsr_ip_prefix_list.h"
58 #include "parser.h"
59
60 #include "common/autobuf.h"
61
62 #include <unistd.h>
63 #include <errno.h>
64
65 #ifdef WIN32
66 #undef EWOULDBLOCK
67 #undef EAGAIN
68 #define EWOULDBLOCK WSAEWOULDBLOCK
69 #define EAGAIN WSAEWOULDBLOCK
70 #undef errno
71 #define errno WSAGetLastError()
72 #undef SHUT_WR
73 #define SHUT_WR SD_SEND
74 #undef strerror
75 #define strerror(x) StrError(x)
76 #define read(fd,buf,size) recv((fd), (buf), (size), 0)
77 #define write(fd,buf,size) send((fd), (buf), (size), 0)
78 #endif
79
80
81 struct ipc_conn {
82   struct autobuf resp;
83   int respstart;
84   int requlen;
85   char requ[256];
86   char csv;
87 };
88
89 static int listen_socket = -1;
90
91
92 static void conn_destroy(struct ipc_conn *);
93
94 static void ipc_action(int, void *, unsigned int);
95
96 static void ipc_http(int, void *, unsigned int);
97
98 static void ipc_http_read(int, struct ipc_conn *);
99
100 static void ipc_http_read_dummy(int, struct ipc_conn *);
101
102 static void ipc_http_write(int, struct ipc_conn *);
103
104 static int send_info(struct ipc_conn *, int);
105
106 static int ipc_print_neigh(struct ipc_conn *);
107
108 static int ipc_print_link(struct ipc_conn *);
109
110 static int ipc_print_routes(struct ipc_conn *);
111
112 static int ipc_print_topology(struct ipc_conn *);
113
114 static int ipc_print_hna_entry(struct autobuf *, const struct olsr_ip_prefix *, const union olsr_ip_addr *, char csv);
115 static int ipc_print_hna(struct ipc_conn *);
116
117 static int ipc_print_mid(struct ipc_conn *);
118
119 static int ipc_print_stat(struct ipc_conn *);
120
121 static void update_statistics_ptr(void *);
122 static bool olsr_msg_statistics(union olsr_message *msg, struct interface *input_if, union olsr_ip_addr *from_addr);
123 static char *olsr_packet_statistics(char *packet, struct interface *interface, union olsr_ip_addr *, int *length);
124 static void update_statistics_ptr(void *data __attribute__ ((unused)));
125
126 #define isprefix(str, pre) (strncmp((str), (pre), strlen(pre)) == 0)
127
128 #define SIW_NEIGH         (1 << 0)
129 #define SIW_LINK          (1 << 1)
130 #define SIW_ROUTE         (1 << 2)
131 #define SIW_HNA           (1 << 3)
132 #define SIW_MID           (1 << 4)
133 #define SIW_TOPO          (1 << 5)
134 #define SIW_STAT          (1 << 6)
135 #define SIW_COOKIES       (1 << 7)
136 #define SIW_CSV           (1 << 8)
137 #define SIW_ALL           (SIW_NEIGH|SIW_LINK|SIW_ROUTE|SIW_HNA|SIW_MID|SIW_TOPO)
138
139 /* variables for statistics */
140 static uint32_t recv_packets[60], recv_messages[60][6];
141 static uint32_t recv_last_relevantTCs;
142 struct olsr_cookie_info *statistics_timer = NULL;
143
144 /**
145  * destructor - called at unload
146  */
147 void
148 olsr_plugin_exit(void)
149 {
150   olsr_parser_remove_function(&olsr_msg_statistics, PROMISCUOUS);
151   olsr_preprocessor_remove_function(&olsr_packet_statistics);
152   CLOSESOCKET(listen_socket);
153 }
154
155 /**
156  *Do initialization here
157  *
158  *This function is called by the my_init
159  *function in uolsrd_plugin.c
160  */
161 int
162 olsrd_plugin_init(void)
163 {
164   struct sockaddr_storage sst;
165   uint32_t yes = 1;
166   socklen_t addrlen;
167
168   statistics_timer = olsr_alloc_cookie("Txtinfo statistics timer", OLSR_COOKIE_TYPE_TIMER);
169   olsr_start_timer(1000, 0, true, &update_statistics_ptr, NULL, statistics_timer->ci_id);
170
171   /* Init ipc socket */
172   listen_socket = socket(olsr_cnf->ip_version, SOCK_STREAM, 0);
173   if (listen_socket == -1) {
174 #ifndef NODEBUG
175     OLSR_PRINTF(1, "(TXTINFO) socket()=%s\n", strerror(errno));
176 #endif
177     return 0;
178   }
179   if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)) < 0) {
180 #ifndef NODEBUG
181     OLSR_PRINTF(1, "(TXTINFO) setsockopt()=%s\n", strerror(errno));
182 #endif
183     CLOSESOCKET(listen_socket);
184     return 0;
185   }
186 #if defined __FreeBSD__ && defined SO_NOSIGPIPE
187   if (setsockopt(listen_socket, SOL_SOCKET, SO_NOSIGPIPE, (char *)&yes, sizeof(yes)) < 0) {
188     perror("SO_REUSEADDR failed");
189     CLOSESOCKET(listen_socket);
190     return 0;
191   }
192 #endif
193   /* Bind the socket */
194
195   /* complete the socket structure */
196   memset(&sst, 0, sizeof(sst));
197   if (olsr_cnf->ip_version == AF_INET) {
198     struct sockaddr_in *addr4 = (struct sockaddr_in *)&sst;
199     addr4->sin_family = AF_INET;
200     addrlen = sizeof(*addr4);
201 #ifdef SIN6_LEN
202     addr4->sin_len = addrlen;
203 #endif
204     addr4->sin_addr.s_addr = INADDR_ANY;
205     addr4->sin_port = htons(ipc_port);
206   } else {
207     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&sst;
208     addr6->sin6_family = AF_INET6;
209     addrlen = sizeof(*addr6);
210 #ifdef SIN6_LEN
211     addr6->sin6_len = addrlen;
212 #endif
213     addr6->sin6_addr = in6addr_any;
214     addr6->sin6_port = htons(ipc_port);
215   }
216
217   /* bind the socket to the port number */
218   if (bind(listen_socket, (struct sockaddr *)&sst, addrlen) == -1) {
219 #ifndef NODEBUG
220     OLSR_PRINTF(1, "(TXTINFO) bind()=%s\n", strerror(errno));
221 #endif
222     CLOSESOCKET(listen_socket);
223     return 0;
224   }
225
226   /* show that we are willing to listen */
227   if (listen(listen_socket, 1) == -1) {
228 #ifndef NODEBUG
229     OLSR_PRINTF(1, "(TXTINFO) listen()=%s\n", strerror(errno));
230 #endif
231     CLOSESOCKET(listen_socket);
232     return 0;
233   }
234
235   /* Register with olsrd */
236   add_olsr_socket(listen_socket, NULL, &ipc_action, NULL, SP_IMM_READ);
237
238 #ifndef NODEBUG
239   OLSR_PRINTF(2, "(TXTINFO) listening on port %d\n", ipc_port);
240 #endif
241
242   memset(recv_packets, 0, sizeof(recv_packets));
243   memset(recv_messages, 0, sizeof(recv_messages));
244
245   recv_last_relevantTCs = 0;
246   olsr_parser_add_function(&olsr_msg_statistics, PROMISCUOUS);
247   olsr_preprocessor_add_function(&olsr_packet_statistics);
248   return 1;
249 }
250
251
252 static void
253 update_statistics_ptr(void *data __attribute__ ((unused)))
254 {
255   uint32_t now = now_times / 1000;
256   int i;
257
258   recv_packets[now % 60] = 0;
259   for (i = 0; i < 6; i++) {
260     recv_messages[now % 60][i] = 0;
261   }
262 }
263
264 /* update message statistics */
265 static bool
266 olsr_msg_statistics(union olsr_message *msg,
267                     struct interface *input_if __attribute__ ((unused)), union olsr_ip_addr *from_addr __attribute__ ((unused)))
268 {
269   uint32_t now = now_times / 1000;
270   int idx, msgtype;
271
272   if (olsr_cnf->ip_version == AF_INET) {
273     msgtype = msg->v4.olsr_msgtype;
274   } else {
275     msgtype = msg->v6.olsr_msgtype;
276   }
277
278   switch (msgtype) {
279   case HELLO_MESSAGE:
280   case TC_MESSAGE:
281   case MID_MESSAGE:
282   case HNA_MESSAGE:
283     idx = msgtype - 1;
284     break;
285
286   case LQ_HELLO_MESSAGE:
287     idx = 0;
288     break;
289   case LQ_TC_MESSAGE:
290     idx = 1;
291     break;
292   default:
293     idx = 4;
294     break;
295   }
296
297   recv_messages[now % 60][idx]++;
298   if (recv_last_relevantTCs != getRelevantTcCount()) {
299     recv_messages[now % 60][5]++;
300     recv_last_relevantTCs ++;
301   }
302   return true;
303 }
304
305 /* update traffic statistics */
306 static char *
307 olsr_packet_statistics(char *packet __attribute__ ((unused)),
308                        struct interface *interface __attribute__ ((unused)),
309                        union olsr_ip_addr *ip __attribute__ ((unused)), int *length __attribute__ ((unused)))
310 {
311   uint32_t now = now_times / 1000;
312   recv_packets[now % 60] += *length;
313
314   return packet;
315 }
316
317 /* destroy the connection */
318 static void
319 conn_destroy(struct ipc_conn *conn)
320 {
321   abuf_free(&conn->resp);
322   free(conn);
323 }
324
325 static void
326 kill_connection(int fd, struct ipc_conn *conn)
327 {
328   remove_olsr_socket(fd, NULL, &ipc_http);
329   CLOSESOCKET(fd);
330   conn_destroy(conn);
331 }
332
333 static void
334 ipc_action(int fd, void *data __attribute__ ((unused)), unsigned int flags __attribute__ ((unused)))
335 {
336   struct ipc_conn *conn;
337   struct sockaddr_storage pin;
338 #ifndef NODEBUG
339   char addr[INET6_ADDRSTRLEN];
340 #endif
341   socklen_t addrlen = sizeof(pin);
342   int http_connection = accept(fd, (struct sockaddr *)&pin, &addrlen);
343   union olsr_ip_addr *ipaddr;
344
345   if (http_connection == -1) {
346     /* this may well happen if the other side immediately closes the connection. */
347 #ifndef NODEBUG
348     OLSR_PRINTF(1, "(TXTINFO) accept()=%s\n", strerror(errno));
349 #endif
350     return;
351   }
352
353   /* check if we want ot speak with it */
354   if (olsr_cnf->ip_version == AF_INET) {
355     struct sockaddr_in *addr4 = (struct sockaddr_in *)&pin;
356     ipaddr = (union olsr_ip_addr *)&addr4->sin_addr;
357   } else {
358     struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pin;
359     ipaddr = (union olsr_ip_addr *)&addr6->sin6_addr;
360   }
361
362   if (!ip_acl_acceptable(&allowed_nets, ipaddr, olsr_cnf->ip_version)) {
363     OLSR_PRINTF(1, "(TXTINFO) From host(%s) not allowed!\n", addr);
364     CLOSESOCKET(http_connection);
365     return;
366   }
367 #ifndef NODEBUG
368   OLSR_PRINTF(2, "(TXTINFO) Connect from %s\n", addr);
369 #endif
370
371   /* make the fd non-blocking */
372   if (set_nonblocking(http_connection) < 0) {
373     CLOSESOCKET(http_connection);
374     return;
375   }
376
377   conn = malloc(sizeof(*conn));
378   if (conn == NULL) {
379     OLSR_WARN(LOG_PLUGINS, "(TXTINFO) Out of memory!");
380     CLOSESOCKET(http_connection);
381     return;
382   }
383   conn->requlen = 0;
384   *conn->requ = '\0';
385   conn->respstart = 0;
386   abuf_init(&conn->resp, 1000);
387   add_olsr_socket(http_connection, NULL, &ipc_http, conn, SP_IMM_READ);
388 }
389
390 static void
391 ipc_http_read_dummy(int fd, struct ipc_conn *conn)
392 {
393   /* just read dummy stuff */
394   ssize_t bytes_read;
395   char dummybuf[128];
396   do {
397     bytes_read = read(fd, dummybuf, sizeof(dummybuf));
398   } while (bytes_read > 0);
399   if (bytes_read == 0) {
400     /* EOF */
401     if (conn->respstart < conn->resp.len && conn->resp.len > 0) {
402       disable_olsr_socket(fd, NULL, &ipc_http, SP_IMM_READ);
403       conn->requlen = -1;
404       return;
405     }
406   } else if (errno == EINTR || errno == EAGAIN) {
407     /* ignore interrupted sys-calls */
408     return;
409   }
410   /* we had either an error or EOF and we are completely done */
411   kill_connection(fd, conn);
412 }
413
414 static void
415 ipc_http_read(int fd, struct ipc_conn *conn)
416 {
417   int send_what = 0;
418   const char *p;
419   ssize_t bytes_read = read(fd, conn->requ + conn->requlen, sizeof(conn->requ) - conn->requlen - 1);    /* leave space for a terminating '\0' */
420   if (bytes_read < 0) {
421     if (errno == EINTR || errno == EAGAIN) {
422       return;
423     }
424     OLSR_WARN(LOG_PLUGINS, "(TXTINFO) read error: %s", strerror(errno));
425     kill_connection(fd, conn);
426     return;
427   }
428   conn->requlen += bytes_read;
429   conn->requ[conn->requlen] = '\0';
430   conn->csv = 0;
431
432   /* look if we have the necessary info. We get here somethign like "GET /path HTTP/1.0" */
433   p = strchr(conn->requ, '/');
434   if (p == NULL) {
435     /* input buffer full ? */
436     if ((sizeof(conn->requ) - conn->requlen - 1) == 0) {
437       kill_connection(fd, conn);
438       return;
439     }
440     /* we didn't get all. Wait for more data. */
441     return;
442   }
443   while (p != NULL) {
444     if (isprefix(p, "/neighbours"))
445       send_what = send_what | SIW_LINK | SIW_NEIGH;
446     else if (isprefix(p, "/neigh"))
447       send_what = send_what | SIW_NEIGH;
448     else if (isprefix(p, "/link"))
449       send_what = send_what | SIW_LINK;
450     else if (isprefix(p, "/route"))
451       send_what = send_what | SIW_ROUTE;
452     else if (isprefix(p, "/hna"))
453       send_what = send_what | SIW_HNA;
454     else if (isprefix(p, "/mid"))
455       send_what = send_what | SIW_MID;
456     else if (isprefix(p, "/topo"))
457       send_what = send_what | SIW_TOPO;
458     else if (isprefix(p, "/stat"))
459       send_what = send_what | SIW_STAT;
460     else if (isprefix(p, "/cookies"))
461       send_what = send_what | SIW_COOKIES;
462     else if (isprefix(p, "/csv"))
463       send_what = send_what | SIW_CSV;
464     p = strchr(++p, '/');
465   }
466   if (send_what == 0) {
467     send_what = SIW_ALL;
468   } else if (send_what == SIW_CSV) {
469     send_what = SIW_ALL | SIW_CSV;
470   }
471
472   if (send_info(conn, send_what) < 0) {
473     kill_connection(fd, conn);
474     return;
475   }
476   enable_olsr_socket(fd, NULL, &ipc_http, SP_IMM_WRITE);
477 }
478
479 static void
480 ipc_http_write(int fd, struct ipc_conn *conn)
481 {
482   ssize_t bytes_written = write(fd, conn->resp.buf + conn->respstart,
483                                 conn->resp.len - conn->respstart);
484   if (bytes_written < 0) {
485     if (errno == EINTR || errno == EAGAIN) {
486       return;
487     }
488     OLSR_WARN(LOG_PLUGINS, "(TXTINFO) write error: %s", strerror(errno));
489     kill_connection(fd, conn);
490     return;
491   }
492   conn->respstart += bytes_written;
493   if (conn->respstart >= conn->resp.len) {
494     /* we are done. */
495     if (conn->requlen < 0) {
496       /* we are completely done. */
497       kill_connection(fd, conn);
498     } else if (shutdown(fd, SHUT_WR) < 0) {
499       kill_connection(fd, conn);
500     } else {
501       disable_olsr_socket(fd, NULL, &ipc_http, SP_IMM_WRITE);
502     }
503   }
504 }
505
506 static void
507 ipc_http(int fd, void *data, unsigned int flags)
508 {
509   struct ipc_conn *conn = data;
510   if ((flags & SP_IMM_READ) != 0) {
511     if (conn->resp.len > 0) {
512       ipc_http_read_dummy(fd, conn);
513     } else {
514       ipc_http_read(fd, conn);
515     }
516   }
517   if ((flags & SP_IMM_WRITE) != 0) {
518     ipc_http_write(fd, conn);
519   }
520 }
521
522
523 static int
524 ipc_print_neigh(struct ipc_conn *conn)
525 {
526   struct neighbor_entry *neigh;
527
528   if (!conn->csv) {
529     if (abuf_appendf(&conn->resp, "Table: Neighbors\nIP address\tSYM\tMPR\tMPRS\tWill.\t2 Hop Neighbors\n") < 0) {
530       return -1;
531     }
532   }
533
534   /* Neighbors */
535   OLSR_FOR_ALL_NBR_ENTRIES(neigh) {
536     struct neighbor_2_list_entry *list_2;
537     struct ipaddr_str buf1;
538     int thop_cnt = 0;
539     for (list_2 = neigh->neighbor_2_list.next; list_2 != &neigh->neighbor_2_list; list_2 = list_2->next) {
540       thop_cnt++;
541     }
542     if (!conn->csv) {
543       if (abuf_appendf(&conn->resp,
544                        "%s\t%s\t%s\t%s\t%d\t%d\n",
545                        olsr_ip_to_string(&buf1, &neigh->neighbor_main_addr),
546                        neigh->status == SYM ? "YES" : "NO",
547                        neigh->is_mpr ? "YES" : "NO",
548                        olsr_lookup_mprs_set(&neigh->neighbor_main_addr) ? "YES" : "NO", neigh->willingness, thop_cnt) < 0) {
549         return -1;
550       }
551     } else {
552       if (abuf_appendf(&conn->resp,
553                        "neigh,%s,%s,%s,%s,%d,%d\n",
554                        olsr_ip_to_string(&buf1, &neigh->neighbor_main_addr),
555                        neigh->status == SYM ? "YES" : "NO",
556                        neigh->is_mpr ? "YES" : "NO",
557                        olsr_lookup_mprs_set(&neigh->neighbor_main_addr) ? "YES" : "NO", neigh->willingness, thop_cnt) < 0) {
558         return -1;
559       }
560     }
561   }
562   OLSR_FOR_ALL_NBR_ENTRIES_END(neigh);
563
564   if (!conn->csv) {
565     if (abuf_appendf(&conn->resp, "\n") < 0) {
566       return -1;
567     }
568   }
569   return 0;
570 }
571
572 static int
573 ipc_print_link(struct ipc_conn *conn)
574 {
575   struct link_entry *lnk;
576
577   if (!conn->csv) {
578     if (abuf_appendf(&conn->resp, "Table: Links\nLocal IP\tRemote IP\tLQ\tNLQ\tCost\n") < 0) {
579       return -1;
580     }
581   }
582
583   /* Link set */
584   OLSR_FOR_ALL_LINK_ENTRIES(lnk) {
585     struct ipaddr_str buf1, buf2;
586     struct lqtextbuffer lqbuffer1, lqbuffer2;
587     if (!conn->csv) {
588       if (abuf_appendf(&conn->resp,
589                        "%s\t%s\t%s\t%s\t\n",
590                        olsr_ip_to_string(&buf1, &lnk->local_iface_addr),
591                        olsr_ip_to_string(&buf2, &lnk->neighbor_iface_addr),
592                        get_link_entry_text(lnk, '\t', &lqbuffer1), get_linkcost_text(lnk->linkcost, false, &lqbuffer2)) < 0) {
593         return -1;
594       }
595     } else {
596       if (abuf_appendf(&conn->resp,
597                        "link,%s,%s,%s,%s\n",
598                        olsr_ip_to_string(&buf1, &lnk->local_iface_addr),
599                        olsr_ip_to_string(&buf2, &lnk->neighbor_iface_addr),
600                        get_link_entry_text(lnk, ',', &lqbuffer1), get_linkcost_text(lnk->linkcost, false, &lqbuffer2)) < 0) {
601         return -1;
602       }
603     }
604   }
605   OLSR_FOR_ALL_LINK_ENTRIES_END(lnk);
606
607   if (!conn->csv) {
608     if (abuf_appendf(&conn->resp, "\n") < 0) {
609       return -1;
610     }
611   }
612   return 0;
613 }
614
615 static int
616 ipc_print_routes(struct ipc_conn *conn)
617 {
618   struct rt_entry *rt;
619
620   if (!conn->csv) {
621     if (abuf_appendf(&conn->resp, "Table: Routes\nDestination\tGateway IP\tMetric\tETX\tInterface\n") < 0) {
622       return -1;
623     }
624   }
625
626   /* Walk the route table */
627   OLSR_FOR_ALL_RT_ENTRIES(rt) {
628     struct ipaddr_str buf;
629     struct ipprefix_str prefixstr;
630     struct lqtextbuffer lqbuffer;
631     if (!conn->csv) {
632       if (abuf_appendf(&conn->resp,
633                        "%s\t%s\t%u\t%s\t%s\t\n",
634                        olsr_ip_prefix_to_string(&prefixstr, &rt->rt_dst),
635                        olsr_ip_to_string(&buf, &rt->rt_best->rtp_nexthop.gateway),
636                        rt->rt_best->rtp_metric.hops,
637                        get_linkcost_text(rt->rt_best->rtp_metric.cost, true, &lqbuffer),
638                        rt->rt_best->rtp_nexthop.interface ? rt->rt_best->rtp_nexthop.interface->int_name : "[null]") < 0) {
639         return -1;
640       }
641     } else {
642       if (abuf_appendf(&conn->resp,
643                        "route,%s,%s,%u,%s,%s\n",
644                        olsr_ip_prefix_to_string(&prefixstr, &rt->rt_dst),
645                        olsr_ip_to_string(&buf, &rt->rt_best->rtp_nexthop.gateway),
646                        rt->rt_best->rtp_metric.hops,
647                        get_linkcost_text(rt->rt_best->rtp_metric.cost, true, &lqbuffer),
648                        rt->rt_best->rtp_nexthop.interface ? rt->rt_best->rtp_nexthop.interface->int_name : "[null]") < 0) {
649         return -1;
650       }
651     }
652   }
653   OLSR_FOR_ALL_RT_ENTRIES_END(rt);
654
655   if (!conn->csv) {
656     if (abuf_appendf(&conn->resp, "\n") < 0) {
657       return -1;
658     }
659   }
660   return 0;
661 }
662
663 static int
664 ipc_print_topology(struct ipc_conn *conn)
665 {
666   struct tc_entry *tc;
667
668   if (!conn->csv) {
669     if (abuf_appendf(&conn->resp, "Table: Topology\nDest. IP\tLast hop IP\tLQ\tNLQ\tCost\n") < 0) {
670       return -1;
671     }
672   }
673
674   /* Topology */
675   OLSR_FOR_ALL_TC_ENTRIES(tc) {
676     struct tc_edge_entry *tc_edge;
677     OLSR_FOR_ALL_TC_EDGE_ENTRIES(tc, tc_edge) {
678       if (tc_edge->edge_inv) {
679         struct ipaddr_str dstbuf, addrbuf;
680         struct lqtextbuffer lqbuffer1, lqbuffer2;
681         if (!conn->csv) {
682           if (abuf_appendf(&conn->resp,
683                            "%s\t%s\t%s\t%s\n",
684                            olsr_ip_to_string(&dstbuf, &tc_edge->T_dest_addr),
685                            olsr_ip_to_string(&addrbuf, &tc->addr),
686                            get_tc_edge_entry_text(tc_edge, '\t', &lqbuffer1),
687                            get_linkcost_text(tc_edge->cost, false, &lqbuffer2)) < 0) {
688             return -1;
689           }
690         } else {
691           if (abuf_appendf(&conn->resp,
692                            "topo,%s,%s,%s,%s\n",
693                            olsr_ip_to_string(&dstbuf, &tc_edge->T_dest_addr),
694                            olsr_ip_to_string(&addrbuf, &tc->addr),
695                            get_tc_edge_entry_text(tc_edge, ',', &lqbuffer1),
696                            get_linkcost_text(tc_edge->cost, false, &lqbuffer2)) < 0) {
697             return -1;
698           }
699         }
700       }
701     }
702     OLSR_FOR_ALL_TC_EDGE_ENTRIES_END(tc, tc_edge);
703   }
704   OLSR_FOR_ALL_TC_ENTRIES_END(tc);
705
706   if (!conn->csv) {
707     if (abuf_appendf(&conn->resp, "\n") < 0) {
708       return -1;
709     }
710   }
711   return 0;
712 }
713
714 static int
715 ipc_print_hna_entry(struct autobuf *autobuf, const struct olsr_ip_prefix *hna_prefix, const union olsr_ip_addr *ipaddr, char csv)
716 {
717   struct ipaddr_str mainaddrbuf;
718   struct ipprefix_str addrbuf;
719   if (!csv) {
720     return abuf_appendf(autobuf,
721                         "%s\t%s\n", olsr_ip_prefix_to_string(&addrbuf, hna_prefix), olsr_ip_to_string(&mainaddrbuf, ipaddr));
722   } else {
723     return abuf_appendf(autobuf,
724                         "hna,%s,%s\n", olsr_ip_prefix_to_string(&addrbuf, hna_prefix), olsr_ip_to_string(&mainaddrbuf, ipaddr));
725   }
726 }
727
728 static int
729 ipc_print_hna(struct ipc_conn *conn)
730 {
731   const struct ip_prefix_entry *hna;
732   struct tc_entry *tc;
733
734   if (!conn->csv) {
735     if (abuf_appendf(&conn->resp, "Table: HNA\nDestination\tGateway\n") < 0) {
736       return -1;
737     }
738   }
739
740   /* Announced HNA entries */
741   OLSR_FOR_ALL_IPPREFIX_ENTRIES(&olsr_cnf->hna_entries, hna) {
742     if (ipc_print_hna_entry(&conn->resp, &hna->net, &olsr_cnf->router_id, conn->csv) < 0) {
743       return -1;
744     }
745   }
746   OLSR_FOR_ALL_IPPREFIX_ENTRIES_END()
747
748     /* HNA entries */
749     OLSR_FOR_ALL_TC_ENTRIES(tc) {
750     struct hna_net *tmp_net;
751     /* Check all networks */
752     OLSR_FOR_ALL_TC_HNA_ENTRIES(tc, tmp_net) {
753       if (ipc_print_hna_entry(&conn->resp, &tmp_net->hna_prefix, &tc->addr, conn->csv) < 0) {
754         return -1;
755       }
756     }
757     OLSR_FOR_ALL_TC_HNA_ENTRIES_END(tc, tmp_net);
758   }
759   OLSR_FOR_ALL_TC_ENTRIES_END(tc);
760
761   if (!conn->csv) {
762     if (abuf_appendf(&conn->resp, "\n") < 0) {
763       return -1;
764     }
765   }
766   return 0;
767 }
768
769 static int
770 ipc_print_mid(struct ipc_conn *conn)
771 {
772   struct tc_entry *tc;
773
774   if (!conn->csv) {
775     if (abuf_appendf(&conn->resp, "Table: MID\nIP address\tAliases\n") < 0) {
776       return -1;
777     }
778   }
779
780   /* MID root is the TC entry */
781   OLSR_FOR_ALL_TC_ENTRIES(tc) {
782     struct ipaddr_str buf;
783     struct mid_entry *alias;
784     char sep = '\t';
785     if (conn->csv) {
786       sep = ',';
787     }
788
789     if (!conn->csv) {
790       if (abuf_puts(&conn->resp, olsr_ip_to_string(&buf, &tc->addr)) < 0) {
791         return -1;
792       }
793     } else {
794       if (abuf_appendf(&conn->resp, "mid,%s", olsr_ip_to_string(&buf, &tc->addr)) < 0) {
795         return -1;
796       }
797     }
798
799     OLSR_FOR_ALL_TC_MID_ENTRIES(tc, alias) {
800       if (abuf_appendf(&conn->resp, "%c%s", sep, olsr_ip_to_string(&buf, &alias->mid_alias_addr)) < 0) {
801         return -1;
802       }
803       if (!conn->csv) {
804         sep = ';';
805       } else {
806         sep = ',';
807       }
808     }
809     OLSR_FOR_ALL_TC_MID_ENTRIES_END(tc, alias);
810     if (abuf_appendf(&conn->resp, "\n") < 0) {
811       return -1;
812     }
813   }
814   OLSR_FOR_ALL_TC_ENTRIES_END(tc);
815   if (!conn->csv) {
816     if (abuf_appendf(&conn->resp, "\n") < 0) {
817       return -1;
818     }
819   }
820   return 0;
821 }
822
823 static int
824 ipc_print_stat(struct ipc_conn *conn)
825 {
826   static const char *names[] = { "HELLO", "TC", "MID", "HNA", "Other", "Rel.TCs" };
827
828   uint32_t msgs[6], traffic, i, j;
829   uint32_t slot = (now_times / 1000 + 59) % 60;
830
831   if (!conn->csv) {
832     if (abuf_appendf(&conn->resp, "Table: Statistics (without duplicates)\nType\tlast seconds\t\t\t\tlast min.\taverage\n") < 0) {
833       return -1;
834     }
835   }
836
837   for (j = 0; j < 6; j++) {
838     msgs[j] = 0;
839     for (i = 0; i < 60; i++) {
840       msgs[j] += recv_messages[i][j];
841     }
842   }
843
844   traffic = 0;
845   for (i = 0; i < 60; i++) {
846     traffic += recv_packets[i];
847   }
848
849   for (i = 0; i < 6; i++) {
850     if (!conn->csv) {
851       if (abuf_appendf(&conn->resp, "%s\t%u\t%u\t%u\t%u\t%u\t%u\t\t%u\n", names[i],
852                        recv_messages[(slot) % 60][i],
853                        recv_messages[(slot + 59) % 60][i],
854                        recv_messages[(slot + 58) % 60][i],
855                        recv_messages[(slot + 57) % 60][i], recv_messages[(slot + 56) % 60][i], msgs[i], msgs[i] / 60) < 0) {
856         return -1;
857       }
858     } else {
859       if (abuf_appendf(&conn->resp, "stat,%s,%u,%u,%u,%u,%u,%u,%u\n", names[i],
860                        recv_messages[(slot) % 60][i],
861                        recv_messages[(slot + 59) % 60][i],
862                        recv_messages[(slot + 58) % 60][i],
863                        recv_messages[(slot + 57) % 60][i], recv_messages[(slot + 56) % 60][i], msgs[i], msgs[i] / 60) < 0) {
864         return -1;
865       }
866     }
867   }
868   if (!conn->csv) {
869     if (abuf_appendf(&conn->resp, "\nTraffic: %8u bytes/s\t%u bytes/minute\taverage %u bytes/s\n",
870                      recv_packets[(slot) % 60], traffic, traffic / 60) < 0) {
871       return -1;
872     }
873   } else {
874     if (abuf_appendf(&conn->resp, "stat,Traffic,%u,%u,%u\n", recv_packets[(slot) % 60], traffic, traffic / 60) < 0) {
875       return -1;
876     }
877   }
878
879   if (!conn->csv) {
880     if (abuf_appendf(&conn->resp, "\n") < 0) {
881       return -1;
882     }
883   }
884   return 0;
885 }
886
887 static int
888 ipc_print_cookies(struct ipc_conn *conn)
889 {
890   int i;
891
892   if (!conn->csv) {
893     if (abuf_appendf(&conn->resp, "Memory cookies:\n") < 0) {
894       return -1;
895     }
896   }
897
898   for (i = 1; i < COOKIE_ID_MAX; i++) {
899     struct olsr_cookie_info *c = olsr_cookie_get(i);
900     if (c == NULL || c->ci_type != OLSR_COOKIE_TYPE_MEMORY) {
901       continue;
902     }
903     if (!conn->csv) {
904       if (abuf_appendf(&conn->resp, "%-25s ", c->ci_name == NULL ? "Unknown" : c->ci_name) < 0) {
905         return -1;
906       }
907       if (abuf_appendf(&conn->resp, "(MEMORY) size: %lu usage: %u freelist: %u\n",
908                        (unsigned long)c->ci_size, c->ci_usage, c->ci_free_list_usage) < 0) {
909         return -1;
910       }
911     } else {
912       if (abuf_appendf(&conn->resp, "mem_cookie,%s,%lu,%u,%u\n", c->ci_name == NULL ? "Unknown" : c->ci_name,
913                        (unsigned long)c->ci_size, c->ci_usage, c->ci_free_list_usage) < 0) {
914         return -1;
915       }
916     }
917   }
918
919   if (!conn->csv) {
920     if (abuf_appendf(&conn->resp, "\nTimer cookies:\n") < 0) {
921       return -1;
922     }
923   }
924
925   for (i = 1; i < COOKIE_ID_MAX; i++) {
926     struct olsr_cookie_info *c = olsr_cookie_get(i);
927     if (c == NULL || c->ci_type != OLSR_COOKIE_TYPE_TIMER) {
928       continue;
929     }
930     if (!conn->csv) {
931       if (abuf_appendf(&conn->resp, "%-25s ", c->ci_name == NULL ? "Unknown" : c->ci_name) < 0) {
932         return -1;
933       }
934       if (abuf_appendf(&conn->resp, "(TIMER) usage: %u changes: %u\n", c->ci_usage, c->ci_changes) < 0) {
935         return -1;
936       }
937     } else {
938       if (abuf_appendf(&conn->resp, "tmr_cookie,%s,%u,%u\n", c->ci_name == NULL ? "Unknown" : c->ci_name,
939                        c->ci_usage, c->ci_changes) < 0) {
940         return -1;
941       }
942     }
943   }
944
945   if (!conn->csv) {
946     if (abuf_appendf(&conn->resp, "\n") < 0) {
947       return -1;
948     }
949   }
950   return 0;
951 }
952
953 static int
954 send_info(struct ipc_conn *conn, int send_what)
955 {
956   int rv;
957
958   /* comma separated values output format */
959   if ((send_what & SIW_CSV) != 0) {
960     conn->csv = 1;
961   }
962
963   /* Print minimal http header */
964   if (!conn->csv) {
965     if (abuf_appendf(&conn->resp, "HTTP/1.0 200 OK\n" "Content-type: text/plain\n\n") < 0) {
966       return -1;
967     }
968   }
969
970   /* Print tables to IPC socket */
971
972   rv = 0;
973   /* links + Neighbors */
974   if ((send_what & SIW_LINK) != 0 && ipc_print_link(conn) < 0) {
975     rv = -1;
976   }
977   if ((send_what & SIW_NEIGH) != 0 && ipc_print_neigh(conn) < 0) {
978     rv = -1;
979   }
980   /* topology */
981   if ((send_what & SIW_TOPO) != 0) {
982     rv = ipc_print_topology(conn);
983   }
984   /* hna */
985   if ((send_what & SIW_HNA) != 0) {
986     rv = ipc_print_hna(conn);
987   }
988   /* mid */
989   if ((send_what & SIW_MID) != 0) {
990     rv = ipc_print_mid(conn);
991   }
992   /* routes */
993   if ((send_what & SIW_ROUTE) != 0) {
994     rv = ipc_print_routes(conn);
995   }
996   /* statistics */
997   if ((send_what & SIW_STAT) != 0) {
998     rv = ipc_print_stat(conn);
999   }
1000   /* cookies */
1001   if ((send_what & SIW_COOKIES) != 0) {
1002     rv = ipc_print_cookies(conn);
1003   }
1004   return rv;
1005 }
1006
1007 /*
1008  * Local Variables:
1009  * mode: c
1010  * style: linux
1011  * c-basic-offset: 4
1012  * indent-tabs-mode: nil
1013  * End:
1014  */