2 * collectd - src/dnstop.c
3 * Copyright (C) 2006 Florian octo Forster
4 * Copyright (C) 2002 The Measurement Factory, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * 3. Neither the name of The Measurement Factory nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 * The Measurement Factory, Inc. <http://www.measurement-factory.com/>
33 * Florian octo Forster <octo at verplant.org>
36 #include <sys/types.h>
40 #include <netinet/in.h>
52 #include <arpa/inet.h>
53 #include <arpa/nameser.h>
55 #include <arpa/nameser_compat.h>
58 #include <sys/socket.h>
59 #include <net/if_arp.h>
61 #include <netinet/if_ether.h>
63 #include <netinet/in_systm.h>
64 #include <netinet/ip.h>
65 #include <netinet/udp.h>
67 #define PCAP_SNAPLEN 1460
68 #define MAX_QNAME_SZ 512
70 #define ETHER_ADDR_LEN 6
71 #define ETHER_TYPE_LEN 2
72 #define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
74 #ifndef ETHERTYPE_8021Q
75 #define ETHERTYPE_8021Q 0x8100
79 #include <net/if_ppp.h>
80 #define PPP_ADDRESS_VAL 0xff /* The address byte value */
81 #define PPP_CONTROL_VAL 0x03 /* The control byte value */
93 typedef struct _AgentAddr AgentAddr;
100 typedef struct _StringCounter StringCounter;
101 struct _StringCounter {
107 /* This struct cobbles together Source and Sld */
108 typedef struct _StringAddrCounter StringAddrCounter;
109 struct _StringAddrCounter {
113 StringAddrCounter *next;
116 typedef struct _foo foo;
122 typedef struct _rfc1035_header rfc1035_header;
123 struct _rfc1035_header {
126 unsigned int opcode:4;
131 unsigned int rcode:4;
132 unsigned short qdcount;
133 unsigned short ancount;
134 unsigned short nscount;
135 unsigned short arcount;
138 typedef struct _AnonMap AnonMap;
145 typedef int Filter_t(unsigned short,
148 const struct in_addr,
149 const struct in_addr);
151 typedef int (printer)(const char *, ...);
154 * flags/features for non-interactive mode
170 static struct in_addr ignore_addr;
171 static int sld_flag = 0;
172 static int nld_flag = 0;
174 static int query_count_intvl = 0;
175 static int query_count_total = 0;
176 int qtype_counts[T_MAX];
177 static int opcode_counts[OP_MAX];
178 static int qclass_counts[C_MAX];
179 static AgentAddr *Sources = NULL;
180 static AgentAddr *Destinations = NULL;
181 static StringCounter *Tlds = NULL;
182 static StringCounter *Slds = NULL;
183 static StringCounter *Nlds = NULL;
184 static StringAddrCounter *SSC2 = NULL;
185 static StringAddrCounter *SSC3 = NULL;
187 static struct bpf_timeval last_ts;
189 static struct timeval last_ts;
193 AgentAddr_lookup_or_add(AgentAddr ** headP, struct in_addr a)
196 for (T = headP; (*T); T = &(*T)->next)
197 if ((*T)->src.s_addr == a.s_addr)
199 (*T) = calloc(1, sizeof(**T));
204 static StringCounter *
205 StringCounter_lookup_or_add(StringCounter ** headP, const char *s)
208 for (T = headP; (*T); T = &(*T)->next)
209 if (0 == strcmp((*T)->s, s))
211 (*T) = calloc(1, sizeof(**T));
216 static StringAddrCounter *
217 StringAddrCounter_lookup_or_add(StringAddrCounter ** headP, struct in_addr a, const char *str)
219 StringAddrCounter **T;
220 for (T = headP; (*T); T = &(*T)->next)
221 if (0 == strcmp((*T)->str, str))
222 if ((*T)->src.s_addr == a.s_addr)
224 (*T) = calloc(1, sizeof(**T));
225 (*T)->str = strdup(str);
230 #define RFC1035_MAXLABELSZ 63
232 rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
244 /* blasted compression */
247 memcpy(&s, buf + (*off), sizeof(s));
254 /* Make sure the pointer is inside this message */
257 return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
258 } else if (c > RFC1035_MAXLABELSZ) {
260 * "(The 10 and 01 combinations are reserved for future use.)"
271 if ((*off) + len > sz) /* message is too short */
273 memcpy(name + no, buf + (*off), len);
276 *(name + (no++)) = '.';
279 *(name + no - 1) = '\0';
280 /* make sure we didn't allow someone to overflow the name buffer */
286 QnameToNld(const char *qname, int nld)
288 const char *t = strrchr(qname, '.');
292 if (0 == strcmp(t, ".arpa"))
294 while (t > qname && dotcount < nld) {
305 handle_dns(const char *buf, int len, const struct in_addr sip, const struct in_addr dip)
309 char qname[MAX_QNAME_SZ];
310 unsigned short qtype;
311 unsigned short qclass;
317 StringAddrCounter *ssc;
319 fprintf (stderr, "handle_dns (buf = %p, len = %i)\n",
322 if (len < sizeof(qh))
325 memcpy(&us, buf + 00, 2);
328 memcpy(&us, buf + 2, 2);
330 qh.qr = (us >> 15) & 0x01;
331 qh.opcode = (us >> 11) & 0x0F;
332 qh.aa = (us >> 10) & 0x01;
333 qh.tc = (us >> 9) & 0x01;
334 qh.rd = (us >> 8) & 0x01;
335 qh.ra = (us >> 7) & 0x01;
336 qh.rcode = us & 0x0F;
338 memcpy(&us, buf + 4, 2);
339 qh.qdcount = ntohs(us);
341 memcpy(&us, buf + 6, 2);
342 qh.ancount = ntohs(us);
344 memcpy(&us, buf + 8, 2);
345 qh.nscount = ntohs(us);
347 memcpy(&us, buf + 10, 2);
348 qh.arcount = ntohs(us);
351 memset(qname, '\0', MAX_QNAME_SZ);
352 x = rfc1035NameUnpack(buf, len, &offset, qname, MAX_QNAME_SZ);
355 if ('\0' == qname[0])
357 while ((t = strchr(qname, '\n')))
359 while ((t = strchr(qname, '\r')))
361 for (t = qname; *t; t++)
364 memcpy(&us, buf + offset, 2);
366 memcpy(&us, buf + offset + 2, 2);
369 fprintf (stderr, "qtype = %hu\n", qtype);
372 qtype_counts[qtype]++;
373 qclass_counts[qclass]++;
374 opcode_counts[qh.opcode]++;
376 s = QnameToNld(qname, 1);
377 sc = StringCounter_lookup_or_add(&Tlds, s);
381 s = QnameToNld(qname, 2);
382 sc = StringCounter_lookup_or_add(&Slds, s);
385 /* increment StringAddrCounter */
386 ssc = StringAddrCounter_lookup_or_add(&SSC2, sip, s);
391 s = QnameToNld(qname, 3);
392 sc = StringCounter_lookup_or_add(&Nlds, s);
395 /* increment StringAddrCounter */
396 ssc = StringAddrCounter_lookup_or_add(&SSC3, sip, s);
404 handle_udp(const struct udphdr *udp, int len, struct in_addr sip, struct in_addr dip)
406 char buf[PCAP_SNAPLEN];
407 fprintf (stderr, "handle_udp (udp = %p, len = %i)\n",
409 if (ntohs (udp->uh_dport) != 53)
411 memcpy(buf, udp + 1, len - sizeof(*udp));
412 if (0 == handle_dns(buf, len - sizeof(*udp), sip, dip))
418 handle_ip(const struct ip *ip, int len)
420 char buf[PCAP_SNAPLEN];
421 int offset = ip->ip_hl << 2;
424 fprintf (stderr, "handle_ip (ip = %p, len = %i)\n",
426 if (ignore_addr.s_addr)
427 if (ip->ip_src.s_addr == ignore_addr.s_addr)
429 if (IPPROTO_UDP != ip->ip_p)
431 memcpy(buf, (void *) ip + offset, len - offset);
432 if (0 == handle_udp((struct udphdr *) buf, len - offset, ip->ip_src, ip->ip_dst))
434 clt = AgentAddr_lookup_or_add(&Sources, ip->ip_src);
436 srv = AgentAddr_lookup_or_add(&Destinations, ip->ip_dst);
443 handle_ppp(const u_char * pkt, int len)
445 char buf[PCAP_SNAPLEN];
447 unsigned short proto;
450 if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
451 pkt += 2; /* ACFC not used */
457 proto = *pkt; /* PFC is used */
461 memcpy(&us, pkt, sizeof(us));
466 if (ETHERTYPE_IP != proto && PPP_IP != proto)
468 memcpy(buf, pkt, len);
469 return handle_ip((struct ip *) buf, len);
475 handle_null(const u_char * pkt, int len)
478 memcpy(&family, pkt, sizeof(family));
479 if (AF_INET != family)
481 return handle_ip((struct ip *) (pkt + 4), len - 4);
486 handle_loop(const u_char * pkt, int len)
489 memcpy(&family, pkt, sizeof(family));
490 if (AF_INET != ntohl(family))
492 return handle_ip((struct ip *) (pkt + 4), len - 4);
499 handle_raw(const u_char * pkt, int len)
501 return handle_ip((struct ip *) pkt, len);
507 handle_ether(const u_char * pkt, int len)
509 char buf[PCAP_SNAPLEN];
510 struct ether_header *e = (void *) pkt;
511 unsigned short etype = ntohs(e->ether_type);
512 fprintf (stderr, "handle_ether (pkt = %p, len = %i)\n",
514 if (len < ETHER_HDR_LEN)
516 pkt += ETHER_HDR_LEN;
517 len -= ETHER_HDR_LEN;
518 if (ETHERTYPE_8021Q == etype) {
519 etype = ntohs(*(unsigned short *) (pkt + 2));
523 if (ETHERTYPE_IP != etype)
525 memcpy(buf, pkt, len);
526 return handle_ip((struct ip *) buf, len);
529 /* public function */
530 void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
534 fprintf (stderr, "handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n",
535 (void *) udata, (void *) hdr, (void *) pkt,
538 if (hdr->caplen < ETHER_HDR_LEN)
541 switch (pcap_datalink (pcap))
544 status = handle_ether (pkt, hdr->caplen);
548 status = handle_ppp (pkt, hdr->caplen);
553 status = handle_loop (pkt, hdr->caplen);
558 status = handle_raw (pkt, hdr->caplen);
562 status = handle_null (pkt, hdr->caplen);
566 fprintf (stderr, "unsupported data link type %d\n",
567 pcap_datalink(pcap));
570 } /* switch (pcap_datalink(pcap)) */
628 snprintf(buf, 30, "#%d", t);
655 snprintf(buf, 30, "Opcode%d", o);
663 main(int argc, char *argv[])
665 char errbuf[PCAP_ERRBUF_SIZE];
668 int readfile_state = 0;
669 struct bpf_program fp;
672 SubReport = Sources_report;
673 ignore_addr.s_addr = 0;
674 progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
678 while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
693 bpf_program_str = strdup(optarg);
696 ignore_addr.s_addr = inet_addr(optarg);
711 device = strdup(argv[0]);
713 if (0 == stat(device, &sb))
715 if (readfile_state) {
716 pcap = pcap_open_offline(device, errbuf);
718 pcap = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
721 fprintf(stderr, "pcap_open_*: %s\n", errbuf);
725 if (0 == isatty(1)) {
726 if (0 == readfile_state) {
727 fprintf(stderr, "Non-interactive mode requires savefile argument\n");
734 memset(&fp, '\0', sizeof(fp));
735 x = pcap_compile(pcap, &fp, bpf_program_str, 1, 0);
737 fprintf(stderr, "pcap_compile failed\n");
740 x = pcap_setfilter(pcap, &fp);
742 fprintf(stderr, "pcap_setfilter failed\n");
747 * non-blocking call added for Mac OS X bugfix. Sent by Max Horn.
748 * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
750 x = pcap_setnonblock(pcap, 1, errbuf);
752 fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
756 switch (pcap_datalink(pcap)) {
758 handle_datalink = handle_ether;
762 handle_datalink = handle_ppp;
767 handle_datalink = handle_loop;
772 handle_datalink = handle_raw;
776 handle_datalink = handle_null;
779 fprintf(stderr, "unsupported data link type %d\n",
780 pcap_datalink(pcap));
787 if (readfile_state < 2) {
789 * On some OSes select() might return 0 even when
790 * there are packets to process. Thus, we always
791 * ignore its return value and just call pcap_dispatch()
794 if (0 == readfile_state) /* interactive */
795 pcap_select(pcap, 1, 0);
796 x = pcap_dispatch(pcap, 50, handle_pcap, NULL);
798 if (0 == x && 1 == readfile_state) {
799 /* block on keyboard until user quits */
808 endwin(); /* klin, Thu Nov 28 08:56:51 2002 */
810 while (pcap_dispatch(pcap, 50, handle_pcap, NULL))
813 Sources_report(); print_func("\n");
814 Destinatioreport(); print_func("\n");
815 Qtypes_report(); print_func("\n");
816 Opcodes_report(); print_func("\n");
817 Tld_report(); print_func("\n");
818 Sld_report(); print_func("\n");
819 Nld_report(); print_func("\n");
820 SldBySource_report();
825 } /* static int main(int argc, char *argv[]) */