More improvements on the named/dns plugin.
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 22 Oct 2006 14:08:45 +0000 (16:08 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Sun, 22 Oct 2006 14:08:45 +0000 (16:08 +0200)
The `dnstop.c' code now contains my IPv6 patch to capture IPv6 traffic. The
plugin captures both, queries and replies, and collects statistics
accordingly. This means that the qtype of queries and the rcode of replies are
collected. The plugin can now be configured to ignore certain sources, but
this functionality is to be improved.

src/dnstop.c
src/dnstop.h
src/named.c

index a5b651d..306a7e0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * collectd - src/dnstop.c
- * Copyright (C) 2006  Florian octo Forster
+ * Modifications Copyright (C) 2006  Florian octo Forster
  * Copyright (C) 2002  The Measurement Factory, Inc.
  * All rights reserved.
  * 
 
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
+#include <netinet/ip6.h>
 #include <netinet/udp.h>
 
+#include <netdb.h>
+
 #define PCAP_SNAPLEN 1460
-#define MAX_QNAME_SZ 512
 #ifndef ETHER_HDR_LEN
 #define ETHER_ADDR_LEN 6
 #define ETHER_TYPE_LEN 2
 #define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
 #endif
 #ifndef ETHERTYPE_8021Q
-#define ETHERTYPE_8021Q 0x8100
+# define ETHERTYPE_8021Q 0x8100
+#endif
+#ifndef ETHERTYPE_IPV6
+# define ETHERTYPE_IPV6 0x86DD
 #endif
 
-#if USE_PPP
 #include <net/if_ppp.h>
-#define PPP_ADDRESS_VAL       0xff     /* The address byte value */
-#define PPP_CONTROL_VAL       0x03     /* The control byte value */
+#ifndef PPP_ADDRESS_VAL
+# define PPP_ADDRESS_VAL 0xff  /* The address byte value */
+#endif
+#ifndef PPP_CONTROL_VAL
+# define PPP_CONTROL_VAL 0x03  /* The control byte value */
 #endif
 
 #ifdef __linux__
+#define uh_sport source
 #define uh_dport dest
 #endif
 
 /*
  * Type definitions
  */
-typedef struct _AgentAddr AgentAddr;
-struct _AgentAddr {
-    struct in_addr src;
-    int count;
-    AgentAddr *next;
-};
-
-typedef struct _StringCounter StringCounter;
-struct _StringCounter {
-    char *s;
-    int count;
-    StringCounter *next;
-};
-
-/* This struct cobbles together Source and Sld */
-typedef struct _StringAddrCounter StringAddrCounter;
-struct _StringAddrCounter {
-    struct in_addr src;
-    char *str;
-    int count;
-    StringAddrCounter *next;
-};
-
-typedef struct _foo foo;
-struct _foo {
-    int cnt;
-    void *ptr;
-};
-
-typedef struct _rfc1035_header rfc1035_header;
-struct _rfc1035_header {
-    unsigned short id;
-    unsigned int qr:1;
-    unsigned int opcode:4;
-    unsigned int aa:1;
-    unsigned int tc:1;
-    unsigned int rd:1;
-    unsigned int ra:1;
-    unsigned int rcode:4;
-    unsigned short qdcount;
-    unsigned short ancount;
-    unsigned short nscount;
-    unsigned short arcount;
-};
-
-typedef struct _AnonMap AnonMap;
-struct _AnonMap {
-    struct in_addr real;
-    struct in_addr anon;
-    AnonMap *next;
+struct ip_list_s
+{
+    struct in6_addr addr;
+    void *data;
+    struct ip_list_s *next;
 };
-
-typedef int Filter_t(unsigned short,
-       unsigned short,
-       const char *,
-       const struct in_addr,
-       const struct in_addr);
+typedef struct ip_list_s ip_list_t;
 
 typedef int (printer)(const char *, ...);
 
@@ -160,71 +118,128 @@ typedef int (printer)(const char *, ...);
 #ifndef T_SRV
 #define T_SRV 33
 #endif
-#define C_MAX 65536
-#define OP_MAX 16
 
 /*
  * Global variables
  */
-pcap_t *pcap = NULL;
-static struct in_addr ignore_addr;
-static int sld_flag = 0;
-static int nld_flag = 0;
+int qtype_counts[T_MAX];
+int opcode_counts[OP_MAX];
+int qclass_counts[C_MAX];
+
+static pcap_t *pcap_obj = NULL;
+
+static ip_list_t *IgnoreList = NULL;
+
+static void (*Callback) (const rfc1035_header_t *) = NULL;
 
 static int query_count_intvl = 0;
 static int query_count_total = 0;
-int qtype_counts[T_MAX];
-static int opcode_counts[OP_MAX];
-static int qclass_counts[C_MAX];
-static AgentAddr *Sources = NULL;
-static AgentAddr *Destinations = NULL;
-static StringCounter *Tlds = NULL;
-static StringCounter *Slds = NULL;
-static StringCounter *Nlds = NULL;
-static StringAddrCounter *SSC2 = NULL;
-static StringAddrCounter *SSC3 = NULL;
 #ifdef __OpenBSD__
 static struct bpf_timeval last_ts;
 #else
 static struct timeval last_ts;
 #endif
 
-static AgentAddr *
-AgentAddr_lookup_or_add(AgentAddr ** headP, struct in_addr a)
+static int cmp_in6_addr (const struct in6_addr *a,
+       const struct in6_addr *b)
 {
-    AgentAddr **T;
-    for (T = headP; (*T); T = &(*T)->next)
-       if ((*T)->src.s_addr == a.s_addr)
-           return (*T);
-    (*T) = calloc(1, sizeof(**T));
-    (*T)->src = a;
-    return (*T);
+    int i;
+
+    for (i = 0; i < 4; i++)
+       if (a->s6_addr32[i] != b->s6_addr32[i])
+           break;
+
+    if (i >= 4)
+       return (0);
+
+    return (a->s6_addr32[i] > b->s6_addr32[i] ? 1 : -1);
+} /* int cmp_addrinfo */
+
+static inline int ignore_list_match (const struct in6_addr *addr)
+{
+    ip_list_t *ptr;
+
+    for (ptr = IgnoreList; ptr != NULL; ptr = ptr->next)
+       if (cmp_in6_addr (addr, &ptr->addr) == 0)
+           return (1);
+    return (0);
+} /* int ignore_list_match */
+
+static void ignore_list_add (const struct in6_addr *addr)
+{
+    ip_list_t *new;
+
+    if (ignore_list_match (addr) != 0)
+       return;
+
+    new = malloc (sizeof (ip_list_t));
+    if (new == NULL)
+    {
+       perror ("malloc");
+       return;
+    }
+
+    memcpy (&new->addr, addr, sizeof (struct in6_addr));
+    new->next = IgnoreList;
+
+    IgnoreList = new;
+} /* void ignore_list_add */
+
+void ignore_list_add_name (const char *name)
+{
+    struct addrinfo *ai_list;
+    struct addrinfo *ai_ptr;
+    struct in6_addr  addr;
+    int status;
+
+    status = getaddrinfo (name, NULL, NULL, &ai_list);
+    if (status != 0)
+       return;
+
+    for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+    {
+       if (ai_ptr->ai_family == AF_INET)
+       {
+           addr.s6_addr32[0] = 0;
+           addr.s6_addr32[1] = 0;
+           addr.s6_addr32[2] = htonl (0x0000FFFF);
+           addr.s6_addr32[3] = ((struct sockaddr_in *) ai_ptr->ai_addr)->sin_addr.s_addr;
+
+           ignore_list_add (&addr);
+       }
+       else
+       {
+           ignore_list_add (&((struct sockaddr_in6 *) ai_ptr->ai_addr)->sin6_addr);
+       }
+    } /* for */
+
+    freeaddrinfo (ai_list);
 }
 
-static StringCounter *
-StringCounter_lookup_or_add(StringCounter ** headP, const char *s)
+static void in6_addr_from_buffer (struct in6_addr *ia,
+       const void *buf, size_t buf_len,
+       int family)
 {
-    StringCounter **T;
-    for (T = headP; (*T); T = &(*T)->next)
-       if (0 == strcmp((*T)->s, s))
-           return (*T);
-    (*T) = calloc(1, sizeof(**T));
-    (*T)->s = strdup(s);
-    return (*T);
+    memset (ia, 0, sizeof (struct in6_addr));
+    if ((AF_INET == family) && (sizeof (uint32_t) == buf_len))
+    {
+       ia->s6_addr32[2] = htonl (0x0000FFFF);
+       ia->s6_addr32[3] = *((uint32_t *) buf);
+    }
+    else if ((AF_INET6 == family) && (sizeof (struct in6_addr) == buf_len))
+    {
+       memcpy (ia, buf, buf_len);
+    }
+} /* void in6_addr_from_buffer */
+
+void dnstop_set_pcap_obj (pcap_t *po)
+{
+       pcap_obj = po;
 }
 
-static StringAddrCounter *
-StringAddrCounter_lookup_or_add(StringAddrCounter ** headP, struct in_addr a, const char *str)
+void dnstop_set_callback (void (*cb) (const rfc1035_header_t *))
 {
-    StringAddrCounter **T;
-    for (T = headP; (*T); T = &(*T)->next)
-       if (0 == strcmp((*T)->str, str))
-           if ((*T)->src.s_addr == a.s_addr)
-               return (*T);
-    (*T) = calloc(1, sizeof(**T));
-    (*T)->str = strdup(str);
-    (*T)->src = a;
-    return (*T);
+       Callback = cb;
 }
 
 #define RFC1035_MAXLABELSZ 63
@@ -282,57 +297,36 @@ rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
     return 0;
 }
 
-static const char *
-QnameToNld(const char *qname, int nld)
-{
-    const char *t = strrchr(qname, '.');
-    int dotcount = 1;
-    if (NULL == t)
-       t = qname;
-    if (0 == strcmp(t, ".arpa"))
-       dotcount--;
-    while (t > qname && dotcount < nld) {
-       t--;
-       if ('.' == *t)
-           dotcount++;
-    }
-    if (t > qname)
-       t++;
-    return t;
-}
-
 static int
-handle_dns(const char *buf, int len, const struct in_addr sip, const struct in_addr dip)
+handle_dns(const char *buf, int len,
+       const struct in6_addr *s_addr,
+       const struct in6_addr *d_addr)
 {
-    rfc1035_header qh;
-    unsigned short us;
-    char qname[MAX_QNAME_SZ];
-    unsigned short qtype;
-    unsigned short qclass;
+    rfc1035_header_t qh;
+    uint16_t us;
     off_t offset;
     char *t;
-    const char *s;
     int x;
-    StringCounter *sc;
-    StringAddrCounter *ssc;
 
-    fprintf (stderr, "handle_dns (buf = %p, len = %i)\n",
-                   (void *) buf, len);
-
-    if (len < sizeof(qh))
+    /* The DNS header is 12 bytes long */
+    if (len < 12)
        return 0;
 
-    memcpy(&us, buf + 00, 2);
+    memcpy(&us, buf + 0, 2);
     qh.id = ntohs(us);
 
     memcpy(&us, buf + 2, 2);
     us = ntohs(us);
+    fprintf (stderr, "Bytes 0, 1: 0x%04hx\n", us);
     qh.qr = (us >> 15) & 0x01;
     qh.opcode = (us >> 11) & 0x0F;
     qh.aa = (us >> 10) & 0x01;
     qh.tc = (us >> 9) & 0x01;
     qh.rd = (us >> 8) & 0x01;
     qh.ra = (us >> 7) & 0x01;
+    qh.z  = (us >> 6) & 0x01;
+    qh.ad = (us >> 5) & 0x01;
+    qh.cd = (us >> 4) & 0x01;
     qh.rcode = us & 0x0F;
 
     memcpy(&us, buf + 4, 2);
@@ -347,98 +341,145 @@ handle_dns(const char *buf, int len, const struct in_addr sip, const struct in_a
     memcpy(&us, buf + 10, 2);
     qh.arcount = ntohs(us);
 
-    offset = sizeof(qh);
-    memset(qname, '\0', MAX_QNAME_SZ);
-    x = rfc1035NameUnpack(buf, len, &offset, qname, MAX_QNAME_SZ);
+    offset = 12;
+    memset(qh.qname, '\0', MAX_QNAME_SZ);
+    x = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
     if (0 != x)
        return 0;
-    if ('\0' == qname[0])
-       strcpy(qname, ".");
-    while ((t = strchr(qname, '\n')))
+    if ('\0' == qh.qname[0])
+       strcpy(qh.qname, ".");
+    while ((t = strchr(qh.qname, '\n')))
        *t = ' ';
-    while ((t = strchr(qname, '\r')))
+    while ((t = strchr(qh.qname, '\r')))
        *t = ' ';
-    for (t = qname; *t; t++)
+    for (t = qh.qname; *t; t++)
        *t = tolower(*t);
 
     memcpy(&us, buf + offset, 2);
-    qtype = ntohs(us);
+    qh.qtype = ntohs(us);
     memcpy(&us, buf + offset + 2, 2);
-    qclass = ntohs(us);
+    qh.qclass = ntohs(us);
 
-    fprintf (stderr, "qtype = %hu\n", qtype);
+    qh.length = (uint16_t) len;
 
     /* gather stats */
-    qtype_counts[qtype]++;
-    qclass_counts[qclass]++;
+    qtype_counts[qh.qtype]++;
+    qclass_counts[qh.qclass]++;
     opcode_counts[qh.opcode]++;
 
-    s = QnameToNld(qname, 1);
-    sc = StringCounter_lookup_or_add(&Tlds, s);
-    sc->count++;
-
-    if (sld_flag) {
-       s = QnameToNld(qname, 2);
-       sc = StringCounter_lookup_or_add(&Slds, s);
-       sc->count++;
-
-       /* increment StringAddrCounter */
-       ssc = StringAddrCounter_lookup_or_add(&SSC2, sip, s);
-       ssc->count++;
+    if (Callback != NULL)
+           Callback (&qh);
 
-    }
-    if (nld_flag) {
-       s = QnameToNld(qname, 3);
-       sc = StringCounter_lookup_or_add(&Nlds, s);
-       sc->count++;
-
-       /* increment StringAddrCounter */
-       ssc = StringAddrCounter_lookup_or_add(&SSC3, sip, s);
-       ssc->count++;
-
-    }
     return 1;
 }
 
 static int
-handle_udp(const struct udphdr *udp, int len, struct in_addr sip, struct in_addr dip)
+handle_udp(const struct udphdr *udp, int len,
+       const struct in6_addr *s_addr,
+       const struct in6_addr *d_addr)
 {
     char buf[PCAP_SNAPLEN];
-    fprintf (stderr, "handle_udp (udp = %p, len = %i)\n",
-                   (void *) udp, len);
-    if (ntohs (udp->uh_dport) != 53)
+    if ((ntohs (udp->uh_dport) != 53)
+                   && (ntohs (udp->uh_sport) != 53))
        return 0;
     memcpy(buf, udp + 1, len - sizeof(*udp));
-    if (0 == handle_dns(buf, len - sizeof(*udp), sip, dip))
+    if (0 == handle_dns(buf, len - sizeof(*udp), s_addr, d_addr))
        return 0;
     return 1;
 }
 
 static int
+handle_ipv6 (struct ip6_hdr *ipv6, int len)
+{
+    char buf[PCAP_SNAPLEN];
+    int offset;
+    int nexthdr;
+
+    struct in6_addr s_addr;
+    struct in6_addr d_addr;
+    uint16_t payload_len;
+
+    offset = sizeof (struct ip6_hdr);
+    nexthdr = ipv6->ip6_nxt;
+    s_addr = ipv6->ip6_src;
+    d_addr = ipv6->ip6_dst;
+    payload_len = ntohs (ipv6->ip6_plen);
+
+    if (ignore_list_match (&s_addr))
+           return (0);
+
+    /* Parse extension headers. This only handles the standard headers, as
+     * defined in RFC 2460, correctly. Fragments are discarded. */
+    while ((IPPROTO_ROUTING == nexthdr) /* routing header */
+           || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */
+           || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */
+           || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
+           || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
+           || (IPPROTO_AH == nexthdr) /* destination options. */
+           || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */
+    {
+       struct ip6_ext ext_hdr;
+       uint16_t ext_hdr_len;
+
+       /* Catch broken packets */
+       if ((offset + sizeof (struct ip6_ext)) > len)
+           return (0);
+
+       /* Cannot handle fragments. */
+       if (IPPROTO_FRAGMENT == nexthdr)
+           return (0);
+
+       memcpy (&ext_hdr, (char *) ipv6 + offset, sizeof (struct ip6_ext));
+       nexthdr = ext_hdr.ip6e_nxt;
+       ext_hdr_len = (8 * (ntohs (ext_hdr.ip6e_len) + 1));
+
+       /* This header is longer than the packets payload.. WTF? */
+       if (ext_hdr_len > payload_len)
+           return (0);
+
+       offset += ext_hdr_len;
+       payload_len -= ext_hdr_len;
+    } /* while */
+
+    /* Catch broken and empty packets */
+    if (((offset + payload_len) > len)
+           || (payload_len == 0)
+           || (payload_len > PCAP_SNAPLEN))
+       return (0);
+
+    if (IPPROTO_UDP != nexthdr)
+       return (0);
+
+    memcpy (buf, (char *) ipv6 + offset, payload_len);
+    if (handle_udp ((struct udphdr *) buf, payload_len, &s_addr, &d_addr) == 0)
+       return (0);
+
+    return (1); /* Success */
+} /* int handle_ipv6 */
+
+static int
 handle_ip(const struct ip *ip, int len)
 {
     char buf[PCAP_SNAPLEN];
     int offset = ip->ip_hl << 2;
-    AgentAddr *clt;
-    AgentAddr *srv;
-    fprintf (stderr, "handle_ip (ip = %p, len = %i)\n",
-                   (void *) ip, len);
-    if (ignore_addr.s_addr)
-       if (ip->ip_src.s_addr == ignore_addr.s_addr)
-           return 0;
+    struct in6_addr s_addr;
+    struct in6_addr d_addr;
+
+    if (ip->ip_v == 6)
+       return (handle_ipv6 ((struct ip6_hdr *) ip, len));
+
+    in6_addr_from_buffer (&s_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET);
+    in6_addr_from_buffer (&d_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET);
+    if (ignore_list_match (&s_addr))
+           return (0);
     if (IPPROTO_UDP != ip->ip_p)
        return 0;
     memcpy(buf, (void *) ip + offset, len - offset);
-    if (0 == handle_udp((struct udphdr *) buf, len - offset, ip->ip_src, ip->ip_dst))
+    if (0 == handle_udp((struct udphdr *) buf, len - offset, &s_addr, &d_addr))
        return 0;
-    clt = AgentAddr_lookup_or_add(&Sources, ip->ip_src);
-    clt->count++;
-    srv = AgentAddr_lookup_or_add(&Destinations, ip->ip_dst);
-    srv->count++;
     return 1;
 }
 
-#if USE_PPP
 static int
 handle_ppp(const u_char * pkt, int len)
 {
@@ -469,8 +510,6 @@ handle_ppp(const u_char * pkt, int len)
     return handle_ip((struct ip *) buf, len);
 }
 
-#endif
-
 static int
 handle_null(const u_char * pkt, int len)
 {
@@ -509,8 +548,6 @@ handle_ether(const u_char * pkt, int len)
     char buf[PCAP_SNAPLEN];
     struct ether_header *e = (void *) pkt;
     unsigned short etype = ntohs(e->ether_type);
-    fprintf (stderr, "handle_ether (pkt = %p, len = %i)\n",
-                   (void *) pkt, len);
     if (len < ETHER_HDR_LEN)
        return 0;
     pkt += ETHER_HDR_LEN;
@@ -520,10 +557,14 @@ handle_ether(const u_char * pkt, int len)
        pkt += 4;
        len -= 4;
     }
-    if (ETHERTYPE_IP != etype)
+    if ((ETHERTYPE_IP != etype)
+           && (ETHERTYPE_IPV6 != etype))
        return 0;
     memcpy(buf, pkt, len);
-    return handle_ip((struct ip *) buf, len);
+    if (ETHERTYPE_IPV6 == etype)
+       return (handle_ipv6 ((struct ip6_hdr *) buf, len));
+    else
+       return handle_ip((struct ip *) buf, len);
 }
 
 /* public function */
@@ -538,16 +579,14 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt
     if (hdr->caplen < ETHER_HDR_LEN)
        return;
 
-    switch (pcap_datalink (pcap))
+    switch (pcap_datalink (pcap_obj))
     {
        case DLT_EN10MB:
            status = handle_ether (pkt, hdr->caplen);
            break;
-#if USE_PPP
        case DLT_PPP:
            status = handle_ppp (pkt, hdr->caplen);
            break;
-#endif
 #ifdef DLT_LOOP
        case DLT_LOOP:
            status = handle_loop (pkt, hdr->caplen);
@@ -564,10 +603,10 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt
 
        default:
            fprintf (stderr, "unsupported data link type %d\n",
-                   pcap_datalink(pcap));
+                   pcap_datalink(pcap_obj));
            status = 0;
            break;
-    } /* switch (pcap_datalink(pcap)) */
+    } /* switch (pcap_datalink(pcap_obj)) */
 
     if (0 == status)
        return;
@@ -577,62 +616,68 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt
     last_ts = hdr->ts;
 }
 
-char *
-qtype_str(int t)
+const char *qtype_str(int t)
 {
-    static char buf[30];
+    static char buf[32];
     switch (t) {
-    case T_A:
-       return "A";
-       break;
-    case T_NS:
-       return "NS";
-       break;
-    case T_CNAME:
-       return "CNAME";
-       break;
-    case T_SOA:
-       return "SOA";
-       break;
-    case T_PTR:
-       return "PTR";
-       break;
-    case T_MX:
-       return "MX";
-       break;
-    case T_TXT:
-       return "TXT";
-       break;
-    case T_SIG:
-       return "SIG";
-       break;
-    case T_KEY:
-       return "KEY";
-       break;
-    case T_AAAA:
-       return "AAAA";
-       break;
-    case T_LOC:
-       return "LOC";
-       break;
-    case T_SRV:
-       return "SRV";
-       break;
-    case T_A6:
-       return "A6";
-       break;
-    case T_ANY:
-       return "ANY";
-       break;
-    default:
-       snprintf(buf, 30, "#%d", t);
-       return buf;
-    }
+           case ns_t_a:        return ("A");
+           case ns_t_ns:       return ("NS");
+           case ns_t_md:       return ("MD");
+           case ns_t_mf:       return ("MF");
+           case ns_t_cname:    return ("CNAME");
+           case ns_t_soa:      return ("SOA");
+           case ns_t_mb:       return ("MB");
+           case ns_t_mg:       return ("MG");
+           case ns_t_mr:       return ("MR");
+           case ns_t_null:     return ("NULL");
+           case ns_t_wks:      return ("WKS");
+           case ns_t_ptr:      return ("PTR");
+           case ns_t_hinfo:    return ("HINFO");
+           case ns_t_minfo:    return ("MINFO");
+           case ns_t_mx:       return ("MX");
+           case ns_t_txt:      return ("TXT");
+           case ns_t_rp:       return ("RP");
+           case ns_t_afsdb:    return ("AFSDB");
+           case ns_t_x25:      return ("X25");
+           case ns_t_isdn:     return ("ISDN");
+           case ns_t_rt:       return ("RT");
+           case ns_t_nsap:     return ("NSAP");
+           case ns_t_nsap_ptr: return ("NSAP-PTR");
+           case ns_t_sig:      return ("SIG");
+           case ns_t_key:      return ("KEY");
+           case ns_t_px:       return ("PX");
+           case ns_t_gpos:     return ("GPOS");
+           case ns_t_aaaa:     return ("AAAA");
+           case ns_t_loc:      return ("LOC");
+           case ns_t_nxt:      return ("NXT");
+           case ns_t_eid:      return ("EID");
+           case ns_t_nimloc:   return ("NIMLOC");
+           case ns_t_srv:      return ("SRV");
+           case ns_t_atma:     return ("ATMA");
+           case ns_t_naptr:    return ("NAPTR");
+           case ns_t_kx:       return ("KX");
+           case ns_t_cert:     return ("CERT");
+           case ns_t_a6:       return ("A6");
+           case ns_t_dname:    return ("DNAME");
+           case ns_t_sink:     return ("SINK");
+           case ns_t_opt:      return ("OPT");
+           case ns_t_tsig:     return ("TSIG");
+           case ns_t_ixfr:     return ("IXFR");
+           case ns_t_axfr:     return ("AXFR");
+           case ns_t_mailb:    return ("MAILB");
+           case ns_t_maila:    return ("MAILA");
+           case ns_t_any:      return ("ANY");
+           case ns_t_zxfr:     return ("ZXFR");
+           default:
+                   snprintf (buf, 32, "#%i", t);
+                   buf[31] = '\0';
+                   return (buf);
+    }; /* switch (t) */
     /* NOTREACHED */
+    return (NULL);
 }
 
-char *
-opcode_str(int o)
+const char *opcode_str (int o)
 {
     static char buf[30];
     switch (o) {
@@ -658,6 +703,35 @@ opcode_str(int o)
     /* NOTREACHED */
 }
 
+const char *rcode_str (int rcode)
+{
+       static char buf[32];
+       switch (rcode)
+       {
+               case ns_r_noerror:  return ("NOERROR");
+               case ns_r_formerr:  return ("FORMERR");
+               case ns_r_servfail: return ("SERVFAIL");
+               case ns_r_nxdomain: return ("NXDOMAIN");
+               case ns_r_notimpl:  return ("NOTIMPL");
+               case ns_r_refused:  return ("REFUSED");
+               case ns_r_yxdomain: return ("YXDOMAIN");
+               case ns_r_yxrrset:  return ("YXRRSET");
+               case ns_r_nxrrset:  return ("NXRRSET");
+               case ns_r_notauth:  return ("NOTAUTH");
+               case ns_r_notzone:  return ("NOTZONE");
+               case ns_r_max:      return ("MAX");
+               case ns_r_badsig:   return ("BADSIG");
+               case ns_r_badkey:   return ("BADKEY");
+               case ns_r_badtime:  return ("BADTIME");
+               default:
+                       snprintf (buf, 32, "RCode%i", rcode);
+                       buf[31] = '\0';
+                       return (buf);
+       }
+       /* Never reached */
+       return (NULL);
+} /* const char *rcode_str (int rcode) */
+
 #if 0
 static int
 main(int argc, char *argv[])
@@ -713,11 +787,11 @@ main(int argc, char *argv[])
     if (0 == stat(device, &sb))
        readfile_state = 1;
     if (readfile_state) {
-       pcap = pcap_open_offline(device, errbuf);
+       pcap_obj = pcap_open_offline(device, errbuf);
     } else {
-       pcap = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
+       pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
     }
-    if (NULL == pcap) {
+    if (NULL == pcap_obj) {
        fprintf(stderr, "pcap_open_*: %s\n", errbuf);
        exit(1);
     }
@@ -732,12 +806,12 @@ main(int argc, char *argv[])
     }
 
     memset(&fp, '\0', sizeof(fp));
-    x = pcap_compile(pcap, &fp, bpf_program_str, 1, 0);
+    x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0);
     if (x < 0) {
        fprintf(stderr, "pcap_compile failed\n");
        exit(1);
     }
-    x = pcap_setfilter(pcap, &fp);
+    x = pcap_setfilter(pcap_obj, &fp);
     if (x < 0) {
        fprintf(stderr, "pcap_setfilter failed\n");
        exit(1);
@@ -747,13 +821,13 @@ main(int argc, char *argv[])
      * non-blocking call added for Mac OS X bugfix.  Sent by Max Horn.
      * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
      */
-    x = pcap_setnonblock(pcap, 1, errbuf);
+    x = pcap_setnonblock(pcap_obj, 1, errbuf);
     if (x < 0) {
        fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
        exit(1);
     }
 
-    switch (pcap_datalink(pcap)) {
+    switch (pcap_datalink(pcap_obj)) {
     case DLT_EN10MB:
        handle_datalink = handle_ether;
        break;
@@ -777,7 +851,7 @@ main(int argc, char *argv[])
        break;
     default:
        fprintf(stderr, "unsupported data link type %d\n",
-           pcap_datalink(pcap));
+           pcap_datalink(pcap_obj));
        return 1;
        break;
     }
@@ -792,8 +866,8 @@ main(int argc, char *argv[])
                 * anyway.
                 */
                if (0 == readfile_state)        /* interactive */
-                   pcap_select(pcap, 1, 0);
-               x = pcap_dispatch(pcap, 50, handle_pcap, NULL);
+                   pcap_select(pcap_obj, 1, 0);
+               x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL);
            }
            if (0 == x && 1 == readfile_state) {
                /* block on keyboard until user quits */
@@ -807,7 +881,7 @@ main(int argc, char *argv[])
        }
        endwin();               /* klin, Thu Nov 28 08:56:51 2002 */
     } else {
-       while (pcap_dispatch(pcap, 50, handle_pcap, NULL))
+       while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL))
                (void) 0;
        cron_pre();
        Sources_report(); print_func("\n");
@@ -820,7 +894,7 @@ main(int argc, char *argv[])
        SldBySource_report();
     }
 
-    pcap_close(pcap);
+    pcap_close(pcap_obj);
     return 0;
 } /* static int main(int argc, char *argv[]) */
 #endif
index 666140e..8408147 100644 (file)
@@ -1,3 +1,5 @@
+#ifndef COLLECTD_DNSTOP
+#define COLLECTD_DNSTOP 1
 /*
  * collectd - src/dnstop.c
  * Copyright (C) 2006  Florian octo Forster
 #include <pcap.h>
 
 #define T_MAX 65536
+#define OP_MAX 16
+#define C_MAX 65536
+#define MAX_QNAME_SZ 512
+
+struct rfc1035_header_s {
+    uint16_t id;
+    unsigned int qr:1;
+    unsigned int opcode:4;
+    unsigned int aa:1;
+    unsigned int tc:1;
+    unsigned int rd:1;
+    unsigned int ra:1;
+    unsigned int z:1;
+    unsigned int ad:1;
+    unsigned int cd:1;
+    unsigned int rcode:4;
+    uint16_t qdcount;
+    uint16_t ancount;
+    uint16_t nscount;
+    uint16_t arcount;
+    uint16_t qtype;
+    uint16_t qclass;
+    char     qname[MAX_QNAME_SZ];
+    uint16_t length;
+};
+typedef struct rfc1035_header_s rfc1035_header_t;
+
 extern int qtype_counts[T_MAX];
+extern int opcode_counts[OP_MAX];
+extern int qclass_counts[C_MAX];
+
+void dnstop_set_pcap_obj (pcap_t *po);
+void dnstop_set_callback (void (*cb) (const rfc1035_header_t *));
 
+void ignore_list_add_name (const char *name);
 void handle_pcap (u_char * udata, const struct pcap_pkthdr *hdr, const u_char * pkt);
+
+const char *qtype_str(int t);
+const char *opcode_str(int o);
+const char *rcode_str (int r);
+
+#endif /* !COLLECTD_DNSTOP */
index ca50902..6177827 100644 (file)
 # define NAMED_HAVE_READ 0
 #endif
 
-static char *qtype_file = "named/qtype-%s.rrd";
+struct counter_list_s
+{
+       unsigned int key;
+       unsigned int value;
+       struct counter_list_s *next;
+};
+typedef struct counter_list_s counter_list_t;
+
+static char *qtype_file   = "named/qtype-%s.rrd";
+static char *opcode_file  = "named/opcode-%s.rrd";
+static char *rcode_file   = "named/rcode-%s.rrd";
 
 static char *qtype_ds_def[] =
 {
-       "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:U",
+       "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 NAMED_HAVE_CONFIG
 #if HAVE_LIBPCAP
 static char *config_keys[] =
 {
        "Interface",
+       "IgnoreSource",
        NULL
 };
-static int config_keys_num = 1;
+static int config_keys_num = 2;
 #endif /* HAVE_LIBPCAP */
 #endif /* NAMED_HAVE_CONFIG */
 
@@ -69,54 +94,108 @@ static int config_keys_num = 1;
 #define PCAP_SNAPLEN 1460
 static char   *pcap_device = NULL;
 static int     pipe_fd = -1;
+
+static counter_list_t *qtype_list;
+static counter_list_t *opcode_list;
+static counter_list_t *rcode_list;
 #endif
 
-#if NAMED_HAVE_CONFIG
-static int named_config (char *key, char *value)
+static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
 {
-#if HAVE_LIBPCAP
-       if (strcasecmp (key, "Interface") == 0)
+       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);
+}
+
+static counter_list_t *counter_list_create (counter_list_t **list,
+               unsigned int key, unsigned int value)
+{
+       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);
+
+       memset (entry, 0, sizeof (counter_list_t));
+       entry->key = key;
+       entry->value = value;
+
+       if (*list == NULL)
        {
-               if (pcap_device != NULL)
-                       free (pcap_device);
-               if ((pcap_device = strdup (value)) == NULL)
-                       return (1);
+               *list = entry;
        }
        else
        {
-               return (-1);
+               counter_list_t *last;
+
+               last = *list;
+               while (last->next != NULL)
+                       last = last->next;
+
+               last->next = entry;
        }
 
-       return (0);
-#endif /* HAVE_LIBPCAP */
+       DBG ("return (%p)", (void *) entry);
+       return (entry);
 }
-#endif /* NAMED_HAVE_CONFIG */
 
-static int named_child_send_data (void)
+static void counter_list_add (counter_list_t **list,
+               unsigned int key, unsigned int increment)
 {
-       int values[2 * T_MAX];
-       int values_num;
-       int i;
+       counter_list_t *entry;
 
-       if (pipe_fd < 0)
+       DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
+                       (void *) *list, key, increment);
+
+       entry = counter_list_search (list, key);
+
+       if (entry != NULL)
+       {
+               entry->value += increment;
+       }
+       else
+       {
+               counter_list_create (list, key, increment);
+       }
+       DBG ("return ()");
+}
+
+static int counter_list_send (counter_list_t *list, int fd)
+{
+       counter_list_t *cl;
+       unsigned int values[2 * T_MAX];
+       unsigned int values_num;
+
+       if (fd < 0)
                return (-1);
 
        values_num = 0;
-       for (i = 0; i < T_MAX; i++)
+
+       for (cl = list;
+                       (cl != NULL) && (values_num < T_MAX);
+                       cl = cl->next)
        {
-               if (qtype_counts[i] != 0)
-               {
-                       values[2 * values_num] = i;
-                       values[(2 * values_num) + 1] = qtype_counts[i];
-                       values_num++;
-               }
+               values[2 * values_num] = cl->key;
+               values[(2 * values_num) + 1] = cl->value;
+               values_num++;
        }
 
-       DBG ("swrite (pipe_fd = %i, values_num = %i)", pipe_fd, values_num);
-       if (swrite (pipe_fd, (const void *) &values_num, sizeof (values_num)) != 0)
+       DBG ("swrite (fd = %i, values_num = %i)", fd, values_num);
+       if (swrite (fd, (const void *) &values_num, sizeof (values_num)) != 0)
        {
-               DBG ("Writing to pipe failed: %s", strerror (errno));
-               syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
+               DBG ("Writing to fd failed: %s", strerror (errno));
+               syslog (LOG_ERR, "named plugin: Writing to fd failed: %s",
                                strerror (errno));
                return (-1);
        }
@@ -124,8 +203,9 @@ static int named_child_send_data (void)
        if (values_num == 0)
                return (0);
 
-       DBG ("swrite (pipe_fd = %i, values = %p, size = %i)", pipe_fd, (void *) values, (int) (sizeof (int) * values_num));
-       if (swrite (pipe_fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
+       DBG ("swrite (fd = %i, values = %p, size = %i)",
+                       fd, (void *) values, (int) (sizeof (int) * values_num));
+       if (swrite (fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
        {
                DBG ("Writing to pipe failed: %s", strerror (errno));
                syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
@@ -135,6 +215,48 @@ static int named_child_send_data (void)
 
        return (values_num);
 }
+#if NAMED_HAVE_CONFIG
+static int named_config (char *key, char *value)
+{
+#if HAVE_LIBPCAP
+       if (strcasecmp (key, "Interface") == 0)
+       {
+               if (pcap_device != NULL)
+                       free (pcap_device);
+               if ((pcap_device = strdup (value)) == NULL)
+                       return (1);
+       }
+       else if (strcasecmp (key, "IgnoreSource") == 0)
+       {
+               if (value != NULL)
+                       ignore_list_add_name (value);
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+#endif /* HAVE_LIBPCAP */
+}
+#endif /* NAMED_HAVE_CONFIG */
+
+static void named_child_callback (const rfc1035_header_t *dns)
+{
+       if (dns->qr == 0)
+       {
+               /* This is a query */
+               counter_list_add (&qtype_list,  dns->qtype,  1);
+       }
+       else
+       {
+               /* This is a reply */
+               counter_list_add (&rcode_list,  dns->rcode,  1);
+       }
+
+       /* FIXME: Are queries, replies or both interesting? */
+       counter_list_add (&opcode_list, dns->opcode, 1);
+}
 
 static void named_child_loop (void)
 {
@@ -161,10 +283,9 @@ static void named_child_loop (void)
                pipe_fd = -1;
                return;
        }
-       pcap = pcap_obj; /* FIXME: This is used by `handle_pcap' */
 
        memset (&fp, 0, sizeof (fp));
-       if (pcap_compile (pcap_obj, &fp, "udp dst port 53", 1, 0) < 0)
+       if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
        {
                DBG ("pcap_compile failed");
                syslog (LOG_ERR, "named plugin: pcap_compile failed");
@@ -183,6 +304,9 @@ static void named_child_loop (void)
 
        DBG ("PCAP object created.");
 
+       dnstop_set_pcap_obj (pcap_obj);
+       dnstop_set_callback (named_child_callback);
+
        /* Set up pipe end */
        poll_fds[0].fd = pipe_fd;
        poll_fds[0].events = POLLOUT;
@@ -191,7 +315,7 @@ static void named_child_loop (void)
        poll_fds[1].fd = pcap_fileno (pcap_obj);
        poll_fds[1].events = POLLIN | POLLPRI;
 
-       while (42)
+       while (pipe_fd > 0)
        {
                DBG ("poll (...)");
                status = poll (poll_fds, 2, -1 /* wait forever for a change */);
@@ -211,11 +335,10 @@ static void named_child_loop (void)
                }
                else if (poll_fds[0].revents & POLLOUT)
                {
-                       DBG ("Calling `named_child_send_data'");
-                       if (named_child_send_data () < 0)
-                       {
-                               break;
-                       }
+                       DBG ("Sending data..");
+                       counter_list_send (qtype_list, pipe_fd);
+                       counter_list_send (opcode_list, pipe_fd);
+                       counter_list_send (rcode_list, pipe_fd);
                }
 
                if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
@@ -226,9 +349,8 @@ static void named_child_loop (void)
                }
                else if (poll_fds[1].revents & (POLLIN | POLLPRI))
                {
-                       /* TODO: Read and analyse packet */
                        status = pcap_dispatch (pcap_obj,
-                                       1 /* Only handle 10 packets at a time */,
+                                       10 /* Only handle 10 packets at a time */,
                                        handle_pcap /* callback */,
                                        NULL /* Whatever this means.. */);
                        if (status < 0)
@@ -305,66 +427,147 @@ static void qtype_write (char *host, char *inst, char *val)
        rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
 }
 
-static void qtype_submit (int qtype_int, unsigned int counter)
+static void rcode_write (char *host, char *inst, char *val)
+{
+       char file[512];
+       int status;
+
+       status = snprintf (file, 512, rcode_file, inst);
+       if (status < 1)
+               return;
+       else if (status >= 512)
+               return;
+
+       rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
+}
+
+static void opcode_write (char *host, char *inst, char *val)
+{
+       char file[512];
+       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);
+}
+
+static void qtype_submit (int qtype, unsigned int counter)
+{
+       char inst[32];
+       char buffer[32];
+       int  status;
+
+       strncpy (inst, qtype_str (qtype), 32);
+       inst[31] = '\0';
+
+       status = snprintf (buffer, 32, "N:%u", counter);
+       if ((status < 1) || (status >= 32))
+               return;
+
+       plugin_submit ("named_qtype", inst, buffer);
+}
+
+static void rcode_submit (int rcode, unsigned int counter)
+{
+       char inst[32];
+       char buffer[32];
+       int  status;
+
+       strncpy (inst, rcode_str (rcode), 32);
+       inst[31] = '\0';
+
+       status = snprintf (buffer, 32, "N:%u", counter);
+       if ((status < 1) || (status >= 32))
+               return;
+
+       plugin_submit ("named_rcode", inst, buffer);
+}
+
+static void opcode_submit (int opcode, unsigned int counter)
 {
-       char *qtype_char;
-       char  buffer[32];
-       int   status;
+       char inst[32];
+       char buffer[32];
+       int  status;
 
-       qtype_char = qtype_str (qtype_int);
+       strncpy (inst, opcode_str (opcode), 32);
+       inst[31] = '\0';
 
-       status = snprintf (buffer, 32, "N:%i", counter);
+       status = snprintf (buffer, 32, "N:%u", counter);
        if ((status < 1) || (status >= 32))
                return;
 
-       plugin_submit ("named_qtype", qtype_char, buffer);
+       plugin_submit ("named_opcode", inst, buffer);
 }
 
 #if NAMED_HAVE_READ
