cd068fb363744b05c06f637a53ec248bd4d48ea5
[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  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27 #include "utils_debug.h"
28 #include "utils_dns.h"
29
30 #define MODULE_NAME "dns"
31
32 #if HAVE_LIBPCAP && HAVE_LIBPTHREAD
33 # include <pthread.h>
34 # include <pcap.h>
35 # include <sys/poll.h>
36 # define DNS_HAVE_READ 1
37 #else
38 # define DNS_HAVE_READ 0
39 #endif
40
41 /*
42  * Private data types
43  */
44 #if DNS_HAVE_READ
45 struct counter_list_s
46 {
47         unsigned int key;
48         unsigned int value;
49         struct counter_list_s *next;
50 };
51 typedef struct counter_list_s counter_list_t;
52 #endif
53
54 /*
55  * Private variables
56  */
57 static char *traffic_file   = "dns/dns_traffic.rrd";
58 static char *qtype_file   = "dns/qtype-%s.rrd";
59 static char *opcode_file  = "dns/opcode-%s.rrd";
60 static char *rcode_file   = "dns/rcode-%s.rrd";
61
62 static char *traffic_ds_def[] =
63 {
64         /* Limit to 1GBit/s */
65         "DS:queries:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
66         "DS:responses:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
67         NULL
68 };
69 static int traffic_ds_num = 2;
70
71 static char *qtype_ds_def[] =
72 {
73         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
74         NULL
75 };
76 static int qtype_ds_num = 1;
77
78 static char *opcode_ds_def[] =
79 {
80         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
81         NULL
82 };
83 static int opcode_ds_num = 1;
84
85 static char *rcode_ds_def[] =
86 {
87         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
88         NULL
89 };
90 static int rcode_ds_num = 1;
91
92 #if DNS_HAVE_READ
93 static char *config_keys[] =
94 {
95         "Interface",
96         "IgnoreSource",
97         NULL
98 };
99 static int config_keys_num = 2;
100
101 #define PCAP_SNAPLEN 1460
102 static char   *pcap_device = NULL;
103
104 static unsigned int    tr_queries;
105 static unsigned int    tr_responses;
106 static counter_list_t *qtype_list;
107 static counter_list_t *opcode_list;
108 static counter_list_t *rcode_list;
109
110 static pthread_t       listen_thread;
111 static int             listen_thread_init = 0;
112 /* The `traffic' mutex if for `tr_queries' and `tr_responses' */
113 static pthread_mutex_t traffic_mutex = PTHREAD_MUTEX_INITIALIZER;
114 static pthread_mutex_t qtype_mutex   = PTHREAD_MUTEX_INITIALIZER;
115 static pthread_mutex_t opcode_mutex  = PTHREAD_MUTEX_INITIALIZER;
116 static pthread_mutex_t rcode_mutex   = PTHREAD_MUTEX_INITIALIZER;
117 #endif /* DNS_HAVE_READ */
118
119 /*
120  * Private functions
121  */
122 #if DNS_HAVE_READ
123 static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
124 {
125         counter_list_t *entry;
126
127         DBG ("counter_list_search (list = %p, key = %u)",
128                         (void *) *list, key);
129
130         for (entry = *list; entry != NULL; entry = entry->next)
131                 if (entry->key == key)
132                         break;
133
134         DBG ("return (%p)", (void *) entry);
135         return (entry);
136 }
137
138 static counter_list_t *counter_list_create (counter_list_t **list,
139                 unsigned int key, unsigned int value)
140 {
141         counter_list_t *entry;
142
143         DBG ("counter_list_create (list = %p, key = %u, value = %u)",
144                         (void *) *list, key, value);
145
146         entry = (counter_list_t *) malloc (sizeof (counter_list_t));
147         if (entry == NULL)
148                 return (NULL);
149
150         memset (entry, 0, sizeof (counter_list_t));
151         entry->key = key;
152         entry->value = value;
153
154         if (*list == NULL)
155         {
156                 *list = entry;
157         }
158         else
159         {
160                 counter_list_t *last;
161
162                 last = *list;
163                 while (last->next != NULL)
164                         last = last->next;
165
166                 last->next = entry;
167         }
168
169         DBG ("return (%p)", (void *) entry);
170         return (entry);
171 }
172
173 static void counter_list_add (counter_list_t **list,
174                 unsigned int key, unsigned int increment)
175 {
176         counter_list_t *entry;
177
178         DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
179                         (void *) *list, key, increment);
180
181         entry = counter_list_search (list, key);
182
183         if (entry != NULL)
184         {
185                 entry->value += increment;
186         }
187         else
188         {
189                 counter_list_create (list, key, increment);
190         }
191         DBG ("return ()");
192 }
193
194 static int dns_config (char *key, char *value)
195 {
196         if (strcasecmp (key, "Interface") == 0)
197         {
198                 if (pcap_device != NULL)
199                         free (pcap_device);
200                 if ((pcap_device = strdup (value)) == NULL)
201                         return (1);
202         }
203         else if (strcasecmp (key, "IgnoreSource") == 0)
204         {
205                 if (value != NULL)
206                         ignore_list_add_name (value);
207         }
208         else
209         {
210                 return (-1);
211         }
212
213         return (0);
214 }
215
216 static void dns_child_callback (const rfc1035_header_t *dns)
217 {
218         if (dns->qr == 0)
219         {
220                 /* This is a query */
221                 pthread_mutex_lock (&traffic_mutex);
222                 tr_queries += dns->length;
223                 pthread_mutex_unlock (&traffic_mutex);
224
225                 pthread_mutex_lock (&qtype_mutex);
226                 counter_list_add (&qtype_list,  dns->qtype,  1);
227                 pthread_mutex_unlock (&qtype_mutex);
228         }
229         else
230         {
231                 /* This is a reply */
232                 pthread_mutex_lock (&traffic_mutex);
233                 tr_responses += dns->length;
234                 pthread_mutex_unlock (&traffic_mutex);
235
236                 pthread_mutex_lock (&rcode_mutex);
237                 counter_list_add (&rcode_list,  dns->rcode,  1);
238                 pthread_mutex_unlock (&rcode_mutex);
239         }
240
241         /* FIXME: Are queries, replies or both interesting? */
242         pthread_mutex_lock (&opcode_mutex);
243         counter_list_add (&opcode_list, dns->opcode, 1);
244         pthread_mutex_unlock (&opcode_mutex);
245 }
246
247 static void *dns_child_loop (void *dummy)
248 {
249         pcap_t *pcap_obj;
250         char    pcap_error[PCAP_ERRBUF_SIZE];
251         struct  bpf_program fp;
252
253         int status;
254
255         /* Don't block any signals */
256         {
257                 sigset_t sigmask;
258                 sigemptyset (&sigmask);
259                 pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
260         }
261
262         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
263         DBG ("Creating PCAP object..");
264         pcap_obj = pcap_open_live (pcap_device,
265                         PCAP_SNAPLEN,
266                         0 /* Not promiscuous */,
267                         atoi (COLLECTD_STEP),
268                         pcap_error);
269         if (pcap_obj == NULL)
270         {
271                 syslog (LOG_ERR, "dns plugin: Opening interface `%s' "
272                                 "failed: %s",
273                                 (pcap_device != NULL) ? pcap_device : "any",
274                                 pcap_error);
275                 return (NULL);
276         }
277
278         memset (&fp, 0, sizeof (fp));
279         if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
280         {
281                 DBG ("pcap_compile failed");
282                 syslog (LOG_ERR, "dns plugin: pcap_compile failed");
283                 return (NULL);
284         }
285         if (pcap_setfilter (pcap_obj, &fp) < 0)
286         {
287                 DBG ("pcap_setfilter failed");
288                 syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
289                 return (NULL);
290         }
291
292         DBG ("PCAP object created.");
293
294         dnstop_set_pcap_obj (pcap_obj);
295         dnstop_set_callback (dns_child_callback);
296
297         status = pcap_loop (pcap_obj,
298                         -1 /* loop forever */,
299                         handle_pcap /* callback */,
300                         NULL /* Whatever this means.. */);
301         if (status < 0)
302                 syslog (LOG_ERR, "dns plugin: Listener thread is exiting "
303                                 "abnormally: %s", pcap_geterr (pcap_obj));
304
305         DBG ("child is exiting");
306
307         pcap_close (pcap_obj);
308         listen_thread_init = 0;
309         pthread_exit (NULL);
310
311         return (NULL);
312 } /* static void dns_child_loop (void) */
313 #endif /* DNS_HAVE_READ */
314
315 static void dns_init (void)
316 {
317 #if DNS_HAVE_READ
318         /* clean up an old thread */
319         int status;
320
321         pthread_mutex_lock (&traffic_mutex);
322         tr_queries   = 0;
323         tr_responses = 0;
324         pthread_mutex_unlock (&traffic_mutex);
325
326         if (listen_thread_init != 0)
327                 return;
328
329         status = pthread_create (&listen_thread, NULL, dns_child_loop,
330                         (void *) 0);
331         if (status != 0)
332         {
333                 syslog (LOG_ERR, "dns plugin: pthread_create failed: %s",
334                                 strerror (status));
335                 return;
336         }
337
338         listen_thread_init = 1;
339 #endif /* DNS_HAVE_READ */
340 }
341
342 static void traffic_write (char *host, char *inst, char *val)
343 {
344         rrd_update_file (host, traffic_file, val,
345                         traffic_ds_def, traffic_ds_num);
346 }
347
348 static void qtype_write (char *host, char *inst, char *val)
349 {
350         char file[512];
351         int status;
352
353         status = snprintf (file, 512, qtype_file, inst);
354         if (status < 1)
355                 return;
356         else if (status >= 512)
357                 return;
358
359         rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
360 }
361
362 static void rcode_write (char *host, char *inst, char *val)
363 {
364         char file[512];
365         int status;
366
367         status = snprintf (file, 512, rcode_file, inst);
368         if (status < 1)
369                 return;
370         else if (status >= 512)
371                 return;
372
373         rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
374 }
375
376 static void opcode_write (char *host, char *inst, char *val)
377 {
378         char file[512];
379         int status;
380
381         status = snprintf (file, 512, opcode_file, inst);
382         if (status < 1)
383                 return;
384         else if (status >= 512)
385                 return;
386
387         rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
388 }
389
390 #if DNS_HAVE_READ
391 static void traffic_submit (unsigned int queries, unsigned int replies)
392 {
393         char buffer[64];
394         int  status;
395
396         status = snprintf (buffer, 64, "N:%u:%u", queries, replies);
397         if ((status < 1) || (status >= 64))
398                 return;
399
400         plugin_submit ("dns_traffic", "-", buffer);
401 }
402
403 static void qtype_submit (int qtype, unsigned int counter)
404 {
405         char inst[32];
406         char buffer[32];
407         int  status;
408
409         strncpy (inst, qtype_str (qtype), 32);
410         inst[31] = '\0';
411
412         status = snprintf (buffer, 32, "N:%u", counter);
413         if ((status < 1) || (status >= 32))
414                 return;
415
416         plugin_submit ("dns_qtype", inst, buffer);
417 }
418
419 static void rcode_submit (int rcode, unsigned int counter)
420 {
421         char inst[32];
422         char buffer[32];
423         int  status;
424
425         strncpy (inst, rcode_str (rcode), 32);
426         inst[31] = '\0';
427
428         status = snprintf (buffer, 32, "N:%u", counter);
429         if ((status < 1) || (status >= 32))
430                 return;
431
432         plugin_submit ("dns_rcode", inst, buffer);
433 }
434
435 static void opcode_submit (int opcode, unsigned int counter)
436 {
437         char inst[32];
438         char buffer[32];
439         int  status;
440
441         strncpy (inst, opcode_str (opcode), 32);
442         inst[31] = '\0';
443
444         status = snprintf (buffer, 32, "N:%u", counter);
445         if ((status < 1) || (status >= 32))
446                 return;
447
448         plugin_submit ("dns_opcode", inst, buffer);
449 }
450
451 static void dns_read (void)
452 {
453         unsigned int keys[T_MAX];
454         unsigned int values[T_MAX];
455         int len;
456         int i;
457
458         counter_list_t *ptr;
459
460         pthread_mutex_lock (&traffic_mutex);
461         values[0] = tr_queries;
462         values[1] = tr_responses;
463         pthread_mutex_unlock (&traffic_mutex);
464         traffic_submit (values[0], values[1]);
465
466         pthread_mutex_lock (&qtype_mutex);
467         for (ptr = qtype_list, len = 0;
468                         (ptr != NULL) && (len < T_MAX);
469                         ptr = ptr->next, len++)
470         {
471                 keys[len]   = ptr->key;
472                 values[len] = ptr->value;
473         }
474         pthread_mutex_unlock (&qtype_mutex);
475
476         for (i = 0; i < len; i++)
477         {
478                 DBG ("qtype = %u; counter = %u;", keys[i], values[i]);
479                 qtype_submit (keys[i], values[i]);
480         }
481
482         pthread_mutex_lock (&opcode_mutex);
483         for (ptr = opcode_list, len = 0;
484                         (ptr != NULL) && (len < T_MAX);
485                         ptr = ptr->next, len++)
486         {
487                 keys[len]   = ptr->key;
488                 values[len] = ptr->value;
489         }
490         pthread_mutex_unlock (&opcode_mutex);
491
492         for (i = 0; i < len; i++)
493         {
494                 DBG ("opcode = %u; counter = %u;", keys[i], values[i]);
495                 opcode_submit (keys[i], values[i]);
496         }
497
498         pthread_mutex_lock (&rcode_mutex);
499         for (ptr = rcode_list, len = 0;
500                         (ptr != NULL) && (len < T_MAX);
501                         ptr = ptr->next, len++)
502         {
503                 keys[len]   = ptr->key;
504                 values[len] = ptr->value;
505         }
506         pthread_mutex_unlock (&rcode_mutex);
507
508         for (i = 0; i < len; i++)
509         {
510                 DBG ("rcode = %u; counter = %u;", keys[i], values[i]);
511                 rcode_submit (keys[i], values[i]);
512         }
513 }
514 #else /* if !DNS_HAVE_READ */
515 # define dns_read NULL
516 #endif
517
518 void module_register (void)
519 {
520         plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
521         plugin_register ("dns_traffic", NULL, NULL, traffic_write);
522         plugin_register ("dns_qtype", NULL, NULL, qtype_write);
523         plugin_register ("dns_rcode", NULL, NULL, rcode_write);
524         plugin_register ("dns_opcode", NULL, NULL, opcode_write);
525 #if DNS_HAVE_READ
526         cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
527 #endif
528 }
529
530 #undef MODULE_NAME