b97ce2870d58569a839b489e250cbb9201227f75
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)
116 {
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);
128 }
130 static counter_list_t *counter_list_create (counter_list_t **list,
131 unsigned int key, unsigned int value)
132 {
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);
163 }
165 static void counter_list_add (counter_list_t **list,
166 unsigned int key, unsigned int increment)
167 {
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 ()");
184 }
186 static int counter_list_send (counter_list_t *list, int fd)
187 {
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);
229 }
230 #if NAMED_HAVE_CONFIG
231 static int dns_config (char *key, char *value)
232 {
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 */
253 }
254 #endif /* NAMED_HAVE_CONFIG */
256 static void dns_child_callback (const rfc1035_header_t *dns)
257 {
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);
273 }
275 static void dns_child_loop (void)
276 {
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 /* Don't catch these signals */
285 signal (SIGINT, SIG_DFL);
286 signal (SIGTERM, SIG_DFL);
288 /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
289 DBG ("Creating PCAP object..");
290 pcap_obj = pcap_open_live (pcap_device,
291 PCAP_SNAPLEN,
292 0 /* Not promiscuous */,
293 0 /* no read timeout */,
294 pcap_error);
295 if (pcap_obj == NULL)
296 {
297 syslog (LOG_ERR, "dns plugin: Opening interface `%s' failed: %s",
298 (pcap_device != NULL) ? pcap_device : "any",
299 pcap_error);
300 close (pipe_fd);
301 pipe_fd = -1;
302 return;
303 }
305 memset (&fp, 0, sizeof (fp));
306 if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
307 {
308 DBG ("pcap_compile failed");
309 syslog (LOG_ERR, "dns plugin: pcap_compile failed");
310 close (pipe_fd);
311 pipe_fd = -1;
312 return;
313 }
314 if (pcap_setfilter (pcap_obj, &fp) < 0)
315 {
316 DBG ("pcap_setfilter failed");
317 syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
318 close (pipe_fd);
319 pipe_fd = -1;
320 return;
321 }
323 DBG ("PCAP object created.");
325 dnstop_set_pcap_obj (pcap_obj);
326 dnstop_set_callback (dns_child_callback);
328 /* Set up pipe end */
329 poll_fds[0].fd = pipe_fd;
330 poll_fds[0].events = POLLOUT;
332 /* Set up pcap device */
333 poll_fds[1].fd = pcap_fileno (pcap_obj);
334 poll_fds[1].events = POLLIN | POLLPRI;
336 while (pipe_fd > 0)
337 {
338 DBG ("poll (...)");
339 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
341 /* Signals are not caught, but this is very handy when
342 * attaching to the process with a debugger. -octo */
343 if ((status < 0) && (errno == EINTR))
344 {
345 errno = 0;
346 continue;
347 }
349 if (status < 0)
350 {
351 syslog (LOG_ERR, "dns plugin: poll(2) failed: %s",
352 strerror (errno));
353 break;
354 }
356 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
357 {
358 DBG ("Pipe closed. Exiting.");
359 syslog (LOG_NOTICE, "dns plugin: Pipe closed. Exiting.");
360 break;
361 }
362 else if (poll_fds[0].revents & POLLOUT)
363 {
364 DBG ("Sending data..");
366 DBG ("swrite (pipe_fd = %i, tr_queries = %i)", pipe_fd, tr_queries);
367 if (swrite (pipe_fd, (const void *) &tr_queries, sizeof (tr_queries)) != 0)
368 {
369 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
370 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
371 strerror (errno));
372 return;
373 }
375 DBG ("swrite (pipe_fd = %i, tr_responses = %i)", pipe_fd, tr_responses);
376 if (swrite (pipe_fd, (const void *) &tr_responses, sizeof (tr_responses)) != 0)
377 {
378 DBG ("Writing to pipe_fd failed: %s", strerror (errno));
379 syslog (LOG_ERR, "dns plugin: Writing to pipe_fd failed: %s",
380 strerror (errno));
381 return;
382 }
384 counter_list_send (qtype_list, pipe_fd);
385 counter_list_send (opcode_list, pipe_fd);
386 counter_list_send (rcode_list, pipe_fd);
387 }
389 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
390 {
391 DBG ("pcap-device closed. Exiting.");
392 syslog (LOG_ERR, "dns plugin: pcap-device closed. Exiting.");
393 break;
394 }
395 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
396 {
397 status = pcap_dispatch (pcap_obj,
398 10 /* Only handle 10 packets at a time */,
399 handle_pcap /* callback */,
400 NULL /* Whatever this means.. */);
401 if (status < 0)
402 {
403 DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj));
404 syslog (LOG_ERR, "dns plugin: pcap_dispatch failed: %s",
405 pcap_geterr (pcap_obj));
406 break;
407 }
408 }
409 } /* while (42) */
411 DBG ("child is exiting");
413 close (pipe_fd);
414 pipe_fd = -1;
415 pcap_close (pcap_obj);
416 } /* static void dns_child_loop (void) */
418 static void dns_init (void)
419 {
420 #if HAVE_LIBPCAP
421 int pipe_fds[2];
422 pid_t pid_child;
424 tr_queries = 0;
425 tr_responses = 0;
427 if (pipe (pipe_fds) != 0)
428 {
429 syslog (LOG_ERR, "dns plugin: pipe(2) failed: %s",
430 strerror (errno));
431 return;
432 }
434 /* Fork off child */
435 pid_child = fork ();
436 if (pid_child < 0)
437 {
438 syslog (LOG_ERR, "dns plugin: fork(2) failed: %s",
439 strerror (errno));
440 close (pipe_fds[0]);
441 close (pipe_fds[1]);
442 return;
443 }
444 else if (pid_child != 0)
445 {
446 /* parent: Close the writing end, keep the reading end. */
447 pipe_fd = pipe_fds[0];
448 close (pipe_fds[1]);
449 }
450 else
451 {
452 /* child: Close the reading end, keep the writing end. */
453 pipe_fd = pipe_fds[1];
454 close (pipe_fds[0]);
456 dns_child_loop ();
457 exit (0);
458 }
460 /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */
461 #endif
462 }
464 static void traffic_write (char *host, char *inst, char *val)
465 {
466 rrd_update_file (host, traffic_file, val,
467 traffic_ds_def, traffic_ds_num);
468 }
470 static void qtype_write (char *host, char *inst, char *val)
471 {
472 char file[512];
473 int status;
475 status = snprintf (file, 512, qtype_file, inst);
476 if (status < 1)
477 return;
478 else if (status >= 512)
479 return;
481 rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
482 }
484 static void rcode_write (char *host, char *inst, char *val)
485 {
486 char file[512];
487 int status;
489 status = snprintf (file, 512, rcode_file, inst);
490 if (status < 1)
491 return;
492 else if (status >= 512)
493 return;
495 rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
496 }
498 static void opcode_write (char *host, char *inst, char *val)
499 {
500 char file[512];
501 int status;
503 status = snprintf (file, 512, opcode_file, inst);
504 if (status < 1)
505 return;
506 else if (status >= 512)
507 return;
509 rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
510 }
512 static void traffic_submit (unsigned int queries, unsigned int replies)
513 {
514 char buffer[64];
515 int status;
517 status = snprintf (buffer, 64, "N:%u:%u", queries, replies);
518 if ((status < 1) || (status >= 64))
519 return;
521 plugin_submit ("dns_traffic", "-", buffer);
522 }
524 static void qtype_submit (int qtype, unsigned int counter)
525 {
526 char inst[32];
527 char buffer[32];
528 int status;
530 strncpy (inst, qtype_str (qtype), 32);
531 inst[31] = '\0';
533 status = snprintf (buffer, 32, "N:%u", counter);
534 if ((status < 1) || (status >= 32))
535 return;
537 plugin_submit ("dns_qtype", inst, buffer);
538 }
540 static void rcode_submit (int rcode, unsigned int counter)
541 {
542 char inst[32];
543 char buffer[32];
544 int status;
546 strncpy (inst, rcode_str (rcode), 32);
547 inst[31] = '\0';
549 status = snprintf (buffer, 32, "N:%u", counter);
550 if ((status < 1) || (status >= 32))
551 return;
553 plugin_submit ("dns_rcode", inst, buffer);
554 }
556 static void opcode_submit (int opcode, unsigned int counter)
557 {
558 char inst[32];
559 char buffer[32];
560 int status;
562 strncpy (inst, opcode_str (opcode), 32);
563 inst[31] = '\0';
565 status = snprintf (buffer, 32, "N:%u", counter);
566 if ((status < 1) || (status >= 32))
567 return;
569 plugin_submit ("dns_opcode", inst, buffer);
570 }
572 #if NAMED_HAVE_READ
573 static unsigned int dns_read_array (unsigned int *values)
574 {
575 unsigned int values_num;
577 if (pipe_fd < 0)
578 return (0);
580 if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
581 {
582 DBG ("Reading from the pipe failed: %s",
583 strerror (errno));
584 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
585 strerror (errno));
586 pipe_fd = -1;
587 return (0);
588 }
589 DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num);
591 assert (values_num <= T_MAX);
593 if (values_num == 0)
594 return (0);
596 if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0)
597 {
598 DBG ("Reading from the pipe failed: %s",
599 strerror (errno));
600 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
601 strerror (errno));
602 pipe_fd = -1;
603 return (0);
604 }
606 return (values_num);
607 }
609 static void dns_read (void)
610 {
611 unsigned int values[2 * T_MAX];
612 unsigned int values_num;
613 int i;
615 if (pipe_fd < 0)
616 return;
618 if (sread (pipe_fd, (void *) &tr_queries, sizeof (tr_queries)) != 0)
619 {
620 DBG ("Reading from the pipe failed: %s",
621 strerror (errno));
622 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
623 strerror (errno));
624 pipe_fd = -1;
625 return;
626 }
627 DBG ("sread (pipe_fd = %i, tr_queries = %u)", pipe_fd, tr_queries);
629 if (sread (pipe_fd, (void *) &tr_responses, sizeof (tr_responses)) != 0)
630 {
631 DBG ("Reading from the pipe failed: %s",
632 strerror (errno));
633 syslog (LOG_ERR, "dns plugin: Reading from the pipe failed: %s",
634 strerror (errno));
635 pipe_fd = -1;
636 return;
637 }
638 DBG ("sread (pipe_fd = %i, tr_responses = %u)", pipe_fd, tr_responses);
640 traffic_submit (tr_queries, tr_responses);
642 values_num = dns_read_array (values);
643 for (i = 0; i < values_num; i++)
644 {
645 DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
646 qtype_submit (values[2 * i], values[(2 * i) + 1]);
647 }
649 values_num = dns_read_array (values);
650 for (i = 0; i < values_num; i++)
651 {
652 DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
653 opcode_submit (values[2 * i], values[(2 * i) + 1]);
654 }
656 values_num = dns_read_array (values);
657 for (i = 0; i < values_num; i++)
658 {
659 DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
660 rcode_submit (values[2 * i], values[(2 * i) + 1]);
661 }
662 }
663 #else /* if !NAMED_HAVE_READ */
664 # define dns_read NULL
665 #endif
667 void module_register (void)
668 {
669 plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
670 plugin_register ("dns_traffic", NULL, NULL, traffic_write);
671 plugin_register ("dns_qtype", NULL, NULL, qtype_write);
672 plugin_register ("dns_rcode", NULL, NULL, rcode_write);
673 plugin_register ("dns_opcode", NULL, NULL, opcode_write);
674 cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
675 }
677 #undef MODULE_NAME