#!/usr/bin/perl -w # # Munin plugin to monitor Tor traffic # # Author: Ge van Geldorp # # Parameters understood: # # host - Change which host to graph (default localhost) # port - Change which port to connect to (default 9051) # password - Plain-text control channel password (see torrc # HashedControlPassword parameter) # cookiefile - Name of the file containing the control channel cookie # (see torrc CookieAuthentication parameter) # # Using HashedControlPassword authentication has the problem that you must # include the plain-text password in the munin config file. To have any # effect, that file shouldn't be world-readable. # If you're using CookieAuthentication, you should run this plugin as a user # which has read access to the tor datafiles. Also note that bugs in versions # upto and including 0.1.1.20 prevent CookieAuthentication from working. # # Usage: place in /etc/munin/node.d/ (or link it there using ln -s) # # Parameters understood: # config (required) # autoconf (optional - used by munin-config) # # # Magic markers - optional - used by installation scripts and # munin-config: # #%# family=contrib #%# capabilities=autoconf use strict; use IO::Socket::INET; # Config our $address = $ENV{host} || "localhost"; # Default: localhost our $port = $ENV{port} || 9051; # Default: 9051 # Don't edit below this line sub Authenticate { my ($socket) = @_; my $authline = "AUTHENTICATE"; if (defined($ENV{cookiefile})) { if (open(COOKIE, "<$ENV{cookiefile}")) { binmode COOKIE; my $cookie; $authline .= " "; while (read(COOKIE, $cookie, 32)) { foreach my $byte (unpack "C*", $cookie) { $authline .= sprintf "%02x", $byte; } } close COOKIE; } } elsif (defined($ENV{password})) { $authline .= ' "' . $ENV{password} . '"'; } print $socket "$authline\r\n"; my $replyline = <$socket>; if (substr($replyline, 0, 1) != '2') { $replyline =~ s/\s*$//; return "Failed to authenticate: $replyline"; } return; } if ($ARGV[0] and $ARGV[0] eq "autoconf") { # Try to connect to the daemon my $socket = IO::Socket::INET->new("$address:$port") or my $failed = 1; if ($failed) { print "no (failed to connect to $address port $port)\n"; exit 1; } my $msg = Authenticate($socket); if (defined($msg)) { print $socket "QUIT\r\n"; close($socket); print "no ($msg)\n"; exit 1; } print $socket "QUIT\r\n"; close($socket); print "yes\n"; exit 0; } if ($ARGV[0] and $ARGV[0] eq "config") { print "graph_title Traffic\n"; print "graph_vlabel bytes per \${graph_period} read (-) / written (+)\n"; print "graph_category Tor\n"; print "graph_info This graph shows the bandwidth used by Tor.\n"; print "read.label byte/s\n"; print "read.type GAUGE\n"; print "read.graph no\n"; print "read.max 10000000\n"; print "write.label byte/s\n"; print "write.type GAUGE\n"; print "write.negative read\n"; print "write.max 10000000\n"; exit 0; } my $socket = IO::Socket::INET->new("$address:$port") or die("Couldn't connect to $address port $port: $!"); my $msg = Authenticate($socket); if (defined($msg)) { print $socket "QUIT\r\n"; close($socket); die "$msg\n"; } print $socket "SETEVENTS bw\r\n"; my $replyline = <$socket>; if (substr($replyline, 0, 1) != '2') { print $socket "QUIT\r\n"; close($socket); $replyline =~ s/\s*$//; die "Failed to get orconn-status info: $replyline\n"; } $replyline = <$socket>; if (substr($replyline, 0, 1) != '6') { print $socket "QUIT\r\n"; close($socket); $replyline =~ s/\s*$//; die "Failed to get bw: $replyline\n"; } my @reply = split(/\s+/, $replyline); print $socket "SETEVENTS\r\n"; $replyline = <$socket>; print $socket "QUIT\r\n"; close($socket); print "read.value $reply[2]\n"; print "write.value $reply[3]\n"; exit 0; # vim:syntax=perl