Convert debuginfo to new plugin api
[olsrd.git] / lib / debuginfo / src / olsrd_debuginfo.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
46 #include <stdlib.h>
47
48 #include "olsr.h"
49 #include "ipcalc.h"
50 #include "neighbor_table.h"
51 #include "tc_set.h"
52 #include "hna_set.h"
53 #include "mid_set.h"
54 #include "routing_table.h"
55 #include "olsr_logging.h"
56 #include "olsr_ip_prefix_list.h"
57 #include "parser.h"
58 #include "olsr_comport_txt.h"
59 #include "olsrd_debuginfo.h"
60 #include "olsr_types.h"
61 #include "defs.h"
62
63 #include "common/autobuf.h"
64
65 #define PLUGIN_DESCR    "OLSRD debuginfo plugin"
66 #define PLUGIN_AUTHOR   "Henning Rogge"
67
68 struct debuginfo_cmd {
69   const char *name;
70   olsr_txthandler handler;
71   struct olsr_txtcommand *cmd;
72 };
73
74 static bool debuginfo_init(void);
75 static bool debuginfo_enable(void);
76 static bool debuginfo_exit(void);
77
78
79 static enum olsr_txtcommand_result debuginfo_msgstat(struct comport_connection *con,
80     const char *cmd, const char *param);
81 static enum olsr_txtcommand_result debuginfo_pktstat(struct comport_connection *con,
82     const char *cmd, const char *param);
83 static enum olsr_txtcommand_result debuginfo_cookies(struct comport_connection *con,
84     const char *cmd, const char *param);
85
86 static void update_statistics_ptr(void *);
87 static void olsr_msg_statistics(struct olsr_message *,
88     struct interface *, union olsr_ip_addr *, enum duplicate_status);
89 static uint8_t *olsr_packet_statistics(uint8_t *binary,
90     struct interface *interface, union olsr_ip_addr *ip, int *length);
91
92 static void update_statistics_ptr(void *data __attribute__ ((unused)));
93
94 /* plugin configuration */
95 static struct ip_acl allowed_nets;
96 static uint32_t traffic_interval, traffic_slots, current_slot;
97
98 /* plugin parameters */
99 static const struct olsrd_plugin_parameters plugin_parameters[] = {
100   {.name = IP_ACL_ACCEPT_PARAP,.set_plugin_parameter = &ip_acl_add_plugin_accept,.data = &allowed_nets},
101   {.name = IP_ACL_REJECT_PARAM,.set_plugin_parameter = &ip_acl_add_plugin_reject,.data = &allowed_nets},
102   {.name = IP_ACL_CHECKFIRST_PARAM,.set_plugin_parameter = &ip_acl_add_plugin_checkFirst,.data = &allowed_nets},
103   {.name = IP_ACL_DEFAULTPOLICY_PARAM,.set_plugin_parameter = &ip_acl_add_plugin_defaultPolicy,.data = &allowed_nets},
104   {.name = "stat_interval", .set_plugin_parameter = &set_plugin_int, .data = &traffic_interval},
105   {.name = "stat_slots", .set_plugin_parameter = &set_plugin_int, .data = &traffic_slots},
106 };
107
108 OLSR_PLUGIN6(plugin_parameters) {
109   .descr = PLUGIN_DESCR,
110   .author = PLUGIN_AUTHOR,
111   .init = debuginfo_init,
112   .enable = debuginfo_enable,
113   .exit = debuginfo_exit,
114   .deactivate = false
115 };
116
117 /* command callbacks and names */
118 static struct debuginfo_cmd commands[] = {
119     {"msgstat", &debuginfo_msgstat, NULL},
120     {"pktstat", &debuginfo_pktstat, NULL},
121     {"cookies", &debuginfo_cookies, NULL}
122 };
123
124 /* variables for statistics */
125 static struct avl_tree stat_msg_tree, stat_pkt_tree;
126
127 static struct debug_msgtraffic_count total_msg_traffic;
128 static struct debug_pkttraffic_count total_pkt_traffic;
129
130 static struct olsr_cookie_info *statistics_msg_mem = NULL;
131 static struct olsr_cookie_info *statistics_pkt_mem = NULL;
132
133 static struct olsr_timer_info *statistics_timer = NULL;
134
135 static union olsr_ip_addr total_ip_addr;
136
137
138 /**
139  *Constructor
140  */
141 static bool
142 debuginfo_init(void)
143 {
144   ip_acl_init(&allowed_nets);
145
146   traffic_interval = 5; /* seconds */
147   traffic_slots = 12;      /* number of 5000 second slots */
148   current_slot = 0;
149
150   memset(&total_ip_addr, 255, sizeof(total_ip_addr));
151   return false;
152 }
153
154 /**
155  *Destructor
156  */
157 static bool
158 debuginfo_exit(void)
159 {
160   size_t i;
161
162   for (i=0; i<ARRAYSIZE(commands); i++) {
163     olsr_com_remove_normal_txtcommand(commands[i].cmd);
164   }
165   olsr_parser_remove_function(&olsr_msg_statistics);
166   olsr_preprocessor_remove_function(&olsr_packet_statistics);
167   ip_acl_flush(&allowed_nets);
168   return false;
169 }
170
171 static bool
172 debuginfo_enable(void)
173 {
174   size_t i;
175
176   /* always allow localhost */
177   if (olsr_cnf->ip_version == AF_INET) {
178     union olsr_ip_addr ip;
179
180     ip.v4.s_addr = ntohl(INADDR_LOOPBACK);
181     ip_acl_add(&allowed_nets, &ip, 32, false);
182   } else {
183     ip_acl_add(&allowed_nets, (const union olsr_ip_addr *)&in6addr_loopback, 128, false);
184     ip_acl_add(&allowed_nets, (const union olsr_ip_addr *)&in6addr_v4mapped_loopback, 128, false);
185   }
186
187   for (i=0; i<ARRAYSIZE(commands); i++) {
188     commands[i].cmd = olsr_com_add_normal_txtcommand(commands[i].name, commands[i].handler);
189     commands[i].cmd->acl = &allowed_nets;
190   }
191
192   statistics_timer = olsr_alloc_timerinfo("debuginfo timer", &update_statistics_ptr, true);
193   olsr_start_timer(traffic_interval * 1000, 0, NULL, statistics_timer);
194
195   statistics_msg_mem = olsr_create_memcookie("debuginfo msgstat",
196       sizeof(struct debug_msgtraffic) + sizeof(struct debug_msgtraffic_count) * traffic_slots);
197
198   statistics_pkt_mem = olsr_create_memcookie("debuginfo pktstat",
199       sizeof(struct debug_pkttraffic) + sizeof(struct debug_pkttraffic_count) * traffic_slots);
200
201   memset(&total_msg_traffic, 0, sizeof(total_msg_traffic));
202   memset(&total_pkt_traffic, 0, sizeof(total_pkt_traffic));
203   avl_init(&stat_msg_tree, avl_comp_default, false, NULL);
204   avl_init(&stat_pkt_tree, avl_comp_default, false, NULL);
205
206   olsr_parser_add_function(&olsr_msg_statistics, PROMISCUOUS);
207   olsr_preprocessor_add_function(&olsr_packet_statistics);
208   return false;
209 }
210
211 static struct debug_msgtraffic *get_msgtraffic_entry(union olsr_ip_addr *ip) {
212   struct debug_msgtraffic *tr;
213   tr = (struct debug_msgtraffic *) avl_find(&stat_msg_tree, ip);
214   if (tr == NULL) {
215     tr = olsr_cookie_malloc(statistics_msg_mem);
216
217     memcpy(&tr->ip, ip, sizeof(union olsr_ip_addr));
218     tr->node.key = &tr->ip;
219
220     avl_insert(&stat_msg_tree, &tr->node);
221   }
222   return tr;
223 }
224
225 static struct debug_pkttraffic *get_pkttraffic_entry(union olsr_ip_addr *ip, struct interface *in) {
226   struct debug_pkttraffic *tr;
227   tr = (struct debug_pkttraffic *) avl_find(&stat_pkt_tree, ip);
228   if (tr == NULL) {
229     tr = olsr_cookie_malloc(statistics_pkt_mem);
230
231     memcpy(&tr->ip, ip, sizeof(union olsr_ip_addr));
232     tr->node.key = &tr->ip;
233
234     tr->int_name = strdup(in ? in->int_name : "---");
235
236     avl_insert(&stat_pkt_tree, &tr->node);
237   }
238   return tr;
239 }
240
241 static void
242 update_statistics_ptr(void *data __attribute__ ((unused)))
243 {
244   struct debug_msgtraffic *msg;
245   struct debug_pkttraffic *pkt;
246   struct list_iterator iterator;
247   uint32_t last_slot, i;
248
249   last_slot = current_slot;
250   current_slot++;
251   if (current_slot == traffic_slots) {
252     current_slot = 0;
253   }
254
255   /* move data from "current" template to slot array */
256   OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES(msg, iterator) {
257     /* subtract old values from node count and total count */
258     for (i=0; i<DTR_MSG_COUNT; i++) {
259       msg->total.data[i] -= msg->traffic[current_slot].data[i];
260     }
261
262     /* copy new traffic into slot */
263     msg->traffic[current_slot] = msg->current;
264
265     /* add new values to node count and total count */
266     for (i=0; i<DTR_MSG_COUNT; i++) {
267       msg->total.data[i] += msg->current.data[i];
268     }
269
270     /* erase new traffic */
271     memset(&msg->current, 0, sizeof(msg->current));
272
273     if (msg->total.data[DTR_MESSAGES] == 0) {
274       /* no traffic left, cleanup ! */
275
276       avl_delete(&stat_msg_tree, &msg->node);
277       olsr_cookie_free(statistics_msg_mem, msg);
278     }
279   }
280
281   OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES(pkt, iterator) {
282     /* subtract old values from node count and total count */
283     for (i=0; i<DTR_PKT_COUNT; i++) {
284       pkt->total.data[i] -= pkt->traffic[current_slot].data[i];
285     }
286
287     /* copy new traffic into slot */
288     pkt->traffic[current_slot] = pkt->current;
289
290     /* add new values to node count and total count */
291     for (i=0; i<DTR_PKT_COUNT; i++) {
292       pkt->total.data[i] += pkt->current.data[i];
293     }
294
295     /* erase new traffic */
296     memset(&pkt->current, 0, sizeof(pkt->current));
297
298     if (pkt->total.data[DTR_PACKETS] == 0) {
299       /* no traffic left, cleanup ! */
300
301       avl_delete(&stat_pkt_tree, &pkt->node);
302       free(pkt->int_name);
303       olsr_cookie_free(statistics_pkt_mem, pkt);
304     }
305   }
306 }
307
308 /* update message statistics */
309 static void
310 olsr_msg_statistics(struct olsr_message *msg,
311     struct interface *input_if __attribute__ ((unused)),
312     union olsr_ip_addr *from_addr __attribute__ ((unused)), enum duplicate_status status  __attribute__ ((unused)))
313 {
314   enum debug_msgtraffic_type type;
315   struct debug_msgtraffic *tr;
316 #if !defined REMOVE_LOG_DEBUG
317   struct ipaddr_str buf;
318 #endif
319
320   switch (msg->type) {
321   case HELLO_MESSAGE:
322     type = DTR_HELLO;
323     break;
324   case TC_MESSAGE:
325     type = DTR_TC;
326     break;
327   case MID_MESSAGE:
328     type = DTR_MID;
329     break;
330   case HNA_MESSAGE:
331     type = DTR_HNA;
332     break;
333   case LQ_HELLO_MESSAGE:
334     type = DTR_HELLO;
335     break;
336   case LQ_TC_MESSAGE:
337     type = DTR_TC;
338     break;
339   default:
340     type = DTR_OTHER;
341     break;
342   }
343
344   /* input data for specific node */
345   tr = get_msgtraffic_entry(&msg->originator);
346   tr->current.data[type]++;
347   tr->current.data[DTR_MESSAGES]++;
348   tr->current.data[DTR_MSG_TRAFFIC] += msg->size;
349
350   OLSR_DEBUG(LOG_PLUGINS, "Added message type %d to statistics of %s: %d\n",
351       type, olsr_ip_to_string(&buf, &tr->ip), tr->current.data[type]);
352
353   /* input data for total traffic handling */
354   tr = get_msgtraffic_entry(&total_ip_addr);
355   tr->current.data[type]++;
356   tr->current.data[DTR_MESSAGES]++;
357   tr->current.data[DTR_MSG_TRAFFIC] += msg->size;
358 }
359
360 /* update traffic statistics */
361 static uint8_t *
362 olsr_packet_statistics(uint8_t *binary, struct interface *interface, union olsr_ip_addr *ip, int *length)
363 {
364   struct debug_pkttraffic *tr;
365   tr = get_pkttraffic_entry(ip, interface);
366   tr->current.data[DTR_PACK_TRAFFIC] += *length;
367   tr->current.data[DTR_PACKETS] ++;
368
369   tr = get_pkttraffic_entry(&total_ip_addr, NULL);
370   tr->current.data[DTR_PACK_TRAFFIC] += *length;
371   tr->current.data[DTR_PACKETS] ++;
372   return binary;
373 }
374
375 static const char *debuginfo_print_trafficip(struct ipaddr_str *buf, union olsr_ip_addr *ip) {
376   static const char *total = "Total";
377   if (olsr_ipcmp(ip, &total_ip_addr) == 0) {
378     return total;
379   }
380   return olsr_ip_to_string(buf, ip);
381 }
382
383 static bool debuginfo_print_msgstat(struct autobuf *buf, union olsr_ip_addr *ip, struct debug_msgtraffic_count *cnt) {
384   struct ipaddr_str strbuf;
385   return abuf_appendf(buf, "%-*s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t\n",
386       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, debuginfo_print_trafficip(&strbuf, ip),
387       cnt->data[DTR_HELLO],
388       cnt->data[DTR_TC],
389       cnt->data[DTR_MID],
390       cnt->data[DTR_HNA],
391       cnt->data[DTR_OTHER],
392       cnt->data[DTR_MESSAGES],
393       cnt->data[DTR_MSG_TRAFFIC]) < 0;
394 }
395
396 static enum olsr_txtcommand_result
397 debuginfo_msgstat(struct comport_connection *con,
398     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused)))
399 {
400   struct debug_msgtraffic *tr;
401   struct list_iterator iterator;
402
403   if (abuf_appendf(&con->out, "Slot size: %d seconds\tSlot count: %d\n", traffic_interval, traffic_slots) < 0) {
404     return ABUF_ERROR;
405   }
406   if (abuf_appendf(&con->out,
407       "Table: Statistics (without duplicates)\n%-*s\tHello\tTC\tMID\tHNA\tOther\tTotal\tBytes\n",
408       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, "IP"
409       ) < 0) {
410     return ABUF_ERROR;
411   }
412
413   if (param == NULL || strcasecmp(param, "node") == 0) {
414     OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES(tr, iterator) {
415       if (debuginfo_print_msgstat(&con->out, &tr->ip, &tr->traffic[current_slot])) {
416         return ABUF_ERROR;
417       }
418     }
419   }
420   else {
421     uint32_t mult = 1, divisor = 1;
422     struct debug_msgtraffic_count cnt;
423     int i;
424
425     if (strcasecmp(param, "total") == 0) {
426       divisor = 1;
427     }
428     else if (strcasecmp(param, "average") == 0) {
429       divisor = traffic_slots;
430     }
431     else if (strcasecmp(param, "avgsec") == 0) {
432       divisor = traffic_slots * traffic_interval;
433       mult = 1;
434     }
435     else if (strcasecmp(param, "avgmin") == 0) {
436       divisor = traffic_slots * traffic_interval;
437       mult = 60;
438     }
439     else if (strcasecmp(param, "avghour") == 0) {
440       divisor = traffic_slots * traffic_interval;
441       mult = 3600;
442     }
443     else {
444       abuf_appendf(&con->out, "Error, unknown parameter %s for msgstat\n", param);
445       return CONTINUE;
446     }
447
448     OLSR_FOR_ALL_MSGTRAFFIC_ENTRIES(tr, iterator) {
449       for (i=0; i<DTR_MSG_COUNT; i++) {
450         cnt.data[i] = (tr->total.data[i] * mult) / divisor;
451       }
452       if (debuginfo_print_msgstat(&con->out, &tr->ip, &cnt)) {
453         return ABUF_ERROR;
454       }
455     }
456   }
457
458   return CONTINUE;
459 }
460
461 static bool debuginfo_print_pktstat(struct autobuf *buf, union olsr_ip_addr *ip, char *int_name, struct debug_pkttraffic_count *cnt) {
462   struct ipaddr_str strbuf;
463   return abuf_appendf(buf, "%-*s\t%s\t%d\t%d\n",
464       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, debuginfo_print_trafficip(&strbuf, ip),
465       int_name,
466       cnt->data[DTR_PACKETS],
467       cnt->data[DTR_PACK_TRAFFIC]) < 0;
468 }
469
470 static enum olsr_txtcommand_result
471 debuginfo_pktstat(struct comport_connection *con,
472     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused)))
473 {
474   struct debug_pkttraffic *tr;
475   struct list_iterator iterator;
476
477   if (abuf_appendf(&con->out, "Slot size: %d seconds\tSlot count: %d\n", traffic_interval, traffic_slots) < 0) {
478     return ABUF_ERROR;
479   }
480   if (abuf_appendf(&con->out,
481       "Table: Statistics (without duplicates)\n%-*s\tInterf.\tPackets\tBytes\n",
482       olsr_cnf->ip_version == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN, "IP"
483       ) < 0) {
484     return ABUF_ERROR;
485   }
486
487   if (param == NULL || strcasecmp(param, "node") == 0) {
488     OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES(tr, iterator) {
489       if (debuginfo_print_pktstat(&con->out, &tr->ip, tr->int_name, &tr->traffic[current_slot])) {
490         return ABUF_ERROR;
491       }
492     }
493   }
494   else {
495     uint32_t mult = 1, divisor = 1;
496     struct debug_pkttraffic_count cnt;
497     int i;
498
499     if (strcasecmp(param, "total") == 0) {
500       divisor = 1;
501     }
502     else if (strcasecmp(param, "average") == 0) {
503       divisor = traffic_slots;
504     }
505     else if (strcasecmp(param, "avgsec") == 0) {
506       divisor = traffic_slots * traffic_interval;
507       mult = 1;
508     }
509     else if (strcasecmp(param, "avgmin") == 0) {
510       divisor = traffic_slots * traffic_interval;
511       mult = 60;
512     }
513     else if (strcasecmp(param, "avghour") == 0) {
514       divisor = traffic_slots * traffic_interval;
515       mult = 3600;
516     }
517     else {
518       abuf_appendf(&con->out, "Error, unknown parameter %s for msgstat\n", param);
519       return CONTINUE;
520     }
521
522     OLSR_FOR_ALL_PKTTRAFFIC_ENTRIES(tr, iterator) {
523       for (i=0; i<DTR_PKT_COUNT; i++) {
524         cnt.data[i] = (tr->total.data[i] * mult) / divisor;
525       }
526       if (debuginfo_print_pktstat(&con->out, &tr->ip, tr->int_name, &cnt)) {
527         return ABUF_ERROR;
528       }
529     }
530   }
531
532   return CONTINUE;
533 }
534
535 static INLINE bool debuginfo_print_cookies_mem(struct autobuf *buf) {
536   struct olsr_cookie_info *c;
537   struct list_iterator iterator;
538
539   OLSR_FOR_ALL_COOKIES(c, iterator) {
540     if (abuf_appendf(buf, "%-25s (MEMORY) size: %lu usage: %u freelist: %u\n",
541         c->ci_name, (unsigned long)c->ci_size, c->ci_usage, c->ci_free_list_usage) < 0) {
542       return true;
543     }
544   }
545   return false;
546 }
547
548 static INLINE bool debuginfo_print_cookies_timer(struct autobuf *buf) {
549   struct olsr_timer_info *t;
550   struct list_iterator iterator;
551
552   OLSR_FOR_ALL_TIMERS(t, iterator) {
553     if (abuf_appendf(buf, "%-25s (TIMER) usage: %u changes: %u\n",
554         t->name, t->usage, t->changes) < 0) {
555       return true;
556     }
557   }
558   return false;
559 }
560
561 static enum olsr_txtcommand_result
562 debuginfo_cookies(struct comport_connection *con,
563     const char *cmd __attribute__ ((unused)), const char *param __attribute__ ((unused)))
564 {
565   if (abuf_puts(&con->out, "Memory cookies:\n") < 0) {
566     return ABUF_ERROR;
567   }
568
569   if (debuginfo_print_cookies_mem(&con->out)) {
570     return ABUF_ERROR;
571   }
572
573   if (abuf_puts(&con->out, "\nTimer cookies:\n") < 0) {
574     return ABUF_ERROR;
575   }
576
577   if (debuginfo_print_cookies_timer(&con->out)) {
578     return ABUF_ERROR;
579   }
580   return CONTINUE;
581 }
582
583 /*
584  * Local Variables:
585  * mode: c
586  * style: linux
587  * c-basic-offset: 4
588  * indent-tabs-mode: nil
589  * End:
590  */