-static void named_read (void)
+static unsigned int named_read_array (unsigned int *values)
 {
-       int values[2 * T_MAX];
-       int values_num;
-       int qtype;
-       int counter;
-       int i;
+       unsigned int values_num;
 
        if (pipe_fd < 0)
-               return;
+               return (0);
 
-       DBG ("Reading from pipe_fd = %i..", pipe_fd);
        if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
        {
+               DBG ("Reading from the pipe failed: %s",
+                               strerror (errno));
                syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
                                strerror (errno));
                pipe_fd = -1;
-               return;
+               return (0);
        }
+       DBG ("sread (pipe_fd = %i, values_num = %u)", pipe_fd, values_num);
 
-       assert ((values_num >= 0) && (values_num <= T_MAX));
+       assert (values_num <= T_MAX);
 
        if (values_num == 0)
-       {
-               DBG ("No values available; returning");
-               return;
-       }
+               return (0);
 
-       DBG ("Reading %i qtype/values from pipe_fd = %i..", values_num, pipe_fd);
-       if (sread (pipe_fd, (void *) values, 2 * sizeof (int) * values_num) != 0)
+       if (sread (pipe_fd, (void *) values, 2 * sizeof (unsigned int) * values_num) != 0)
        {
+               DBG ("Reading from the pipe failed: %s",
+                               strerror (errno));
                syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
                                strerror (errno));
                pipe_fd = -1;
