Code

Merge branch 'master' into ff/dns
[collectd.git] / src / dns.c
1 /**
2  * collectd - src/dns.c
3  * Copyright (C) 2006  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27 #include "utils_debug.h"
29 #if HAVE_SYS_POLL_H
30 # include <sys/poll.h>
31 #endif
33 #define MODULE_NAME "dns"
35 #if HAVE_LIBPCAP
36 # define NAMED_HAVE_CONFIG 1
37 #else
38 # define NAMED_HAVE_CONFIG 0
39 #endif
41 #if HAVE_LIBPCAP
42 # include "utils_dns.h"
43 # define NAMED_HAVE_READ 1
44 #else
45 # define NAMED_HAVE_READ 0
46 #endif
48 struct counter_list_s
49 {
50         unsigned int key;
51         unsigned int value;
52         struct counter_list_s *next;
53 };
54 typedef struct counter_list_s counter_list_t;
56 static char *traffic_file   = "dns/dns_traffic.rrd";
57 static char *qtype_file   = "dns/qtype-%s.rrd";
58 static char *opcode_file  = "dns/opcode-%s.rrd";
59 static char *rcode_file   = "dns/rcode-%s.rrd";
61 static char *traffic_ds_def[] =
62 {
63         /* Limit to 1GBit/s */
64         "DS:queries:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
65         "DS:responses:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
66         NULL
67 };
68 static int traffic_ds_num = 2;
70 static char *qtype_ds_def[] =
71 {
72         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
73         NULL
74 };
75 static int qtype_ds_num = 1;
77 static char *opcode_ds_def[] =
78 {
79         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
80         NULL
81 };
82 static int opcode_ds_num = 1;
84 static char *rcode_ds_def[] =
85 {
86         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
87         NULL
88 };
89 static int rcode_ds_num = 1;
91 #if NAMED_HAVE_CONFIG
92 #if HAVE_LIBPCAP
93 static char *config_keys[] =
94 {
95         "Interface",
96         "IgnoreSource",
97         NULL
98 };
99 static int config_keys_num = 2;
100 #endif /* HAVE_LIBPCAP */
101 #endif /* NAMED_HAVE_CONFIG */
103 #if HAVE_LIBPCAP
104 #define PCAP_SNAPLEN 1460
105 static char   *pcap_device = NULL;
106 static int     pipe_fd = -1;
108 static unsigned int    tr_queries;
109 static unsigned int    tr_responses;
110 static counter_list_t *qtype_list;
111 static counter_list_t *opcode_list;
112 static counter_list_t *rcode_list;
113 #endif
115 static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
117         counter_list_t *entry;
119         DBG ("counter_list_search (list = %p, key = %u)",
120                         (void *) *list, key);
122         for (entry = *list; entry != NULL; entry = entry->next)
123                 if (entry->key == key)
124                         break;
126         DBG ("return (%p)", (void *) entry);
127         return (entry);
130 static counter_list_t *counter_list_create (counter_list_t **list,
131                 unsigned int key, unsigned int value)
133         counter_list_t *entry;
135         DBG ("counter_list_create (list = %p, key = %u, value = %u)",
136                         (void *) *list, key, value);
138         entry = (counter_list_t *) malloc (sizeof (counter_list_t));
139         if (entry == NULL)
140                 return (NULL);
142         memset (entry, 0, sizeof (counter_list_t));
143         entry->key = key;
144         entry->value = value;
146         if (*list == NULL)
147         {
148                 *list = entry;
149         }
150         else
151         {
152                 counter_list_t *last;
154                 last = *list;
155                 while (last->next != NULL)
156                         last = last->next;
158                 last->next = entry;
159         }
161         DBG ("return (%p)", (void *) entry);
162         return (entry);
165 static void counter_list_add (counter_list_t **list,
166                 unsigned int key, unsigned int increment)
168         counter_list_t *entry;
170         DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
171                         (void *) *list, key, increment);
173         entry = counter_list_search (list, key);
175         if (entry != NULL)
176         {
177                 entry->value += increment;
178         }
179         else
180         {
181                 counter_list_create (list, key, increment);
182         }
183         DBG ("return ()");
186 static int counter_list_send (counter_list_t *list, int fd)
188         counter_list_t *cl;
189         unsigned int values[2 * T_MAX];
190         unsigned int values_num;
192         if (fd < 0)
193                 return (-1);
195         values_num = 0;
197         for (cl = list;
198                         (cl != NULL) && (values_num < T_MAX);
199                         cl = cl->next)
200         {
201                 values[2 * values_num] = cl->key;
202                 values[(2 * values_num) + 1] = cl->value;
203                 values_num++;
204         }
206         DBG ("swrite (fd = %i, values_num = %i)", fd, values_num);
207         if (swrite (fd, (const void *) &values_num, sizeof (values_num)) != 0)
208         {
209                 DBG ("Writing to fd failed: %s", strerror (errno));
210                 syslog (LOG_ERR, "dns plugin: Writing to fd failed: %s",
211                                 strerror (errno));
212                 return (-1);
213         }
215         if (values_num == 0)
216                 return (0);
218         DBG ("swrite (fd = %i, values = %p, size = %i)",
219                         fd, (void *) values, (int) (sizeof (int) * values_num));
220         if (swrite (fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
221         {
222                 DBG ("Writing to pipe failed: %s", strerror (errno));
223                 syslog (LOG_ERR, "dns plugin: Writing to pipe failed: %s",
224                                 strerror (errno));
225                 return (-1);
226         }
228         return (values_num);
230 #if NAMED_HAVE_CONFIG
231 static int dns_config (char *key, char *value)
233 #if HAVE_LIBPCAP
234         if (strcasecmp (key, "Interface") == 0)
235         {
236                 if (pcap_device != NULL)
237                         free (pcap_device);
238                 if ((pcap_device = strdup (value)) == NULL)
239                         return (1);
240         }
241         else if (strcasecmp (key, "IgnoreSource") == 0)
242         {
243                 if (value != NULL)
244                         ignore_list_add_name (value);
245         }
246         else
247         {
248                 return (-1);
249         }
251         return (0);
252 #endif /* HAVE_LIBPCAP */
254 #endif /* NAMED_HAVE_CONFIG */
256 static void dns_child_callback (const rfc1035_header_t *dns)
258         if (dns->qr == 0)
259         {
260                 /* This is a query */
261                 tr_queries += dns->length;
262                 counter_list_add (&qtype_list,  dns->qtype,  1);
263         }
264         else
265         {
266                 /* This is a reply */
267                 tr_responses += dns->length;
268                 counter_list_add (&rcode_list,  dns->rcode,  1);
269         }
271         /* FIXME: Are queries, replies or both interesting? */
272         counter_list_add (&opcode_list, dns->opcode, 1);
275 static void dns_child_loop (void)
277         pcap_t *pcap_obj;
278         char    pcap_error[PCAP_ERRBUF_SIZE];
279         struct  bpf_program fp;
281         struct pollfd poll_fds[2];
282         int status;
284         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
285         DBG ("Creating PCAP object..");
286         pcap_obj = pcap_open_live (pcap_device,
287                         PCAP_SNAPLEN,
288                         0 /* Not promiscuous */,
289                         0 /* no read timeout */,
290                         pcap_error);
291         if (pcap_obj == NULL)
292         {
293                 syslog (LOG_ERR, "dns plugin: Opening interface `%s' failed: %s",
294                                 (pcap_device != NULL) ? pcap_device : "any",
295                                 pcap_error);
296                 close (pipe_fd);
297                 pipe_fd = -1;
298                 return;
299         }
301         memset (&fp, 0, sizeof (fp));
302         if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
303         {
304                 DBG ("pcap_compile failed");
305                 syslog (LOG_ERR, "dns plugin: pcap_compile failed");
306                 close (pipe_fd);
307                 pipe_fd = -1;
308                 return;
309         }
310         if (pcap_setfilter (pcap_obj, &fp) < 0)
311         {
312                 DBG ("pcap_setfilter failed");
313                 syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
314                 close (pipe_fd);
315                 pipe_fd = -1;
316                 return;
317         }
319         DBG ("PCAP object created.");
321         dnstop_set_pcap_obj (pcap_obj);
322         dnstop_set_callback (dns_child_callback);
324         /* Set up pipe end */
325         poll_fds[0].fd = pipe_fd;
326         poll_fds[0].events = POLLOUT;
328         /* Set up pcap device */
329         poll_fds[1].fd = pcap_fileno (pcap_obj);
330         poll_fds[1].events = POLLIN | POLLPRI;
332         while (pipe_fd > 0)
333         {
334                 DBG ("poll (...)");
335                 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
337                 if (status < 0)
338                 {
339                         syslog (LOG_ERR, "dns plugin: poll(2) failed: %s",
340                                         strerror (errno));
341                         break;
342                 }
344                 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
345                 {
346                         DBG ("Pipe closed. Exiting.");
347                         syslog (LOG_NOTICE, "dns plugin: Pipe closed. Exiting.");
348                         break;
349                 }
350                 else if (poll_fds[0].revents & POLLOUT)
351                 {
352                         DBG ("Sending data..");
354                         DBG ("swrite (pipe_fd = %i, tr_queries = %i)", pipe_fd, tr_queries);
355                         if (swrite (pipe_fd, (const void *) &tr_queries, sizeof (tr_queries)) != 0)
356                         {
357                                 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
358                                 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
359                                                 strerror (errno));
360                                 return;
361                         }
363                         DBG ("swrite (pipe_fd = %i, tr_responses = %i)", pipe_fd, tr_responses);
364                         if (swrite (pipe_fd, (const void *) &tr_responses, sizeof (tr_responses)) != 0)
365                         {
366                                 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
367                                 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
368                                                 strerror (errno));
369                                 return;
370                         }
372                         counter_list_send (qtype_list, pipe_fd);
373                         counter_list_send (opcode_list, pipe_fd);
374                         counter_list_send (rcode_list, pipe_fd);
375                 }
377                 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
378                 {
379                         DBG ("pcap-device closed. Exiting.");
380                         syslog (LOG_ERR, "dns plugin: pcap-device closed. Exiting.");
381                         break;
382                 }
383                 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
384                 {
385                         status = pcap_dispatch (pcap_obj,
386                                         10 /* Only handle 10 packets at a time */,
387                                         handle_pcap /* callback */,
388                                         NULL /* Whatever this means.. */);
389                         if (status < 0)
390                         {
391                                 DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj));
392                                 syslog (LOG_ERR, "dns plugin: pcap_dispatch failed: %s",
393                                                 pcap_geterr (pcap_obj));
394                                 break;
395                         }
396                 }
397         } /* while (42) */
399         DBG ("child is exiting");
401         close (pipe_fd);
402         pipe_fd = -1;
403         pcap_close (pcap_obj);
404 } /* static void dns_child_loop (void) */
406 static void dns_init (void)
408 #if HAVE_LIBPCAP
409         int pipe_fds[2];
410         pid_t pid_child;
412         tr_queries   = 0;
413         tr_responses = 0;
415         if (pipe (pipe_fds) != 0)
416         {
417                 syslog (LOG_ERR, "dns plugin: pipe(2) failed: %s",
418                                 strerror (errno));
419                 return;
420         }
422         /* Fork off child */
423         pid_child = fork ();
424         if (pid_child < 0)
425         {
426                 syslog (LOG_ERR, "dns plugin: fork(2) failed: %s",
427                                 strerror (errno));
428                 close (pipe_fds[0]);
429                 close (pipe_fds[1]);
430                 return;
431         }
432         else if (pid_child != 0)
433         {
434                 /* parent: Close the writing end, keep the reading end. */
435                 pipe_fd = pipe_fds[0];
436                 close (pipe_fds[1]);
437         }
438         else
439         {
440                 /* child: Close the reading end, keep the writing end. */
441                 pipe_fd = pipe_fds[1];
442                 close (pipe_fds[0]);
444                 dns_child_loop ();
445                 exit (0);
446         }
448         /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */
449 #endif
452 static void traffic_write (char *host, char *inst, char *val)
454         rrd_update_file (host, traffic_file, val,
455                         traffic_ds_def, traffic_ds_num);
458 static void qtype_write (char *host, char *inst, char *val)
460         char file[512];
461         int status;
463         status = snprintf (file, 512, qtype_file, inst);
464         if (status < 1)
465                 return;
466         else if (status >= 512)
467                 return;
469         rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
472 static void rcode_write (char *host, char *inst, char *val)
474         char file[512];
475         int status;
477         status = snprintf (file, 512, rcode_file, inst);
478         if (status < 1)
479                 return;
480         else if (status >= 512)
481                 return;
483         rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
486 static void opcode_write (char *host, char *inst, char *val)
488         char file[512];
489         int status;
491         status = snprintf (file, 512, opcode_file, inst);
492         if (status < 1)
493                 return;
494         else if (status >= 512)
495                 return;
497         rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
500 static void traffic_submit (unsigned int queries, unsigned int replies)
502         char buffer[64];
503         int  status;
505         status = snprintf (buffer, 64, "N:%u:%u", queries, replies);
506         if ((status < 1) || (status >= 64))
507                 return;
509         plugin_submit ("dns_traffic", "-", buffer);
512 static void qtype_submit (int qtype, unsigned int counter)
514         char inst[32];
515         char buffer[32];
516         int  status;
518         strncpy (inst, qtype_str (qtype), 32);
519         inst[31] = '\0';
521         status = snprintf (buffer, 32, "N:%u", counter);
522         if ((status < 1) || (status >= 32))
523                 return;
525         plugin_submit ("dns_qtype", inst, buffer);
528 static void rcode_submit (int rcode, unsigned int counter)
530         char inst[32];
531         char buffer[32];
532         int  status;
534         strncpy (inst, rcode_str (rcode), 32);
535         inst[31] = '\0';
537         status = snprintf (buffer, 32, "N:%u", counter);
538         if ((status < 1) || (status >= 32))
539                 return;
541         plugin_submit ("dns_rcode", inst, buffer);
544 static void opcode_submit (int opcode, unsigned int counter)
546         char inst[32];
547         char buffer[32];
548         int  status;
550         strncpy (inst, opcode_str (opcode), 32);
551         inst[31] = '\0';
553         status = snprintf (buffer, 32, "N:%u", counter);
554         if ((status < 1) || (status >= 32))
555                 return;
557         plugin_submit ("dns_opcode", inst, buffer);
560 #if NAMED_HAVE_READ
561 static unsigned int dns_read_array (unsigned int *values)
563         unsigned int values_num;
565         if (pipe_fd < 0)
566                 return (0);
568         if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
569         {
570                 DBG ("Reading from the pipe failed: %s",
571                                 strerror (errno));
572                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
573                                 strerror (errno));
574                 pipe_fd = -1;
575                 return (0);
576         }
577         DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num);
579         assert (values_num <= T_MAX);
581         if (values_num == 0)
582                 return (0);
584         if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0)
585         {
586                 DBG ("Reading from the pipe failed: %s",
587                                 strerror (errno));
588                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
589                                 strerror (errno));
590                 pipe_fd = -1;
591                 return (0);
592         }
594         return (values_num);
597 static void dns_read (void)
599         unsigned int values[2 * T_MAX];
600         unsigned int values_num;
601         int i;
603         if (pipe_fd < 0)
604                 return;
606         if (sread (pipe_fd, (void *) &tr_queries, sizeof (tr_queries)) != 0)
607         {
608                 DBG ("Reading from the pipe failed: %s",
609                                 strerror (errno));
610                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
611                                 strerror (errno));
612                 pipe_fd = -1;
613                 return;
614         }
615         DBG ("sread (pipe_fd = %i, tr_queries = %u)", pipe_fd, tr_queries);
617         if (sread (pipe_fd, (void *) &tr_responses, sizeof (tr_responses)) != 0)
618         {
619                 DBG ("Reading from the pipe failed: %s",
620                                 strerror (errno));
621                 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
622                                 strerror (errno));
623                 pipe_fd = -1;
624                 return;
625         }
626         DBG ("sread (pipe_fd = %i, tr_responses = %u)", pipe_fd, tr_responses);
628         traffic_submit (tr_queries, tr_responses);
630         values_num = dns_read_array (values);
631         for (i = 0; i < values_num; i++)
632         {
633                 DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
634                 qtype_submit (values[2 * i], values[(2 * i) + 1]);
635         }
637         values_num = dns_read_array (values);
638         for (i = 0; i < values_num; i++)
639         {
640                 DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
641                 opcode_submit (values[2 * i], values[(2 * i) + 1]);
642         }
644         values_num = dns_read_array (values);
645         for (i = 0; i < values_num; i++)
646         {
647                 DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
648                 rcode_submit (values[2 * i], values[(2 * i) + 1]);
649         }
651 #else /* if !NAMED_HAVE_READ */
652 # define dns_read NULL
653 #endif
655 void module_register (void)
657         plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
658         plugin_register ("dns_traffic", NULL, NULL, traffic_write);
659         plugin_register ("dns_qtype", NULL, NULL, qtype_write);
660         plugin_register ("dns_rcode", NULL, NULL, rcode_write);
661         plugin_register ("dns_opcode", NULL, NULL, opcode_write);
662         cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
665 #undef MODULE_NAME