mirror of
https://github.com/NLnetLabs/unbound.git
synced 2024-09-21 14:47:09 +00:00
50b728aa08
git-svn-id: file:///svn/unbound/trunk@1250 be551aaa-1e26-0410-a405-d3ace91eadb9
393 lines
12 KiB
Bash
Executable File
393 lines
12 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# plugin for munin to monitor usage of unbound servers.
|
|
#
|
|
# (C) 2008 W.C.A. Wijngaards. BSD Licensed.
|
|
#
|
|
# To install; enable statistics and unbound-control in unbound.conf
|
|
# server: extended-statistics: yes
|
|
# statistics-cumulative: no
|
|
# statistics-interval: 0
|
|
# remote-control: control-enable: yes
|
|
#
|
|
# Environment variables
|
|
# statefile - where to put temporary statefile.
|
|
# unbound_conf - where the unbound.conf file is located.
|
|
# unbound_control - where to find unbound-control executable.
|
|
# spoof_warn - what level to warn about spoofing
|
|
# spoof_crit - what level to crit about spoofing
|
|
#
|
|
# You can set them in your munin/plugin-conf.d/plugins.conf file
|
|
# with:
|
|
# [unbound*]
|
|
# user root
|
|
# env.statefile /usr/local/var/munin/plugin-state/unbound-state
|
|
# env.unbound_conf /usr/local/etc/unbound/unbound.conf
|
|
# env.unbound_control /usr/local/sbin/unbound-control
|
|
# env.spoof_warn 1000
|
|
# env.spoof_crit 100000
|
|
#
|
|
# This plugin can create different graphs depending on what name
|
|
# you link it as (with ln -s) into the plugins directory
|
|
# You can link it multiple times.
|
|
# If you are only a casual user, the _hits and _by_type are most interesting,
|
|
# possibly followed by _by_rcode.
|
|
#
|
|
# unbound_munin_hits - base volume, cache hits, unwanted traffic
|
|
# unbound_munin_queue - to monitor the internal requestlist
|
|
# unbound_munin_memory - memory usage
|
|
# unbound_munin_by_type - incoming queries by type
|
|
# unbound_munin_by_class - incoming queries by class
|
|
# unbound_munin_by_opcode - incoming queries by opcode
|
|
# unbound_munin_by_rcode - answers by rcode, validation status
|
|
# unbound_munin_by_flags - incoming queries by flags
|
|
#
|
|
# Magic markers - optional - used by installation scripts and
|
|
# munin-config:
|
|
#
|
|
#%# family=contrib
|
|
#%# capabilities=autoconf suggest
|
|
|
|
# POD documentation
|
|
: <<=cut
|
|
=head1 NAME
|
|
|
|
unbound_munin_ - Munin plugin to monitor the Unbound DNS resolver.
|
|
|
|
=head1 APPLICABLE SYSTEMS
|
|
|
|
System with unbound daemon.
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
[unbound*]
|
|
user root
|
|
env.statefile /usr/local/var/munin/plugin-state/unbound-state
|
|
env.unbound_conf /usr/local/etc/unbound/unbound.conf
|
|
env.unbound_control /usr/local/sbin/unbound-control
|
|
env.spoof_warn 1000
|
|
env.spoof_crit 100000
|
|
|
|
Use the .env settings to override the defaults.
|
|
|
|
=head1 USAGE
|
|
|
|
Can be used to present different graphs. Use ln -s for that name in
|
|
the plugins directory to enable the graph.
|
|
unbound_munin_hits - base volume, cache hits, unwanted traffic
|
|
unbound_munin_queue - to monitor the internal requestlist
|
|
unbound_munin_memory - memory usage
|
|
unbound_munin_by_type - incoming queries by type
|
|
unbound_munin_by_class - incoming queries by class
|
|
unbound_munin_by_opcode - incoming queries by opcode
|
|
unbound_munin_by_rcode - answers by rcode, validation status
|
|
unbound_munin_by_flags - incoming queries by flags
|
|
|
|
=head1 AUTHOR
|
|
|
|
Copyright 2008 W.C.A. Wijngaards
|
|
|
|
=head1 LICENSE
|
|
|
|
BSD
|
|
|
|
=cut
|
|
|
|
state=${statefile:-/usr/local/var/munin/plugin-state/unbound-state}
|
|
conf=${unbound_conf:-/usr/local/etc/unbound/unbound.conf}
|
|
ctrl=${unbound_control:-/usr/local/sbin/unbound-control}
|
|
warn=${spoof_warn:-1000}
|
|
crit=${spoof_crit:-100000}
|
|
|
|
# number of seconds between polling attempts.
|
|
# makes the statefile hang around for at least this many seconds,
|
|
# so that multiple links of this script can share the results.
|
|
lee=55
|
|
|
|
# to keep things within 19 characters
|
|
ABBREV="-e s/total/t/ -e s/thread/t/ -e s/num/n/ -e s/query/q/ -e s/answer/a/ -e s/unwanted/u/ -e s/requestlist/ql/ -e s/type/t/ -e s/class/c/ -e s/opcode/o/ -e s/rcode/r/ -e s/edns/e/ -e s/mem/m/ -e s/cache/c/ -e s/mod/m/"
|
|
|
|
# get value from $1 into return variable $value
|
|
get_value ( ) {
|
|
value="`grep '^'$1'=' $state | sed -e 's/^.*=//'`"
|
|
if test "$value"x = ""x; then
|
|
value="0"
|
|
fi
|
|
}
|
|
|
|
# download the state from the unbound server.
|
|
get_state ( ) {
|
|
# do not refetch if the file exists and only LEE seconds old
|
|
if test -f $state; then
|
|
now=`date +%s`
|
|
get_value "time.now"
|
|
value="`echo $value | sed -e 's/\..*$//'`"
|
|
if test $now -lt `expr $value + $lee`; then
|
|
return
|
|
fi
|
|
fi
|
|
$ctrl -c $conf stats > $state
|
|
if test $? -ne 0; then
|
|
echo "error retrieving data from unbound server"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
if test "$1" = "autoconf" ; then
|
|
if test ! -f $conf; then
|
|
echo no "($conf does not exist)"
|
|
exit 1
|
|
fi
|
|
if test ! -d `dirname $state`; then
|
|
echo no "($state directory does not exist)"
|
|
exit 1
|
|
fi
|
|
echo yes
|
|
exit 0
|
|
fi
|
|
|
|
if test "$1" = "suggest" ; then
|
|
echo "hits"
|
|
echo "queue"
|
|
echo "memory"
|
|
echo "by_type"
|
|
echo "by_class"
|
|
echo "by_opcode"
|
|
echo "by_rcode"
|
|
echo "by_flags"
|
|
exit 0
|
|
fi
|
|
|
|
# determine my type, by name
|
|
id=`echo $0 | sed -e 's/^.*unbound_munin_//'`
|
|
if test "$id"x = ""x; then
|
|
# some default to keep people sane.
|
|
id="hits"
|
|
fi
|
|
|
|
# if $1 exists in statefile, config is echoed with label $2
|
|
exist_config ( ) {
|
|
mn=`echo $1 | sed $ABBREV | tr . _`
|
|
if grep '^'$1'=' $state >/dev/null 2>&1; then
|
|
echo "$mn.label $2"
|
|
echo "$mn.min 0"
|
|
fi
|
|
}
|
|
|
|
# print label and min 0 for a name $1 in unbound format
|
|
p_config ( ) {
|
|
mn=`echo $1 | sed $ABBREV | tr . _`
|
|
echo $mn.label "$2"
|
|
echo $mn.min 0
|
|
}
|
|
|
|
if test "$1" = "config" ; then
|
|
if test ! -f $state; then
|
|
get_state
|
|
fi
|
|
case $id in
|
|
hits)
|
|
echo "graph_title Unbound DNS traffic and cache hits"
|
|
echo "graph_args --base 1000 -l 0"
|
|
echo "graph_vlabel queries / second"
|
|
echo "graph_category DNS"
|
|
for x in thread0.num.queries thread1.num.queries \
|
|
thread2.num.queries thread3.num.queries thread4.num.queries \
|
|
thread5.num.queries thread6.num.queries thread7.num.queries; do
|
|
exist_config $x "queries handled by `basename $x .num.queries`"
|
|
done
|
|
p_config "total.num.queries" "total queries from clients"
|
|
p_config "total.num.cachehits" "cache hits"
|
|
p_config "num.query.tcp" "TCP queries"
|
|
p_config "unwanted.queries" "queries that failed acl"
|
|
p_config "unwanted.replies" "unwanted or unsolicited replies"
|
|
echo "u_replies.warning $warn"
|
|
echo "u_replies.critical $crit"
|
|
echo "graph_info DNS queries to the recursive resolver. The unwanted replies could be innocent duplicate packets, late replies, or spoof threats."
|
|
;;
|
|
queue)
|
|
echo "graph_title Unbound requestlist size"
|
|
echo "graph_args --base 1000 -l 0"
|
|
echo "graph_vlabel number of queries"
|
|
echo "graph_category DNS"
|
|
p_config "total.requestlist.avg" "Average size of queue on insert"
|
|
p_config "total.requestlist.max" "Max size of queue (in 5 min)"
|
|
p_config "total.requestlist.overwritten" "Number of queries replaced by new ones"
|
|
p_config "total.requestlist.exceeded" "Number of queries dropped due to lack of space"
|
|
echo "graph_info The queries that did not hit the cache and need recursion service take up space in the requestlist. If there are too many queries, first queries get overwritten, and at last resort dropped."
|
|
;;
|
|
memory)
|
|
echo "graph_title Unbound memory usage"
|
|
echo "graph_args --base 1024 -l 0"
|
|
echo "graph_vlabel memory used in bytes"
|
|
echo "graph_category DNS"
|
|
p_config "mem.total.sbrk" "Total memory"
|
|
p_config "mem.cache.rrset" "RRset cache memory"
|
|
p_config "mem.cache.message" "Message cache memory"
|
|
p_config "mem.mod.iterator" "Iterator module memory"
|
|
p_config "mem.mod.validator" "Validator module and key cache memory"
|
|
echo "graph_info The memory used by unbound."
|
|
;;
|
|
by_type)
|
|
echo "graph_title Unbound DNS queries by type"
|
|
echo "graph_args --base 1000 -l 0"
|
|
echo "graph_vlabel queries per second"
|
|
echo "graph_category DNS"
|
|
for x in `grep "^num.query.type" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
tp=`echo $nm | sed -e s/num.query.type.//`
|
|
p_config "$nm" "$tp"
|
|
done
|
|
echo "graph_info queries by DNS RR type queried for"
|
|
;;
|
|
by_class)
|
|
echo "graph_title Unbound DNS queries by class"
|
|
echo "graph_args --base 1000 -l 0"
|
|
echo "graph_vlabel queries per second"
|
|
echo "graph_category DNS"
|
|
for x in `grep "^num.query.class" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
tp=`echo $nm | sed -e s/num.query.class.//`
|
|
p_config "$nm" "$tp"
|
|
done
|
|
echo "graph_info queries by DNS RR class queried for."
|
|
;;
|
|
by_opcode)
|
|
echo "graph_title Unbound DNS queries by opcode"
|
|
echo "graph_args --base 1000 -l 0"
|
|
echo "graph_vlabel queries per second"
|
|
echo "graph_category DNS"
|
|
for x in `grep "^num.query.opcode" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
tp=`echo $nm | sed -e s/num.query.opcode.//`
|
|
p_config "$nm" "$tp"
|
|
done
|
|
echo "graph_info queries by opcode in the query packet."
|
|
;;
|
|
by_rcode)
|
|
echo "graph_title Unbound DNS answers by return code"
|
|
echo "graph_args --base 1000 -l 0"
|
|
echo "graph_vlabel queries per second"
|
|
echo "graph_category DNS"
|
|
for x in `grep "^num.answer.rcode" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
tp=`echo $nm | sed -e s/num.answer.rcode.//`
|
|
p_config "$nm" "$tp"
|
|
done
|
|
p_config "num.answer.secure" "answer secure"
|
|
p_config "num.answer.bogus" "answer bogus"
|
|
p_config "num.rrset.bogus" "num rrsets marked bogus"
|
|
echo "graph_info answers sorted by return value. rrsets bogus is the number of rrsets marked bogus per second by the validator"
|
|
;;
|
|
by_flags)
|
|
echo "graph_title Unbound DNS incoming queries by flags"
|
|
echo "graph_args --base 1000 -l 0"
|
|
echo "graph_vlabel queries per second"
|
|
echo "graph_category DNS"
|
|
p_config "num.query.flags.QR" "QR (query reply) flag"
|
|
p_config "num.query.flags.AA" "AA (auth answer) flag"
|
|
p_config "num.query.flags.TC" "TC (truncated) flag"
|
|
p_config "num.query.flags.RD" "RD (recursion desired) flag"
|
|
p_config "num.query.flags.RA" "RA (rec avail) flag"
|
|
p_config "num.query.flags.Z" "Z (zero) flag"
|
|
p_config "num.query.flags.AD" "AD (auth data) flag"
|
|
p_config "num.query.flags.CD" "CD (check disabled) flag"
|
|
p_config "num.query.edns.present" "EDNS OPT present"
|
|
p_config "num.query.edns.DO" "DO (DNSSEC OK) flag"
|
|
echo "graph_info This graphs plots the flags inside incoming queries. For example, if QR, AA, TC, RA, Z flags are set, the query can be rejected. RD, AD, CD and DO are legitimately set by some software."
|
|
;;
|
|
esac
|
|
|
|
exit 0
|
|
fi
|
|
|
|
# do the stats itself
|
|
get_state
|
|
|
|
# get the time elapsed
|
|
get_value "time.elapsed"
|
|
if test $value = 0 || test $value = "0.000000"; then
|
|
echo "error: time elapsed 0 or could not retrieve data"
|
|
exit 1
|
|
fi
|
|
elapsed="$value"
|
|
|
|
# print value for $1 / elapsed
|
|
print_qps ( ) {
|
|
mn=`echo $1 | sed $ABBREV | tr . _`
|
|
get_value $1
|
|
echo "$mn.value" `echo scale=6';' $value / $elapsed | bc `
|
|
}
|
|
|
|
# print qps if line already found in $2
|
|
print_qps_line ( ) {
|
|
mn=`echo $1 | sed $ABBREV | tr . _`
|
|
value="`echo $2 | sed -e 's/^.*=//'`"
|
|
echo "$mn.value" `echo scale=6';' $value / $elapsed | bc `
|
|
}
|
|
|
|
# print value for $1
|
|
print_value ( ) {
|
|
mn=`echo $1 | sed $ABBREV | tr . _`
|
|
get_value $1
|
|
echo "$mn.value" $value
|
|
}
|
|
|
|
case $id in
|
|
hits)
|
|
for x in thread0.num.queries thread1.num.queries thread2.num.queries \
|
|
thread3.num.queries thread4.num.queries thread5.num.queries \
|
|
thread6.num.queries thread7.num.queries total.num.queries \
|
|
total.num.cachehits num.query.tcp unwanted.queries \
|
|
unwanted.replies; do
|
|
if grep "^"$x"=" $state >/dev/null 2>&1; then
|
|
print_qps $x
|
|
fi
|
|
done
|
|
;;
|
|
queue)
|
|
for x in total.requestlist.avg total.requestlist.max \
|
|
total.requestlist.overwritten total.requestlist.exceeded; do
|
|
print_value $x
|
|
done
|
|
;;
|
|
memory)
|
|
for x in mem.total.sbrk mem.cache.rrset mem.cache.message \
|
|
mem.mod.iterator mem.mod.validator; do
|
|
print_value $x
|
|
done
|
|
;;
|
|
by_type)
|
|
for x in `grep "^num.query.type" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
print_qps_line $nm $x
|
|
done
|
|
;;
|
|
by_class)
|
|
for x in `grep "^num.query.class" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
print_qps_line $nm $x
|
|
done
|
|
;;
|
|
by_opcode)
|
|
for x in `grep "^num.query.opcode" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
print_qps_line $nm $x
|
|
done
|
|
;;
|
|
by_rcode)
|
|
for x in `grep "^num.answer.rcode" $state`; do
|
|
nm=`echo $x | sed -e 's/=.*$//'`
|
|
print_qps_line $nm $x
|
|
done
|
|
print_qps "num.answer.secure"
|
|
print_qps "num.answer.bogus"
|
|
print_qps "num.rrset.bogus"
|
|
;;
|
|
by_flags)
|
|
for x in num.query.flags.QR num.query.flags.AA num.query.flags.TC num.query.flags.RD num.query.flags.RA num.query.flags.Z num.query.flags.AD num.query.flags.CD num.query.edns.present num.query.edns.DO; do
|
|
print_qps $x
|
|
done
|
|
;;
|
|
esac
|