#!/usr/bin/perl # -*- perl -*- # # Wildcard-script to monitor network port usage. To monitor a # port, link port_ to this file. E.g. # # ln -s /usr/share/munin/node/plugins-auto/port_ /etc/munin/node.d/port_www # # ...will monitor www connections. Services are those listed in # /etc/services. # # Parameters: # # config (required) # autoconf (optional - used by munin-config) # suggest (optional - used by munin-config) # # Magic markers (optional - used by munin-config and some installation # scripts): #%# family=manual #%# capabilities=autoconf suggest use constant { ST_ESTABLISHED => 1, ST_LISTEN => 10, }; sub cache_open { my ($fd, $file) = @_; my $cache_dir = "/var/lib/munin/plugin-state"; my $cache = $file; $cache =~ s:/:_:g; $cache = "$cache_dir/$cache"; my $ttl = $ENV{'cache_ttl'}; $ttl = 60 unless (defined $ttl); # The user can disable caching by setting cache_ttl to something # other than a positive integer. if ($ttl !~ /^\d+$/ || $ttl == 0) { return open($fd, $file); } if (-e $cache && -M _ < $ttl / 86400) { return open($fd, $cache); } unless (-d $cache_dir) { mkdir($cache_dir) || die "$cache_dir: mkdir: $!\n"; } my $retval; $retval = open(CACHE_INPUT, $file); return $retval unless ($retval); my $tmpfile = "$cache.$$"; open(CACHE_OUTPUT, ">$tmpfile") || die "$tmpfile: open: $!\n"; while () { print CACHE_OUTPUT; } close(CACHE_OUTPUT) || die "$tmpfile: close: $!\n"; close(CACHE_INPUT); rename($tmpfile, $cache) || die "$cache: rename: $!\n"; # Okay, we have a fresh copy in our cache. return open($fd, $cache); } if ($ARGV[0] and $ARGV[0] =~ /^\s*autoconf\s*$/i) { if (-r "/proc/net/tcp" or -r "/proc/net/tcp6") { print "yes\n"; exit 0; } else { print "no\n"; exit 1; } } sub count_tcp_ports { my ($state, $ports) = @_; cache_open("NETSTAT", "/proc/net/tcp") or exit 1; my $header = ; $header =~ /^(.*\s)local_address\s/; # 32/4 + 1 corresponds to the IP address in hex + ":" my $local_port_offset = length($1) + 32/4 + 1; $header =~ /^(.*\s)st\s/; my $state_offset = length($1); while () { next unless (hex(substr($_, $state_offset, 2)) == $state); ++$ports->{hex(substr($_, $local_port_offset, 4))}; } close NETSTAT; } sub count_tcp6_ports { my ($state, $ports) = @_; cache_open("NETSTAT", "/proc/net/tcp6") or exit 1; my $header = ; $header =~ /^(.*\s)local_address\s/; # 128/4 + 1 corresponds to the IP address in hex + ":" my $local_port_offset = length($1) + 128/4 + 1; $header =~ /^(.*\s)st\s/; my $state_offset = length($1); while () { next unless (hex(substr($_, $state_offset, 2)) == $state); ++$ports->{hex(substr($_, $local_port_offset, 4))}; } close(NETSTAT); } if ($ARGV[0] and $ARGV[0] =~ /^\s*suggest\s*$/i) { if (-r "/proc/net/tcp" or -r "/proc/net/tcp6") { my %ports = (); count_tcp_ports(ST_LISTEN, \%ports) if (-r "/proc/net/tcp"); count_tcp6_ports(ST_LISTEN, \%ports) if (-r "/proc/net/tcp6"); foreach my $port (keys %ports) { my $p = (getservbyport($port, "tcp"))[0]; print $p || $port, "\n"; } exit 0; } exit 1; } my ($name, $port); if ($0 =~ /port_(.+)*$/) { $name = $port = $1; if ($port =~ /\D/) { $port = (getservbyname ($name, "tcp"))[2]; } } else { exit 2; } if ($ARGV[0] and $ARGV[0] =~ /^\s*config\s*$/i) { if (int($name)) { print "graph_title TCP port $name connection count\n"; } else { print "graph_title $name connection count\n"; } print "graph_args --base 1000 -l 0\n"; print "graph_vlabel concurrent connections\n"; print "graph_category network\n"; print "count.label $name\n"; exit 0; } my %ports = (); count_tcp_ports(ST_ESTABLISHED, \%ports) if (-r "/proc/net/tcp"); count_tcp6_ports(ST_ESTABLISHED, \%ports) if (-r "/proc/net/tcp6"); # If there aint any the right answer is 0 no blank or U $ports{$port} = 0 if $ports{$port} == ''; print "count.value ", $ports{$port}, "\n"; # vim:syntax=perl