a83f0ac16f0f3984d5d7143ee1fe9c8cbf558451
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 /* 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)
407 {
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
450 }
452 static void traffic_write (char *host, char *inst, char *val)
453 {
454 rrd_update_file (host, traffic_file, val,
455 traffic_ds_def, traffic_ds_num);
456 }
458 static void qtype_write (char *host, char *inst, char *val)
459 {
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);
470 }
472 static void rcode_write (char *host, char *inst, char *val)
473 {
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);
484 }
486 static void opcode_write (char *host, char *inst, char *val)
487 {
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);
498 }
500 static void traffic_submit (unsigned int queries, unsigned int replies)
501 {
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);
510 }
512 static void qtype_submit (int qtype, unsigned int counter)
513 {
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);
526 }
528 static void rcode_submit (int rcode, unsigned int counter)
529 {
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);
542 }
544 static void opcode_submit (int opcode, unsigned int counter)
545 {
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);
558 }
560 #if NAMED_HAVE_READ
561 static unsigned int dns_read_array (unsigned int *values)
562 {
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);
595 }
597 static void dns_read (void)
598 {
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 }
650 }
651 #else /* if !NAMED_HAVE_READ */
652 # define dns_read NULL
653 #endif
655 void module_register (void)
656 {
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);
663 }
665 #undef MODULE_NAME