/**
* collectd - src/dns.c
- * Copyright (C) 2006 Florian octo Forster
+ * Copyright (C) 2006-2011 Florian octo Forster
+ * Copyright (C) 2009 Mirko Buffoni
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * Free Software Foundation; only version 2 of the License is applicable.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
+ * Mirko Buffoni <briareos at eswat.org>
**/
+#define _DEFAULT_SOURCE
+#define _BSD_SOURCE
+
#include "collectd.h"
#include "common.h"
#include "plugin.h"
#include "configfile.h"
-#include "utils_debug.h"
-#define MODULE_NAME "dns"
+#include "utils_dns.h"
+#include <pthread.h>
+#include <poll.h>
-#if HAVE_LIBPCAP && HAVE_LIBPTHREAD
-# include "utils_dns.h"
-# include <pthread.h>
-# include <pcap.h>
-# include <sys/poll.h>
-# define DNS_HAVE_READ 1
-#else
-# define DNS_HAVE_READ 0
-#endif
+#include <pcap.h>
/*
* Private data types
*/
-#if DNS_HAVE_READ
struct counter_list_s
{
unsigned int key;
struct counter_list_s *next;
};
typedef struct counter_list_s counter_list_t;
-#endif
/*
* Private variables
*/
-static char *traffic_file = "dns/dns_traffic.rrd";
-static char *qtype_file = "dns/qtype-%s.rrd";
-static char *opcode_file = "dns/opcode-%s.rrd";
-static char *rcode_file = "dns/rcode-%s.rrd";
-
-static char *traffic_ds_def[] =
-{
- /* Limit to 1GBit/s */
- "DS:queries:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
- "DS:responses:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
- NULL
-};
-static int traffic_ds_num = 2;
-
-static char *qtype_ds_def[] =
-{
- "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
- NULL
-};
-static int qtype_ds_num = 1;
-
-static char *opcode_ds_def[] =
-{
- "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
- NULL
-};
-static int opcode_ds_num = 1;
-
-static char *rcode_ds_def[] =
-{
- "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
- NULL
-};
-static int rcode_ds_num = 1;
-
-#if DNS_HAVE_READ
-static char *config_keys[] =
+static const char *config_keys[] =
{
"Interface",
"IgnoreSource",
- NULL
+ "SelectNumericQueryTypes"
};
-static int config_keys_num = 2;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+static int select_numeric_qtype = 1;
#define PCAP_SNAPLEN 1460
static char *pcap_device = NULL;
-static unsigned int tr_queries;
-static unsigned int tr_responses;
+static derive_t tr_queries;
+static derive_t tr_responses;
static counter_list_t *qtype_list;
static counter_list_t *opcode_list;
static counter_list_t *rcode_list;
static pthread_mutex_t qtype_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t opcode_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t rcode_mutex = PTHREAD_MUTEX_INITIALIZER;
-#endif /* DNS_HAVE_READ */
/*
* Private functions
*/
-#if DNS_HAVE_READ
static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
{
counter_list_t *entry;
- DBG ("counter_list_search (list = %p, key = %u)",
- (void *) *list, key);
-
for (entry = *list; entry != NULL; entry = entry->next)
if (entry->key == key)
break;
- DBG ("return (%p)", (void *) entry);
return (entry);
}
{
counter_list_t *entry;
- DBG ("counter_list_create (list = %p, key = %u, value = %u)",
- (void *) *list, key, value);
-
entry = (counter_list_t *) malloc (sizeof (counter_list_t));
if (entry == NULL)
return (NULL);
last->next = entry;
}
- DBG ("return (%p)", (void *) entry);
return (entry);
}
{
counter_list_t *entry;
- DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
- (void *) *list, key, increment);
-
entry = counter_list_search (list, key);
if (entry != NULL)
{
counter_list_create (list, key, increment);
}
- DBG ("return ()");
}
-static int dns_config (char *key, char *value)
+static int dns_config (const char *key, const char *value)
{
if (strcasecmp (key, "Interface") == 0)
{
if (value != NULL)
ignore_list_add_name (value);
}
+ else if (strcasecmp (key, "SelectNumericQueryTypes") == 0)
+ {
+ if ((value != NULL) && IS_FALSE (value))
+ select_numeric_qtype = 0;
+ else
+ select_numeric_qtype = 1;
+ }
else
{
return (-1);
if (dns->qr == 0)
{
/* This is a query */
+ int skip = 0;
+ if (!select_numeric_qtype)
+ {
+ const char *str = qtype_str(dns->qtype);
+ if ((str == NULL) || (str[0] == '#'))
+ skip = 1;
+ }
+
pthread_mutex_lock (&traffic_mutex);
tr_queries += dns->length;
pthread_mutex_unlock (&traffic_mutex);
- pthread_mutex_lock (&qtype_mutex);
- counter_list_add (&qtype_list, dns->qtype, 1);
- pthread_mutex_unlock (&qtype_mutex);
+ if (skip == 0)
+ {
+ pthread_mutex_lock (&qtype_mutex);
+ counter_list_add (&qtype_list, dns->qtype, 1);
+ pthread_mutex_unlock (&qtype_mutex);
+ }
}
else
{
pthread_mutex_unlock (&opcode_mutex);
}
-static void *dns_child_loop (void *dummy)
+static int dns_run_pcap_loop (void)
{
pcap_t *pcap_obj;
char pcap_error[PCAP_ERRBUF_SIZE];
}
/* Passing `pcap_device == NULL' is okay and the same as passign "any" */
- DBG ("Creating PCAP object..");
- pcap_obj = pcap_open_live (pcap_device,
+ DEBUG ("dns plugin: Creating PCAP object..");
+ pcap_obj = pcap_open_live ((pcap_device != NULL) ? pcap_device : "any",
PCAP_SNAPLEN,
0 /* Not promiscuous */,
- atoi (COLLECTD_STEP),
+ (int) CDTIME_T_TO_MS (plugin_get_interval () / 2),
pcap_error);
if (pcap_obj == NULL)
{
- syslog (LOG_ERR, "dns plugin: Opening interface `%s' "
+ ERROR ("dns plugin: Opening interface `%s' "
"failed: %s",
(pcap_device != NULL) ? pcap_device : "any",
pcap_error);
- return (NULL);
+ return (PCAP_ERROR);
}
memset (&fp, 0, sizeof (fp));
- if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
+ status = pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0);
+ if (status < 0)
{
- DBG ("pcap_compile failed");
- syslog (LOG_ERR, "dns plugin: pcap_compile failed");
- return (NULL);
+ ERROR ("dns plugin: pcap_compile failed: %s",
+ pcap_statustostr (status));
+ return (status);
}
- if (pcap_setfilter (pcap_obj, &fp) < 0)
+
+ status = pcap_setfilter (pcap_obj, &fp);
+ if (status < 0)
{
- DBG ("pcap_setfilter failed");
- syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
- return (NULL);
+ ERROR ("dns plugin: pcap_setfilter failed: %s",
+ pcap_statustostr (status));
+ return (status);
}
- DBG ("PCAP object created.");
+ DEBUG ("dns plugin: PCAP object created.");
dnstop_set_pcap_obj (pcap_obj);
dnstop_set_callback (dns_child_callback);
status = pcap_loop (pcap_obj,
-1 /* loop forever */,
handle_pcap /* callback */,
- NULL /* Whatever this means.. */);
- if (status < 0)
- syslog (LOG_ERR, "dns plugin: Listener thread is exiting "
- "abnormally: %s", pcap_geterr (pcap_obj));
-
- DBG ("child is exiting");
+ NULL /* user data */);
+ INFO ("dns plugin: pcap_loop exited with status %i.", status);
+ /* We need to handle "PCAP_ERROR" specially because libpcap currently
+ * doesn't return PCAP_ERROR_IFACE_NOT_UP for compatibility reasons. */
+ if (status == PCAP_ERROR)
+ status = PCAP_ERROR_IFACE_NOT_UP;
pcap_close (pcap_obj);
- listen_thread_init = 0;
- pthread_exit (NULL);
+ return (status);
+} /* int dns_run_pcap_loop */
- return (NULL);
-} /* static void dns_child_loop (void) */
-#endif /* DNS_HAVE_READ */
-
-static void dns_init (void)
+static int dns_sleep_one_interval (void) /* {{{ */
{
-#if DNS_HAVE_READ
- /* clean up an old thread */
- int status;
-
- pthread_mutex_lock (&traffic_mutex);
- tr_queries = 0;
- tr_responses = 0;
- pthread_mutex_unlock (&traffic_mutex);
+ cdtime_t interval;
+ struct timespec ts = { 0, 0 };
+ int status = 0;
- if (listen_thread_init != 0)
- return;
+ interval = plugin_get_interval ();
+ CDTIME_T_TO_TIMESPEC (interval, &ts);
- status = pthread_create (&listen_thread, NULL, dns_child_loop,
- (void *) 0);
- if (status != 0)
+ while (42)
{
- syslog (LOG_ERR, "dns plugin: pthread_create failed: %s",
- strerror (status));
- return;
- }
+ struct timespec rem = { 0, 0 };
- listen_thread_init = 1;
-#endif /* DNS_HAVE_READ */
-}
+ status = nanosleep (&ts, &rem);
+ if (status == 0)
+ break;
+ else if ((errno == EINTR) || (errno == EAGAIN))
+ {
+ ts = rem;
+ continue;
+ }
+ else
+ break;
+ }
-static void traffic_write (char *host, char *inst, char *val)
-{
- rrd_update_file (host, traffic_file, val,
- traffic_ds_def, traffic_ds_num);
-}
+ return (status);
+} /* }}} int dns_sleep_one_interval */
-static void qtype_write (char *host, char *inst, char *val)
+static void *dns_child_loop (__attribute__((unused)) void *dummy) /* {{{ */
{
- char file[512];
int status;
- status = snprintf (file, 512, qtype_file, inst);
- if (status < 1)
- return;
- else if (status >= 512)
- return;
-
- rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
-}
+ while (42)
+ {
+ status = dns_run_pcap_loop ();
+ if (status != PCAP_ERROR_IFACE_NOT_UP)
+ break;
-static void rcode_write (char *host, char *inst, char *val)
-{
- char file[512];
- int status;
+ dns_sleep_one_interval ();
+ }
- status = snprintf (file, 512, rcode_file, inst);
- if (status < 1)
- return;
- else if (status >= 512)
- return;
+ if (status != PCAP_ERROR_BREAK)
+ ERROR ("dns plugin: PCAP returned error %s.",
+ pcap_statustostr (status));
- rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
-}
+ listen_thread_init = 0;
+ return (NULL);
+} /* }}} void *dns_child_loop */
-static void opcode_write (char *host, char *inst, char *val)
+static int dns_init (void)
{
- char file[512];
+ /* clean up an old thread */
int status;
- status = snprintf (file, 512, opcode_file, inst);
- if (status < 1)
- return;
- else if (status >= 512)
- return;
-
- rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
-}
-
-#if DNS_HAVE_READ
-static void traffic_submit (unsigned int queries, unsigned int replies)
-{
- char buffer[64];
- int status;
-
- status = snprintf (buffer, 64, "N:%u:%u", queries, replies);
- if ((status < 1) || (status >= 64))
- return;
-
- plugin_submit ("dns_traffic", "-", buffer);
-}
+ pthread_mutex_lock (&traffic_mutex);
+ tr_queries = 0;
+ tr_responses = 0;
+ pthread_mutex_unlock (&traffic_mutex);
-static void qtype_submit (int qtype, unsigned int counter)
-{
- char inst[32];
- char buffer[32];
- int status;
+ if (listen_thread_init != 0)
+ return (-1);
- strncpy (inst, qtype_str (qtype), 32);
- inst[31] = '\0';
+ status = plugin_thread_create (&listen_thread, NULL, dns_child_loop,
+ (void *) 0);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("dns plugin: pthread_create failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
- status = snprintf (buffer, 32, "N:%u", counter);
- if ((status < 1) || (status >= 32))
- return;
+ listen_thread_init = 1;
- plugin_submit ("dns_qtype", inst, buffer);
-}
+ return (0);
+} /* int dns_init */
-static void rcode_submit (int rcode, unsigned int counter)
+static void submit_derive (const char *type, const char *type_instance,
+ derive_t value)
{
- char inst[32];
- char buffer[32];
- int status;
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
- strncpy (inst, rcode_str (rcode), 32);
- inst[31] = '\0';
+ values[0].derive = value;
- status = snprintf (buffer, 32, "N:%u", counter);
- if ((status < 1) || (status >= 32))
- return;
+ vl.values = values;
+ vl.values_len = 1;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "dns", sizeof (vl.plugin));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
- plugin_submit ("dns_rcode", inst, buffer);
-}
+ plugin_dispatch_values (&vl);
+} /* void submit_derive */
-static void opcode_submit (int opcode, unsigned int counter)
+static void submit_octets (derive_t queries, derive_t responses)
{
- char inst[32];
- char buffer[32];
- int status;
+ value_t values[2];
+ value_list_t vl = VALUE_LIST_INIT;
- strncpy (inst, opcode_str (opcode), 32);
- inst[31] = '\0';
+ values[0].derive = queries;
+ values[1].derive = responses;
- status = snprintf (buffer, 32, "N:%u", counter);
- if ((status < 1) || (status >= 32))
- return;
+ vl.values = values;
+ vl.values_len = 2;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "dns", sizeof (vl.plugin));
+ sstrncpy (vl.type, "dns_octets", sizeof (vl.type));
- plugin_submit ("dns_opcode", inst, buffer);
-}
+ plugin_dispatch_values (&vl);
+} /* void submit_octets */
-static void dns_read (void)
+static int dns_read (void)
{
unsigned int keys[T_MAX];
unsigned int values[T_MAX];
values[0] = tr_queries;
values[1] = tr_responses;
pthread_mutex_unlock (&traffic_mutex);
- traffic_submit (values[0], values[1]);
+
+ if ((values[0] != 0) || (values[1] != 0))
+ submit_octets (values[0], values[1]);
pthread_mutex_lock (&qtype_mutex);
for (ptr = qtype_list, len = 0;
for (i = 0; i < len; i++)
{
- DBG ("qtype = %u; counter = %u;", keys[i], values[i]);
- qtype_submit (keys[i], values[i]);
+ DEBUG ("dns plugin: qtype = %u; counter = %u;", keys[i], values[i]);
+ submit_derive ("dns_qtype", qtype_str (keys[i]), values[i]);
}
pthread_mutex_lock (&opcode_mutex);
for (i = 0; i < len; i++)
{
- DBG ("opcode = %u; counter = %u;", keys[i], values[i]);
- opcode_submit (keys[i], values[i]);
+ DEBUG ("dns plugin: opcode = %u; counter = %u;", keys[i], values[i]);
+ submit_derive ("dns_opcode", opcode_str (keys[i]), values[i]);
}
pthread_mutex_lock (&rcode_mutex);
for (i = 0; i < len; i++)
{
- DBG ("rcode = %u; counter = %u;", keys[i], values[i]);
- rcode_submit (keys[i], values[i]);
+ DEBUG ("dns plugin: rcode = %u; counter = %u;", keys[i], values[i]);
+ submit_derive ("dns_rcode", rcode_str (keys[i]), values[i]);
}
-}
-#else /* if !DNS_HAVE_READ */
-# define dns_read NULL
-#endif
+
+ return (0);
+} /* int dns_read */
void module_register (void)
{
- plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
- plugin_register ("dns_traffic", NULL, NULL, traffic_write);
- plugin_register ("dns_qtype", NULL, NULL, qtype_write);
- plugin_register ("dns_rcode", NULL, NULL, rcode_write);
- plugin_register ("dns_opcode", NULL, NULL, opcode_write);
-#if DNS_HAVE_READ
- cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
-#endif
-}
-
-#undef MODULE_NAME
+ plugin_register_config ("dns", dns_config, config_keys, config_keys_num);
+ plugin_register_init ("dns", dns_init);
+ plugin_register_read ("dns", dns_read);
+} /* void module_register */