+               return (0);
+       }
+
+       return (values_num);
+}
+
+static void named_read (void)
+{
+       unsigned int values[2 * T_MAX];
+       unsigned int values_num;
+       int i;
+
+       if (pipe_fd < 0)
                return;
+
+       values_num = named_read_array (values);
+       for (i = 0; i < values_num; i++)
+       {
+               DBG ("qtype = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
+               qtype_submit (values[2 * i], values[(2 * i) + 1]);
        }
 
+       values_num = named_read_array (values);
        for (i = 0; i < values_num; i++)
        {
-               qtype = values[2 * i];
-               counter = values[(2 * i) + 1];
+               DBG ("opcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
+               opcode_submit (values[2 * i], values[(2 * i) + 1]);
+       }
 
-               DBG ("qtype = %i; counter = %i;", qtype, counter);
-               qtype_submit (qtype, counter);
+       values_num = named_read_array (values);
+       for (i = 0; i < values_num; i++)
+       {
+               DBG ("rcode = %u; counter = %u;", values[2 * i], values[(2 * i) + 1]);
+               rcode_submit (values[2 * i], values[(2 * i) + 1]);
        }
 }
 #else /* if !NAMED_HAVE_READ */
@@ -375,7 +578,8 @@ void module_register (void)
 {
        plugin_register (MODULE_NAME, named_init, named_read, NULL);
        plugin_register ("named_qtype", NULL, NULL, qtype_write);
-       /* TODO */
+       plugin_register ("named_rcode", NULL, NULL, rcode_write);
+       plugin_register ("named_opcode", NULL, NULL, opcode_write);
        cf_register (MODULE_NAME, named_config, config_keys, config_keys_num);
 }