diff --git a/bin/cfgmaker b/bin/cfgmaker index 10eb9aa..703791d 100755 --- a/bin/cfgmaker +++ b/bin/cfgmaker @@ -44,7 +44,7 @@ BEGIN { use FindBin; use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}@@lib@@${main::SL}mrtg2"; +use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; use MRTG_lib "2.100015"; use Getopt::Long; @@ -224,7 +224,7 @@ sub InterfaceInfo($$$$$) { # maximum value (4,294,967,295) and ifHighSpeed must be used # to report the interace's speed. For a sub-layer which has # no concept of bandwidth, this object should be zero." - if ( (not defined $value) || ($value == 2**32-1) || ($value == 2**32-2)) { + if ( (not defined $value) || ($value == 2**32-1) ) { ($if, $value) = split /:/, $ifHighSpeed[$i], 2; $value = $value * 1000000; # highSpeed = contador * 10^6 debug('base',"Speed: $if - $value"); diff --git a/bin/cfgmaker.ifhighspeed b/bin/cfgmaker.ifhighspeed deleted file mode 100755 index e022bd1..0000000 --- a/bin/cfgmaker.ifhighspeed +++ /dev/null @@ -1,2892 +0,0 @@ -#! /usr/bin/perl -w -# -*- mode: Perl -*- -################################################################## -# MRTG 2.17.7 -- Config file creator -################################################################## -# Created by Tobias Oetiker -# this produces an mrtg config file for one router or more routers -# by pulling info off the router via snmp -################################################################## -# Distributed under the GNU copyleft -# Copyright 2000 by Tobias Oetiker -################################################################## - -# DEBUG TARGETS -# base - basic program flow -# snpo - SNMP Polling -# snpd - SNMP Detail -#@main::DEBUG=qw(base); -@main::DEBUG=qw(base snpo snpd); - -require 5.005; -use strict; - -BEGIN { - # Automatic OS detection ... do NOT touch - if ( $^O =~ /^(?:(ms)?(dos|win(32|nt)?))/i ) { - $main::OS = 'NT'; - $main::SL = '\\'; - $main::PS = ';'; - } elsif ( $^O =~ /^NetWare$/i ) { - $main::OS = 'NW'; - $main::SL = '/'; - $main::PS = ';'; - } elsif ( $^O =~ /^VMS$/i ) { - $main::OS = 'VMS'; - $main::SL = '.'; - $main::PS = ':'; - } else { - $main::OS = 'UNIX'; - $main::SL = '/'; - $main::PS = ':'; - } -} - -use FindBin; -use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}@@lib@@${main::SL}mrtg2"; - -use MRTG_lib "2.100015"; -use Getopt::Long; -use Pod::Usage; -use Socket; - - -sub main { - - # assign defaults - my %opt = ( - 'enable-ipv6' => 0, - 'use-16bit' => 0, - 'community' => 'public', - 'interfaces' => 1, - 'enablesnmpv3' => 0, -# 'snmp-options' => ':::::2' - ); - my %routers; - my %confcache; - my $ipv4only; - my %v3opt; - $opt{fullcmd} = - "$0 ".(join " ", - map {$_ =~ /[ \[\]\*\{\}\;\>\<\&]/ ? qq{"$_"} : $_ } @ARGV); - options(\%opt,\%routers); - - # Check for IPv6 libraries if IPv6 is enabled. - # If the check fails, IPv6 is disabled. - $ipv4only = 1; - if ($opt{'enable-ipv6'} == 1) { - if ((eval {local $SIG{__DIE__};require Socket6;}) && (eval {local $SIG{__DIE__};require IO::Socket::INET6;})) { - debug ('base',"IPv6 libraries found, IPv6 enabled."); - $ipv4only = 0; - } else { - warn "WARNING: IPv6 libraries not found, IPv6 disabled.\n"; - $opt{'enable-ipv6'} = 0; - } - } - - if ($opt{'use-16bit'} == 1) { - warn "WARNING: Using 16bit RequestIDs\n"; - no warnings; - $SNMP_Session::default_use_16bit_request_ids=1; - } - # Check for SNMP V3 - # - if (exists($opt{username}) or lc($opt{enablesnmpv3}) eq "yes" ) { - if (eval {local $SIG{__DIE__};require Net_SNMP_util;}) { - import Net_SNMP_util; - debug('base', "SNMP V3 libraries found, SNMP V3 enabled."); - $opt{enablesnmpv3} = "yes"; - push @{$opt{global}}, "enablesnmpv3: yes"; - } else { - warn "WARNING: SNMP V3 libraries not found, SNMP V3 disabled. Falling back to V2c.\n"; - require SNMP_util; - import SNMP_util; - $opt{enablesnmpv3} = "revert"; - } - } - else { - require SNMP_util; - import SNMP_util; - $opt{enablesnmpv3} = "no"; - } - - init(); - - foreach my $router - (sort - {($routers{$a}{noofrouter}) <=> ($routers{$b}{noofrouter})} - keys %routers) - { - my @snmpopt = split(/:/,$routers{$router}{'snmp-options'}); - if ($snmpopt[5] and $snmpopt[5] == 3) { - if ($opt{enablesnmpv3} eq "revert") { - $snmpopt[5] = 2; - warn "reverting to snmpV2c for router $router\n"; - $routers{$router}{'snmp-options'} = join(":",@snmpopt); - $routers{$router}{snmpopt_current} = $routers{$router}{'snmp-options'}; - } else { - die "SNMP V3 requires a --username parameter as part of the User Security Model for router $routers{$router}{routerkey}" if $opt{enablesnmpv3} eq "no"; - $routers{$router}{enablesnmpv3} = $opt{enablesnmpv3}; - %v3opt = parsev3(\%opt); - } - } else { - debug('base',"snmpv3 available, but using v1/v2c for $routers{$router}{routerkey}") if $opt{enablesnmpv3} eq "yes"; - } - - - # pod2usage(-verbose=>1,-message=>"ERROR: Could not Parse $router\n") - # unless $router =~ /.*\@.*/; - debug('base',"Get Device Info on $router"); - $routers{$router}{ipv4only} = $ipv4only; - if ( my $devinfo = DeviceInfo($router,\%routers,\%v3opt) ) { - $routers{$router}{deviceinfo} = $devinfo; - } else { - warn "WARNING: Skipping $router as no info could be retrieved\n\n"; - sleep 5; - next; - } - - if ($opt{interfaces}) { - debug('base',"Populating confcache"); - populateconfcache(\%confcache,$router,$routers{$router}{ipv4only},1,\%v3opt); - debug('base',"Get Interface Info"); - InterfaceInfo(\%confcache,\%routers,$router,\%opt,\%v3opt); - } - } - GenConf(\%opt,\%routers,\%confcache,\%v3opt); -} # end main - -main; -exit 0; - -sub InterfaceInfo($$$$$) { - my $confcache = shift; - my $routers = shift; - my $router = shift; - my $opt = shift; - my $v3opt = shift; - my @Variables = qw (ifIndex ifType ifAdminStatus ifOperStatus ifMtu); - - my $snmphost = v4onlyifnecessary($router, $routers->{$router}{ipv4only}); - - if ($routers->{$router}{deviceinfo}{Vendor} eq 'cisco' && - $routers->{$router}{deviceinfo}{sysDescr} =~ m/Version\s+(\d+\.\d+)/) { - push @Variables, ($1 > 11.0 or $1 < 10.0 ) ? "ifAlias" : "CiscolocIfDescr"; - if ($1 > 11.2) {push @Variables, "vmVlan";}; - if ($1 > 11.3) {push @Variables, "vlanTrunkPortDynamicStatus";}; - } elsif ( $routers->{$router}{deviceinfo}{Vendor} =~ /(?:hp|juniper|dlink|wwp|foundry|dellLan|force10|3com|extremenetworks|openBSD|arista|enterasys|zyxel|vyatta|dcn|brocade)/i) { - push @Variables, "ifAlias"; - } - - my $descr = $routers->{$router}{deviceinfo}{sysDescr}; - if ($routers->{$router}{deviceinfo}{Vendor} eq 'cisco' && - $descr =~ m/Catalyst\sOperating\sSystem|Cisco\sSystems\sWS-C2900/ ) { - push @Variables, "CiscoCatalystPortName"; - push @Variables, "vmVlan"; - } - if ($routers->{$router}{deviceinfo}{Vendor} eq 'cisco' && - $descr =~ m/Catalyst/ ) { - push @Variables, "vlanTrunkPortDynamicStatus"; - } - if ($descr =~ m/Passport-8610/ || $descr =~ m/MERS-8610/ ) { - push @Variables, "ppPortName"; - } - - foreach my $var (@Variables) { - debug('base',"Walking $var"); - foreach my $tuple (snmpwalk($snmphost,$v3opt, $var)){ - my($if,$value) = split /:/, $tuple, 2; - $value =~ s/[\0- ]+$//; # no trailing space - $routers->{$router}{$if}{$var} = $value; - debug('snpd'," $router -> $if -> $var = $value"); - } - } - - # interface speed var depends on snmp version - - my $snmp_version = (split(':', $router, 6))[5] || 1; - if ( $snmp_version =~ /[23]/ ) { - debug('base',"Walking ifSpeed"); - my @ifSpeed = snmpwalk($snmphost, $v3opt,'ifSpeed'); - debug('snpd',"\@ifSpeed = @ifSpeed\n"); - debug('base',"Walking ifHighSpeed"); - my @ifHighSpeed = snmpwalk($snmphost,$v3opt, 'ifHighSpeed'); - for ( my $i=0; $i<=$#ifSpeed; $i++ ) { - my ($if,$value) = split /:/, $ifSpeed[$i], 2; -# the mib entry on ifSpeed says -# "An estimate of the interface's current bandwidth in bits -# per second. For interfaces which do not vary in bandwidth -# or for those where no accurate estimation can be made, this -# object should contain the nominal bandwidth. If the -# bandwidth of the interface is greater than the maximum value -# reportable by this object then this object should report its -# maximum value (4,294,967,295) and ifHighSpeed must be used -# to report the interace's speed. For a sub-layer which has -# no concept of bandwidth, this object should be zero." - if ( (not defined $value) || ($value == 2**32-1) ) { - ($if, $value) = split /:/, $ifHighSpeed[$i], 2; - $value = $value * 1000000; # highSpeed = contador * 10^6 - debug('base',"Speed: $if - $value"); - } - if ( ($descr =~ m/MERS-8610/ ) && (defined $ifHighSpeed[$i]) ) { - ($if, $value) = split /:/, $ifHighSpeed[$i], 2; - $value = $value * 1000000; # highSpeed = contador * 10^6 - debug('base',"Speed: $if - $value"); - - } - $routers->{$router}{$if}{'ifSpeed'} = $value; - } - } else { - debug('base',"Walking ifSpeed"); - foreach my $tuple (snmpwalk($snmphost,$v3opt, 'ifSpeed')){ - my($if,$value) = split /:/, $tuple, 2; - $routers->{$router}{$if}{'ifSpeed'} = $value; - debug('snpd'," $router -> $if -> ifSpeed = $value"); - } - } - - # magic speed determination for portmaster IFs - - if ($routers->{$router}{deviceinfo}{Vendor} eq 'portmaster') { - # We can only approximate speeds - # - # so we think that ppp can do 76800 bit/s, and slip 38400. - # (actualy, slip is a bit faster, but usualy users with newer modems - # use ppp). Alternatively, you can set async speed to 115200 or - # 230400 (the maximum speed supported by portmaster). - # - # But if the interface is ptpW (sync), max speed is 128000 - # change it to your needs. On various Portmasters there are - # various numbers of sync interfaces, so modify it. - # - # The most commonly used PM-2ER has only one sync. - # - # Paul Makeev (mac@redline.ru) - # - foreach my $if (keys %{$routers->{$router}}) { - next unless $if =~ /^\d+$/; - my $ift = $routers->{$router}{$if}{ifType}; - my $ifd = $routers->{$router}{$if}{Descr}; - if ($ift == 23) { - if ($ifd eq 'ptpW1') { - $routers->{$router}{$if}{ifSpeed} = 128000; - } else { - $routers->{$router}{$if}{ifSpeed} = 76800; - } - } elsif ($ift == 28) { - $routers->{$router}{$if}{ifSpeed} = 38400; - } elsif ($ift == 6) { - $routers->{$router}{$if}{ifSpeed} = 10000000; - } - } - } - - # match confcache info into tree - my $cachekey = cleanhostkey $router; - - foreach my $method (keys %{$$confcache{$cachekey}}) { - foreach my $key (keys %{$$confcache{$cachekey}{$method}}) { - my $if = readfromcache($confcache,$router,$method,$key); - next unless $if =~ /^\d+$/; - $routers->{$router}{$if}{$method} = $key; - for ($method) { - #fix special chars in ifdescr - # no need for this we fix if references later on - # /^Descr|Name$/ && do { - # $routers->{$router}{$if}{"R$method"} = $routers->{$router}{$if}{$method}; - # $routers->{$router}{$if}{$method} =~ s/(:)/\\$1/g; - # next; - # }; - - #find hostname of IF - !$$opt{noreversedns} && /^Ip$/ and do { - my $name = - gethostbyaddr( - pack('C4', - split(/\./, - $routers->{$router}{$if}{$method})), - AF_INET); - $routers->{$router}{$if}{DNSName} = ($name or ""); - next; - }; - } - } - } -} # end InterfaceInfo - -sub GenConf ($$$$) { - my $opt = shift; - my $routers = shift; - my $confcache = shift; - my $v3opt = shift; - my $conf = "# Created by \n# $$opt{fullcmd}\n\n"; - - # print global options - $conf .= < ($$routers{$b}{noofrouter})} - keys %$routers ) { - my $router_ref = $$routers{$router}; - my $router_opt = $$router_ref{opt}; - my $router_dev = $$router_ref{deviceinfo}; - - # Did any global options appear on the command line - # before this router? If so, include them into the - # configuration file. - if (defined $$router_opt{global}) { - foreach my $key (@{$$router_opt{global}}) { - $conf .= "$key\n"; - } - } - - # If IPv6 is enabled, add IPv4Only target directive for targets - # that do not support SNMP over IPv6. - my $ipv4only_directive; - my $router_ipv4only = ($$opt{'enable-ipv6'} == 1) && $$router_ref{ipv4only}; - - my $syscontact = $$router_dev{sysContact}; - my $html_syscontact = html_escape($syscontact); - my $syslocation = $$router_dev{sysLocation}; - my $html_syslocation = html_escape($syslocation); - my $sysname = $$router_dev{sysName}; - my $sysdescr = $$router_dev{sysDescr}; - my $comment_sysdescr = $sysdescr; - # make sure embeded newlines do not harm us here - $comment_sysdescr =~ s/[\n\r]+/\n\# /g; - my $community = $$router_ref{community}; - $community =~ s/([@ ])/\\$1/g; - my $router_connect = "$community\@$$router_ref{routername}$$router_ref{snmpopt_current}"; - my @v3options; - foreach my $v3op (keys %$v3opt) { - push @v3options, $v3op."=>'".$$v3opt{$v3op}."'"; - } - my $v3options = join(",",@v3options) if $$router_ref{'snmp-options'} =~ /(?::[^:]*){4}:3/ ; - my $html_sysdescr = html_escape($sysdescr); - my $router_name = - ($$router_ref{routername} - . (($$router_ref{'dns-domain'})?'.':'') - . $$router_ref{'dns-domain'}); - - # James Overbeck 2001/09/20 - # Moved $directory_name definition from within the interface - # foreach loop to here. In its previous location, $directory_name - # was not accessible to host templates. $directory_name is not - # changed per-interface so it might as well be here instead of - # where it was. - - my $directory_name = ""; - - if (defined $$router_opt{subdirs}) { - $directory_name = $$router_opt{subdirs}; - $directory_name =~ s/HOSTNAME/$router_name/g; - $directory_name =~ s/SNMPNAME/$$router_dev{sysName}/g; - } - - - my $target_lines = ""; - my $problem_lines = ""; - my $head_lines = " -###################################################################### -# System: $sysname -# Description: $comment_sysdescr -# Contact: $syscontact -# Location: $syslocation -###################################################################### -"; - - my $separator_lines = "\n\n"; - - # Host specific config lines generation code starts HERE - - if(defined $$router_opt{'host-template'}) { - # First test if the file exists and is readable, die if not. - die "File $$router_opt{'host-template'} didn't exist.\n" - unless (-e $$router_opt{'host-template'} - and -r $$router_opt{'host-template'}); - # Open the file (or die). - open IF_TEMPLATE, $$router_opt{'host-template'} - or die "File $$router_opt{'host-template'} couldn't be opened.\n"; - - my @template_lines = readline *IF_TEMPLATE; - close IF_TEMPLATE; - $@ = undef; - eval ('local $SIG{__DIE__};'.join("", @template_lines)); - - die "ERROR Evaluation of the contents in the file \n\n". - "$$router_opt{'host-template'}\ngave the error \n\n\"$@\"\n\nExiting cfgmaker\n" if $@; - } - - $conf .= ($head_lines - . $problem_lines - . $target_lines - . $separator_lines); - - # Host specific config lines generation code ends HERE - - - if ($$router_opt{'interfaces'}) { - foreach my $ifindex (sort {int($a) <=> int($b)} grep /^\d+$/, keys %$router_ref) { - my $i = $$router_ref{$ifindex}; - - # Now define a number of variables used for this interface. - # Some variables are just used internally by cfgmaker to - # process the interface, others are provided for usage in - # host and interface templates and for interface filters. - - my $if_index = $ifindex; - my $if_eth = $$i{Eth} || 'No Ethernet Id'; - - # does it make sense to look at the interface ? - my @prob; - my $default_ifstate = 1; # State assumed up. - my $default_iftype = 1; # iftype assumed ok. - my $if_ok = 1; # - - my $if_admin = (($$i{ifAdminStatus} || 0) == 1); - my $if_oper = (($$i{ifOperStatus} || 0) == 1); - - my $if_type = $$i{ifType} || -1; - my $if_is_ethernet = 0 < scalar(grep {$_ == $if_type;} - (6,7,26,62,69,117)); - my $if_is_isdn = (0 < scalar (grep {$_ == $if_type;} - (20,21,63,75,76,77))); - my $if_is_dialup = $if_is_isdn || - (0 < scalar (grep {$_ == $if_type;} - (23,81,82,108))); - my $if_is_atm = (0 < scalar(grep {$_ == $if_type;} - (37,49,107,105,106,114,134))); - - my $if_is_wan = 0 < scalar(grep {$_ == $if_type;} - (22,30,32,39,44,46)); - - my $if_is_lan = $if_is_ethernet || - (0 < scalar (grep {$_ == $if_type;} - (8,9,11,15,26,55,59,60,115))); - my $if_is_dsl = (0 < scalar(grep {$_ == $if_type;} - (94,95,96,97))); - my $if_is_loopback = $if_type == 24; - my $if_is_ciscovlan = - ($$router_dev{Vendor} eq 'cisco' - and $$i{Descr} =~ /^(unrouted )?[- ]?VLAN[- ]?\d*$/i); - - my $if_ip = $$i{Ip} || 'No Ip'; - my $if_snmp_descr = $$i{Descr} || 'No Description'; - $if_snmp_descr =~ s/\n$//; # no you don't want to know who does this - # ok ... dell 3524 - $if_snmp_descr =~ s/ /-/g; - my $if_type_num = $$i{ifType} || 'Unknown Type'; - $$i{ifType} ||= -1; - my $if_snmp_name = $$i{Name} || 'No Name'; - my $if_snmp_alias = $$i{ifAlias} || ''; - my $if_cisco_descr = $$i{CiscolocIfDescr} || ''; - my $if_dns_name = $$i{DNSName} || 'No DNS name'; - my $if_vlan_id = $$i{vmVlan} || 'No Vlan'; - my $if_cisco_trunk = ($$i{vlanTrunkPortDynamicStatus} || 0 == 1); - my $if_MTU = $$i{ifMtu} || 'No MTU'; - - # For Nokia IPSO, find non-ethernet interfaces with IP addresses - # and add missing MAC address and Port Speed information to - # to the LOGICAL and LOGICAL+VLAN interfaces. - if ( $$router_dev{Vendor} eq 'nokiaipsofw' ) { - if ($$i{ifType} ne "6" and - $$router_dev{sysDescr} =~ / IPSO / && - $$i{Ip} =~ /^\d+/ and - (not $$i{Eth} or - not $$i{ifSpeed} or - $$i{ifSpeed} < 10 ) - ) { - my $logical_if_name = $$i{Name}; - - # Split the LOGICAL interface name in attempt - # to match with base PHYSICAL interface detail. - my ($logical_if_HEAD, $logical_if_TAIL) = - $logical_if_name =~ /^(.*)(c\d+)$/; - - foreach my $ifindexTMP (sort {int($a) <=> int($b)} - grep /^\d+$/, keys %$router_ref) { - next unless $ifindexTMP =~ /^\d+$/; - - my $physical_if_name = $$router_ref{$ifindexTMP}; - - if ($$physical_if_name{ifType} == 6 && - $logical_if_HEAD eq $$physical_if_name{Name} ) { - $$i{Eth} ||= $$physical_if_name{Eth}; - $$i{ifSpeed} = $$physical_if_name{ifSpeed} - if ( not $$i{ifSpeed} or $$i{ifSpeed} < 10 ); - } - } - } - } - - # First investigate the state of the interface. - if (not defined $$router_opt{'no-down'}) { - if (($$i{ifAdminStatus} || 0 )== 2) { - push @prob, "it is administratively DOWN"; - $default_ifstate = 0; - } elsif (($$i{ifAdminStatus} || 0 ) == 3) { - push @prob, "it is in administrative TEST mode"; - $default_ifstate = 0; - } - - if (not defined $$router_opt{'show-op-down'}) { - if (($$i{ifOperStatus} || 0 ) == 2) { - push @prob, "it is operationally DOWN"; - $default_ifstate = 0; - } elsif (($$i{ifOperStatus} || 0 ) == 3) { - push @prob, "it is in operational TEST mode"; - $default_ifstate = 0; - } - } - } - - - # Investigate the type of the interface. - if ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 18) { # by fwo@obsidian.co.za - push @prob, "it is a DS1 controllers"; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 19) { # by fwo@obsidian.co.za - push @prob, "it is a E1 controllers"; - $default_iftype = 0; - } elsif ($$i{ifType} == 24) { - push @prob, "it is a Software Loopback interface" ; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 30) { # by blube@floridadigital.net - push @prob, "it is a DS3 controller"; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 102) { # by dan.mcdonald@austinenergy.com - push @prob, "it is a Voice controller"; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 103) { # by dan.mcdonald@austinenergy.com - push @prob, "it is a Voice dial peer"; - $default_iftype = 0; - } elsif ($$i{ifType} == 162) { - push @prob, "it is a CEF Sub-interface"; # John Begley - } elsif ($$router_dev{Vendor} eq 'cisco' - and $$i{Descr} eq 'Null0') { - push @prob, "it is a cisco Null0 interface"; - $default_iftype = 0; - } - my $default = $default_iftype && $default_ifstate; - - # Do some futher investigation if the interface makes - # sense to collect on - - # I debated whether to insert the zero-speed check before - # or after the "will always be zero" sections below. - # I settled on before since I'll assume the user knows - # what speed the zero-speed interfaces should be better - # than the simple logic below. - if (not $$i{ifSpeed} and $$router_opt{'zero-speed'}) { - # Set all interfaces with zero speed to the value specified - # by the --zero-speed= command line option. - - # Be sure the value specified is a valid integer. - # It seems like this could be done once when - # $$router_opt is set, but I didn't see any example - # of input validation in that part of cfgmaker, - # so it gets done here, more times than are - # really necessary. ;-) - unless ($$router_opt{'zero-speed'} =~ /^\d+$/) { - die "ERROR: zero-speed specified with non-integer speed: $$router_opt{'zero-speed'}"; - } - $$i{ifSpeed} = $$router_opt{'zero-speed'}; - } - - if (not $$i{ifSpeed} and $$router_dev{Vendor} eq 'foundry' and $$i{ifType} and $$i{ifType} == 194) { - # foundry ATM subinterfaces always report 0 speed, make it 155Mbps instead. - $$i{ifSpeed} = 155000000; - } elsif (not $$i{ifSpeed} and $$router_dev{Vendor} eq 'foundry' and $$i{ifType} and $$i{ifType} == 135) { - # Foundry virtual Ethernet interfaces always report 0 speed, make it 1GB instead. - $$i{ifSpeed} = 1000000000; - } elsif (not $$i{ifSpeed} and $$router_dev{Vendor} eq 'cisco' and $$i{sysDescr} and $$i{sysDescr} =~ /FWSM-Firewall / ) { - # Cisco PIX Firewall Switch Modules have effective backplane speed of 600 Megs - $$i{ifSpeed} = 600000000; - } elsif (not $$i{ifSpeed} and $$router_dev{Vendor} eq '3com' and $$i{Descr} and $$i{Descr} =~ /RMON VLAN (\d+)/ ) { - $$i{ifSpeed} = 100000000; - $if_vlan_id = $1; - } elsif (not $$i{ifSpeed}) { - push @prob, "has no ifSpeed property"; - $$i{ifSpeed} = 0; - $if_ok = 0; - } - - my $message; - my $nohc =0; - if ($message = IsCounterBroken($ifindex, $router_ref,$v3opt)) { - # set snmpopt_current to working snmp options - if ($message eq '1') { - $nohc = 1; - } else { - push @prob, "got '$message' from interface when trying to query"; - $if_ok = 0; - } - } - - my $community = $$router_ref{community}; - $community =~ s/([@ ])/\\$1/g; - my $router_connect = "$community\@$$router_ref{routername}$$router_ref{snmpopt_current}"; - - my $v3options = join(",",@v3options) if $$router_ref{snmpopt_current} =~ /(?::[^:]*){4}:3/ ; - - # determine interface reference - my $if_ref; - if (defined $$router_opt{ifref}) { - foreach (split /,/,$$router_opt{ifref}) { - /^ip$/ && do { if($$i{Ip} ){ $if_ref = "/".$$i{Ip}; last;} next}; - /^eth$/ && do { if($$i{Eth} ){ $if_ref = "!".$$i{Eth}; last;} next}; - /^descr?$/&& do { if($$i{Descr}){ $if_ref = "\\".$$i{Descr};last;} next}; - /^name$/ && do { if($$i{Name} ){ $if_ref = "#".$$i{Name}; last;} next}; - /^type$/ && do { if($$i{Type} ){ $if_ref = "%".$$i{Type}; last;} next}; - /^nr$/ && do { $if_ref = $ifindex; last }; - die "ERROR: Invalid value for --ifref: $$router_opt{ifref} ($_)\n"; - } - if (not defined $if_ref) { - push @prob, "--ifref=$$router_opt{ifref} is not unique for this interface"; - $if_ref = $ifindex; - $if_ok = 0; - } - } else { - $if_ref = $ifindex; - } - - # generate Target name - my $trim_if_ref = $if_ref; - $trim_if_ref =~ s/[\#!\/\\:\s\@%]+/_/g; - $trim_if_ref =~ s/^_*(.+?)_*$/$1/; - my $target_name = "${router_name}_$trim_if_ref"; - my $if_title_desc = $if_ref; - $if_title_desc =~ s/^[^\d]//; - my $if_speed = int($$i{ifSpeed} / 8); - my $if_speed_str = fmi($if_speed,$$router_ref{flags}); - my $if_type_desc = IfType($$i{ifType}); - my $html_if_type_desc = html_escape($if_type_desc); - my $desc_prefix = 'Traffic Analysis for '; - - my $port_dot = $$i{Name} || 'Unknown'; - $port_dot =~ s/\//./g; - my $if_port_name = $$router_ref{$port_dot}{CiscoCatalystPortName}; - my $if_pp_port_name = $$router_ref{$ifindex}{ppPortName}; - - if (defined $$router_opt{ifdesc}) { - $desc_prefix = ''; - foreach (split /,/,$$router_opt{ifdesc}) { - /^ip$/ && do { if($$i{Ip}) { $if_title_desc = $$i{Ip}; last } next }; - /^eth$/ && do { if($$i{Eth}) { $if_title_desc = $$i{Eth}; last } next }; - /^descr?$/ && do { if($$i{Descr}){ $if_title_desc = $if_snmp_descr; last } next }; - /^alias$/ && do { if($$i{ifAlias}){ $if_title_desc = "$if_snmp_descr $if_snmp_alias $if_cisco_descr"; last } next }; - /^name$/ && do { if($$i{Name}) {$if_title_desc = "#".$$i{Name}; last } next }; - /^catname$/ && do {$if_title_desc = $if_port_name; last }; - /^ppname$/ && do {$if_title_desc = $if_pp_port_name; last}; - /^type$/ && do { if($$i{Type}) { $if_title_desc = "%".$$i{Type}; last } next }; - /^nr$/ && do {$if_title_desc = "Interface $ifindex"; last}; - /^$/ && do {$if_title_desc = $if_type_desc || $if_snmp_descr; last }; - die "ERROR: Invalid value for --ifdesc: '$$router_opt{ifdesc} ($_)'\n"; - } - } - - # Now setup a large number of variables needed for the - # generation of the configuration lines. - - $if_title_desc =~ s/\\([:@\\\/\# ])/$1/g; # unescape - $if_title_desc = $if_snmp_name if not $if_title_desc; - my $html_if_title_desc = html_escape($if_title_desc); - my $html_desc_prefix = html_escape($desc_prefix); - - my $html_if_snmp_descr = html_escape($if_snmp_descr); - my $html_if_snmp_name = html_escape($if_snmp_name); - my $html_if_snmp_alias = html_escape($if_snmp_alias); - my $html_if_cisco_descr = html_escape($if_cisco_descr); - my $if_description = "$if_snmp_descr $if_snmp_alias $if_cisco_descr"; - my $html_if_description = html_escape($if_description); - my $if_title = "$desc_prefix$if_title_desc -- $sysname"; - my $html_if_title = html_escape($if_title); - - my $head_lines = "### Interface $ifindex >> Descr: '$if_snmp_descr' |". - " Name: '$if_snmp_name' | Ip: '$if_ip' | Eth: '$if_eth' ###\n"; - - my $target_lines = ""; - my $separator_lines = "\n\n"; - - # escape the if reference - $if_ref =~ s/([& :])/\\$1/g; - my $default_target_directive = "Target[$target_name]: $if_ref:$router_connect"; - $default_target_directive .= "\nSnmpOptions[$target_name]: $v3options" if $$router_ref{snmpopt_current} =~ /(?::[^:]*){4}:3/ ; - $default_target_directive .= "\nnoHC[$target_name]: yes" if $nohc == 1; - my $if_snmp_descr_save = $if_snmp_descr; - $if_snmp_descr_save =~ s/"/'/g; - my $default_setenv_directive = "SetEnv[$target_name]: MRTG_INT_IP=\"$if_ip\" MRTG_INT_DESCR=\"$if_snmp_descr_save\""; - my $default_directory_directive = ($directory_name ? "Directory[$target_name]: $directory_name" : ""); - my $default_maxbytes_directive = "MaxBytes[$target_name]: $if_speed"; - - $ipv4only_directive = $router_ipv4only ? "IPv4Only[$target_name]: yes" : ""; - - my $default_title_directive = "Title[$target_name]: $html_desc_prefix$html_if_title_desc -- $sysname"; - my $default_pagetop_directive = - "PageTop[$target_name]:

$html_desc_prefix$html_if_title_desc -- $sysname

-
- - - - - - - - - - - - - - - - - - - - - "; - $default_pagetop_directive .= " - - - - " if defined $if_port_name; - $default_pagetop_directive .= " - - - - " if defined $if_pp_port_name; - $default_pagetop_directive .= " - - - - "; - $default_pagetop_directive .= " - - - - " if $if_ip; - $default_pagetop_directive .= " -
System:$sysname in $html_syslocation
Maintainer:$html_syscontact
Description:$html_if_description
ifType:$html_if_type_desc ($if_type_num)
ifName:$html_if_snmp_name
Port Name:$if_port_name
Port Name:$if_pp_port_name
Max Speed:$if_speed_str
Ip:$if_ip ($if_dns_name)
-
"; - - my $default_target_lines = - ("\n" - . $default_target_directive . "\n" - . $default_setenv_directive . "\n" - . ($default_directory_directive - ? ($default_directory_directive . "\n") - : "") - . $default_maxbytes_directive . "\n" - . ($ipv4only_directive - ? ($ipv4only_directive . "\n") - : "") - . $default_title_directive . "\n" - . $default_pagetop_directive . "\n"); - - - # If --if-filter is provided, evalutat that. If it - # returns true, clear @prob. If it returns false, - # instead add a complaint to @prob. - - if (defined $$router_opt{'if-filter'}) { - $@ = undef; - if (eval('local $SIG{__DIE__};'.$$router_opt{'if-filter'})) { - @prob = (); - } else { - push @prob, "filter specified by --if-filter rejected the interface"; - $if_ok = 0; - } - die "ERROR: with if-filter $$router_opt{'if-filter'}: $@" if $@; - } - - - # issue problem report - - my $problem_lines = ""; - - if (@prob) { - $problem_lines .= "### The following interface is commented out because:\n"; - map {$problem_lines .= "### * $_\n"} @prob; - $if_ok = 0; - } - - # The target config generation code starts HERE. - - - if (defined $$router_opt{'if-template'}) { - # First test if the file exists and is readable, - # die if not. - die "File $$router_opt{'if-template'} didn't exist.\n" unless (-e $$router_opt{'if-template'} - and -r $$router_opt{'if-template'}); - # Open the file (or die). - open IF_TEMPLATE, $$router_opt{'if-template'} - or die "File $$router_opt{'if-template'} couldn't be opened.\n"; - my @template_lines = readline *IF_TEMPLATE; - - $@ = undef; - eval ('local $SIG{__DIE__};'.join( "", @template_lines)); - die "Evaluation of the contents in the file \n\n$$router_opt{'if-template'}\n". - "gave the error \n\n\"$@\"\n\nExiting cfgmaker\n" if $@; - } else { - $target_lines = $default_target_lines; - } - - - if ($target_lines && not $if_ok) { # comment out the target lines if needed - $target_lines =~ s/^/\# /gm; - } - - $conf .= ($head_lines - . $problem_lines - . $target_lines - . $separator_lines); - - } - # Target generation code ends HERE. - } - } - - # print any global options which might have - # appeared on the command line after the last - # router. - if (defined $$opt{global}) { - foreach my $key (@{$$opt{global}}) { - $conf .= "$key\n"; - } - } - - if ($$opt{output}) { - debug ('base', "Writing $$opt{output}"); - open X, ">$$opt{output}" or die "ERROR: creating $$opt{output}: $!\n"; - print X $conf; - close X; - } else { - print $conf; - } -} # end GenConf - -sub IsCounterBroken ($$$) { - my $if = shift; - my $router_ref = shift; - my $v3opt = shift; - my $router = $$router_ref{routerkey}; - my $fallback = 0; - local $SNMP_Session::suppress_warnings = 3; - local $Net_SNMP_util::suppress_warnings = 3; - - my $ipv4only = $$router_ref{ipv4only}; - my $snmphost = v4onlyifnecessary($router, $ipv4only); - - if ($router =~ /:[\d.]*:[\d.]*:[\d.]*:[23]/) { - my $speed = (snmpget($snmphost, $v3opt, 'ifHighSpeed.'.$if))[0] || 'unknown'; - debug('base',"snmpget $snmphost for ifHighSpeed.$if -> $speed Mb/s"); - $SNMP_Session::errmsg = undef; - $Net_SNMP_util::ErrorMessage = undef; - my $counter = (snmpget($snmphost,$v3opt, 'ifHCInOctets.'.$if))[0] || 'unknown'; - debug('base',"snmpget $snmphost for ifHCInOctets.$if -> $counter"); - if( $speed eq 'unknown' or $counter !~ /^\d+$/ or $SNMP_Session::errmsg or $Net_SNMP_util::ErrorMessage){ - $SNMP_Session::errmsg = undef; - $Net_SNMP_util::ErrorMessage = undef; - $fallback = 1; - debug('base',"check for HighspeedCounters failed ... Dropping back to V1"); - } else { - return 0; - } - } - if ( $fallback == 1 or $$router_ref{snmpopt_current} !~ /:[\d.]*:[\d.]*:[\d.]*:[23]/) { - my $counter = (snmpget($snmphost, 'ifInOctets.'.$if))[0]; - if (defined $SNMP_Session::errmsg) { - my $error = $SNMP_Session::errmsg; - $SNMP_Session::errmsg = undef; - $error =~ s/\n/\n### /g; - return $error; - } elsif (defined $Net_SNMP_util::ErrorMessage and $Net_SNMP_util::ErrorMessage =~ /\w/) { - my $error = $Net_SNMP_util::ErrorMessage; - $Net_SNMP_util::ErrorMessage = undef; - $error =~ s/\n/\n### /g; - return $error; - } elsif (not defined $counter or $counter eq '' or $counter =~ /\D/) { - return "No counter exists for $if"; - } - } - return $fallback; -} # end IsCounterBroken - -# DeviceInfo does fallback between IPv6 and IPv4: if an IPv6 snmpwalk returns -# undef values (= an error) and the target is a hostname, then it repeats the -# query using IPv4 in case the target does not support SNMP over IPv6. -# If DeviceInfo falls back to IPv4, it sets the ipv4only field for the target -# in the routers hash. -sub DeviceInfo ($$$) { - my $router=shift; - my $routers=shift; - my $v3opt=shift; - my %DevInfo; - my $ipv4only = $$routers{$router}{ipv4only}; - - my @variables = snmpwalk(v4onlyifnecessary($router, $ipv4only),$v3opt,'1.3.6.1.2.1.1'); # walk system - if (!(defined $variables[0])) { - # Do we need to fall back to IPv4? - my ($commmunity, $host) = ($1, $2) if ($router =~ /^(.*)@([^@]+)$/); - if ( ( ! $ipv4only ) && ( $host !~ /^\[(.*)\]/) ) { - # Not using IPv4, not an IPv6 address, so a hostname - debug ('base',"No response using IPv6 for $router, trying again using IPv4"); - $$routers{$router}{ipv4only} = 1; - @variables = snmpwalk(v4onlyifnecessary($router, 1),$v3opt, '1.3.6.1.2.1.1'); - } - } - if ( defined $variables[0] ) { - my (%DevInfo, %revOIDS); - - if ($$routers{$router}{enablesnmpv3} || '' eq "yes") { - no warnings; - %revOIDS = reverse %Net_SNMP_util::OIDS; - } - else { - no warnings; - %revOIDS = reverse %SNMP_util::OIDS; - } - foreach my $variable ( @variables ) { - my ($oid, $value) = split ( ':', $variable, 2); - if ($revOIDS{'1.3.6.1.2.1.1.'.$oid}){ - $DevInfo{ $revOIDS{'1.3.6.1.2.1.1.'.$oid} } = $value; - } - } - # vendor identification - my %vendorIDs = ( - # Add your vendor here - # sysObjectID Vendora - '1.3.6.1.4.1.11863.' => 'tplink', - '1.3.6.1.4.1.43.' => '3com', - '1.3.6.1.4.1.11.' => 'hp', - '1.3.6.1.4.1.9.' => 'cisco', - '1.3.6.1.4.1.171.' => 'dlink', - '1.3.6.1.4.1.6141.' => 'wwp', - '1.3.6.1.4.1.674.10895.' => 'dellLan', - '1.3.6.1.4.1.1916.' => 'extremenetworks', - '1.3.6.1.4.1.1991.' => 'foundry', - '1.3.6.1.4.1.6027.' => 'force10', - '1.3.6.1.4.1.2636.' => 'juniper', - '1.3.6.1.4.1.94.' => 'nokiaipsofw', - '1.3.6.1.4.1.307.' => 'portmaster', - '1.3.6.1.4.1.890.' => 'zyxel', - '1.3.6.1.4.1.2272.30' => 'nortel', - '1.3.6.1.4.1.6339' => 'DCN', - '1.3.6.1.4.1.30155.' => 'openBSD', - '1.3.6.1.4.1.30065.' => 'arista', - '1.3.6.1.4.1.5624.' => 'enterasys', - '1.3.6.1.4.1.30803.' => 'Vyatta', - '1.3.6.1.4.1.3955.' => 'LinkSys', - '1.3.6.1.4.1.1588.' => 'brocade' - ); - $DevInfo{Vendor} = 'Unknown Vendor - '.$DevInfo{sysObjectID}; - foreach (keys %vendorIDs) { - $DevInfo{Vendor} = $vendorIDs{$_} if ($DevInfo{sysObjectID} =~ /\Q$_\E/); - } - debug('base',"Vendor Id: $DevInfo{Vendor}"); - return \%DevInfo; - } else { - # we just die because the snmp module has already complained - return undef; - } -} # end DeviceInfo - - -sub fmi ($$) { - my $number = shift; - my $flags = shift; - my(@short); - if ($$flags{bits}){ - $number*=8; - @short = ("bits/s","kbits/s","Mbits/s","Gbits/s"); - } else { - @short = ("Bytes/s","kBytes/s","MBytes/s","GBytes/s"); - } - my $digits=length("".$number); - my $divm=0; - while ($digits-$divm*3 > 4) { $divm++; } - my $divnum = $number/10**($divm*3); - return sprintf("%1.1f %s",$divnum,$short[$divm]); -} # end fmi - - -sub IfType ($) { - my $id = shift; - my $ift = { - '1'=>'Other', - '2'=>'regular1822', - '3'=>'hdh1822', - '4'=>'ddnX25', - '5'=>'rfc877x25', - '6'=>'ethernetCsmacd', - '7'=>'iso88023Csmacd', - '8'=>'iso88024TokenBus', - '9'=>'iso88025TokenRing', - '10'=>'iso88026Man', - '11'=>'starLan', - '12'=>'proteon10Mbit', - '13'=>'proteon80Mbit', - '14'=>'hyperchannel', - '15'=>'fddi', - '16'=>'lapb', - '17'=>'sdlc', - '18'=>'ds1', - '19'=>'e1', - '20'=>'basicISDN', - '21'=>'primaryISDN', - '22'=>'propPointToPointSerial', - '23'=>'ppp', - '24'=>'softwareLoopback', - '25'=>'eon', - '26'=>'ethernet-3Mbit', - '27'=>'nsip', - '28'=>'slip', - '29'=>'ultra', - '30'=>'ds3', - '31'=>'sip', - '32'=>'frame-relay', - '33'=>'rs232', - '34'=>'para', - '35'=>'arcnet', - '36'=>'arcnetPlus', - '37'=>'atm', - '38'=>'miox25', - '39'=>'sonet', - '40'=>'x25ple', - '41'=>'iso88022llc', - '42'=>'localTalk', - '43'=>'smdsDxi', - '44'=>'frameRelayService', - '45'=>'v35', - '46'=>'hssi', - '47'=>'hippi', - '48'=>'modem', - '49'=>'aal5', - '50'=>'sonetPath', - '51'=>'sonetVT', - '52'=>'smdsIcip', - '53'=>'propVirtual', - '54'=>'propMultiplexor', - '55'=>'100BaseVG', - '56'=>'Fibre Channel', - '57'=>'HIPPI Interface', - '58'=>'Obsolete for FrameRelay', - '59'=>'ATM Emulation of 802.3 LAN', - '60'=>'ATM Emulation of 802.5 LAN', - '61'=>'ATM Emulation of a Circuit', - '62'=>'FastEthernet (100BaseT)', - '63'=>'ISDN & X.25', - '64'=>'CCITT V.11/X.21', - '65'=>'CCITT V.36', - '66'=>'CCITT G703 at 64Kbps', - '67'=>'Obsolete G702 see DS1-MIB', - '68'=>'SNA QLLC', - '69'=>'Full Duplex Fast Ethernet (100BaseFX)', - '70'=>'Channel', - '71'=>'Radio Spread Spectrum (802.11)', - '72'=>'IBM System 360/370 OEMI Channel', - '73'=>'IBM Enterprise Systems Connection', - '74'=>'Data Link Switching', - '75'=>'ISDN S/T Interface', - '76'=>'ISDN U Interface', - '77'=>'Link Access Protocol D (LAPD)', - '78'=>'IP Switching Opjects', - '79'=>'Remote Source Route Bridging', - '80'=>'ATM Logical Port', - '81'=>'AT&T DS0 Point (64 Kbps)', - '82'=>'AT&T Group of DS0 on a single DS1', - '83'=>'BiSync Protocol (BSC)', - '84'=>'Asynchronous Protocol', - '85'=>'Combat Net Radio', - '86'=>'ISO 802.5r DTR', - '87'=>'Ext Pos Loc Report Sys', - '88'=>'Apple Talk Remote Access Protocol', - '89'=>'Proprietary Connectionless Protocol', - '90'=>'CCITT-ITU X.29 PAD Protocol', - '91'=>'CCITT-ITU X.3 PAD Facility', - '92'=>'MultiProtocol Connection over Frame/Relay', - '93'=>'CCITT-ITU X213', - '94'=>'Asymetric Digitial Subscriber Loop (ADSL)', - '95'=>'Rate-Adapt Digital Subscriber Loop (RDSL)', - '96'=>'Symetric Digitial Subscriber Loop (SDSL)', - '97'=>'Very High Speed Digitial Subscriber Loop (HDSL)', - '98'=>'ISO 802.5 CRFP', - '99'=>'Myricom Myrinet', - '100'=>'Voice recEive and transMit (voiceEM)', - '101'=>'Voice Foreign eXchange Office (voiceFXO)', - '102'=>'Voice Foreign eXchange Station (voiceFXS)', - '103'=>'Voice Encapulation', - '104'=>'Voice Over IP Encapulation', - '105'=>'ATM DXI', - '106'=>'ATM FUNI', - '107'=>'ATM IMA', - '108'=>'PPP Multilink Bundle', - '109'=>'IBM IP over CDLC', - '110'=>'IBM Common Link Access to Workstation', - '111'=>'IBM Stack to Stack', - '112'=>'IBM Virtual IP Address (VIPA)', - '113'=>'IBM Multi-Protocol Channel Support', - '114'=>'IBM IP over ATM', - '115'=>'ISO 802.5j Fiber Token Ring', - '116'=>'IBM Twinaxial Data Link Control (TDLC)', - '117'=>'Gigabit Ethernet', - '118'=>'Higher Data Link Control (HDLC)', - '119'=>'Link Access Protocol F (LAPF)', - '120'=>'CCITT V.37', - '121'=>'CCITT X.25 Multi-Link Protocol', - '122'=>'CCITT X.25 Hunt Group', - '123'=>'Transp HDLC', - '124'=>'Interleave Channel', - '125'=>'Fast Channel', - '126'=>'IP (for APPN HPR in IP Networks)', - '127'=>'CATV MAC Layer', - '128'=>'CATV Downstream Interface', - '129'=>'CATV Upstream Interface', - '130'=>'Avalon Parallel Processor', - '131'=>'Encapsulation Interface', - '132'=>'Coffee Pot', - '133'=>'Circuit Emulation Service', - '134'=>'ATM Sub Interface', - '135'=>'Layer 2 Virtual LAN using 802.1Q', - '136'=>'Layer 3 Virtual LAN using IP', - '137'=>'Layer 3 Virtual LAN using IPX', - '138'=>'IP Over Power Lines', - '139'=>'Multi-Media Mail over IP', - '140'=>'Dynamic synchronous Transfer Mode (DTM)', - '141'=>'Data Communications Network', - '142'=>'IP Forwarding Interface', - '144'=>'IEEE1394 High Performance Serial Bus', - '150'=>'MPLS Tunnel Virtual Interface', - '161'=>'IEEE 802.3ad Link Aggregate', - '162'=>'Cisco Express Forwarding Interface', - '166'=>'MPLS', - '238'=>'Asymmetric Digital Subscriber Loop', - '246'=>'Pseudowire interface type', - '251'=>'Very high speed digital subscriber line', - }; - return ($ift->{$id} || "Uknown Interface Type"); -} # end IfType - - - - - -sub options ($$) { - my $opt = shift; - my $routers = shift; - - my $noofrouter = 0; # How many routers we've seen on cmdline. - - # The $flags hash stores what we've seen in Options[_], - # Options[^] and Options[$] so far. - # A cmdline arg like --global 'Options[_]: bits' will insert - # the element $$flags{default}{bits}="set". - # Similarly --global 'Options[$]:' will delete all elements - # in $$flags{append} - # - # This was originally created to manipulate the "bits" flag - # so fmi should know when to use "bits" or "bytes". It might - # be overkill to use such a comples solution but it makes life - # easier if cfgmaker in the future has to be extended to be - # aware of other Options[] settings like gauge, growright etc. - - my %flags; - { - my $def = {}; - my $pre = {}; - my $app = {}; - %flags = (default => $def, - prepend => $pre, - append => $app); - } - - my $addrouter_ornf = addrouter($opt, - $routers, - \$noofrouter, - \%flags); - - Getopt::Long::Configure("permute"); - GetOptions( $opt, - 'help|?', - 'man', - 'subdirs=s', - 'no-down', - 'show-op-down', - 'noreversedns', - 'ifref=s', - 'ifdesc=s', - 'if-filter=s', - 'if-template=s', - 'interfaces!', - 'host-template=s', - 'community=s', - 'username=s', - 'authkey=s', - 'authpassword=s', - 'authprotocol=s', - 'contextengineid=s', - 'contextname=s', - 'privkey=s', - 'privpassword=s', - 'privprotocol=s', - 'snmp-options=s', - 'dns-domain=s', - 'version', - 'output=s', - 'global=s@', - 'enable-ipv6', - 'enablesnmpv3', - 'use-16bit', - 'zero-speed=s', - '<>', $addrouter_ornf) or pod2usage(2); - - die("cfgmaker for mrtg-2.17.7\n") if $$opt{version}; - pod2usage(-exitval => 0, -verbose => 2) if $$opt{man}; - pod2usage(-verbose => 1) if not keys %$routers; -} # end options - -# The callback routine used by GetOptions to process "non-option -# strings" (routers) among the arguments is given only ONE argument. -# However, I want it to be able to specify both the %options hash -# (for read access) and the %routers hash (for modifying) as well -# as the router's name. This makes for three arguments. -# -# The solution is to use a closure. addrouter takes a opt hash, a -# routers hash, an index to the current number of routers and a flags -# hash and then returns a function which "remembers" these -# values (the closure) but also takes an argument (the router name). - -sub addrouter() { - my $opt = shift; - my $routers = shift; - my $noofrouter = shift; - my $flags = shift; - - return sub { - my $rawname = shift; - - $$noofrouter++; # First increase the number of routers seen. - - my ($community,$routername,$routerkey,$snmpopt,$dnsdomain,$tmpname,@tmpsnmp); - - # Now make sure that the router is defined with the - # proper community, domainname and SNMP options. - # Dissect the rawname to find out what it contains. - - # First check for community: - if ($rawname =~ /^(.+)\@([^@]+)$/) { - # Community was given explicitly! - $community = $1; - $tmpname = $2 - } else { - $community = $$opt{community}; - $tmpname = $rawname; - } - # Now separate the router name from the options. We - # can't just split on the : character because a numeric - # IPv6 address contains a variable number of :'s - if( ($tmpname =~ /^(\[.*\]):(.*)$/) || ($tmpname =~ /^(\[.*\])$/) ){ - # Numeric IPv6 address between [] - ($routername, $snmpopt) = ($1, $2); - } else { - # Hostname or numeric IPv4 address - ($routername, $snmpopt) = split(':', $tmpname, 2); - } - @tmpsnmp = split(':', $snmpopt || ''); - - $routername =~ s/\.$//; # if the hostname ends in a '.' remove it - # it seems to cause trouble in some other - # parts of mrtg - - # Now setup the SNMP options. - if (not defined $$opt{'snmp-options'}) { - $snmpopt = ':' . (join ':', @tmpsnmp); # No merge needed. - } else { - my ($t,$o,@s); - my @optsnmp = split ':',$$opt{'snmp-options'}; - - # Trim first element as the SNMP options start - # with a colon and thus the first element is a - # dummy "" string not corresponding to any SNMP option - # (or rather, corresponding to a router, if there had - # been one...) - shift @optsnmp; - - while ((scalar @tmpsnmp > 0) - or (scalar @optsnmp > 0)) { - $t = shift @tmpsnmp; - $o = shift @optsnmp; - - if(not defined $t) {$t = "";} - if(not defined $o) {$o = "";} - - if($t ne "") - { - push @s, $t; - } else { - push @s, $o; - } - } - - $snmpopt = ':' . (join ':', @s); - } - - my $newopt={}; # Perhaps unecessary initialization but... - - foreach my $o (keys %$opt) { - my $ovalue = $$opt{$o}; - - $$newopt{$o} = $ovalue - unless - ($o =~ /^fullcmd$/ or - $o =~ /^community$/ or - $o =~ /^snmp-options$/ or - $o =~ /^global$/ or - $o =~ /^output$/ - ); - - # Ok, copy the --globals array from $$opt so we know - # that which global(s) to print out into the config. - push @{$$newopt{$o}}, @{$$opt{$o}} if ($o =~ /^global$/); - - # Go through these --global statements one by one. - # If anyone of them contains Options[] for any of the - # targets [_], [^] or [_], process those statements - # tenderly and populate the $$flags{}{} hashes accordingly. - for my $g (@{$$opt{"global"}}) { - if ($g =~ /^options\[([_^\$])\]:\s*(.*)$/i){ - my ($t,$fs); - $t = $1; - $fs = $2; - $t =~ s/_/default/; - $t =~ s/\^/prepend/; - $t =~ s/\$/append/; - - # If a line like "options[X]:" is found clear - # all flags for that category and then go to next - # --global 'Options[..' line if any. - if ($fs =~ /^\s*$/) { - $$flags{$t} = {}; - next; - } else { - for my $f (split /\s*,\s*/,$fs) { - $$flags{$t}{$f} = "set"; - } - } - } - } - $$opt{$o} = [] if ($o =~ /^global$/); - } - - # Now let this router get it's own copy of - # the "currently effective" flags. - # Note, Options[_] should only be considered - # if Options[^] and Options[$] both are absent. - - my $newflags = {}; - - if((0 == keys %{$$flags{prepend}}) - and (0== keys %{$$flags{append}})) { - for my $f (keys %{$$flags{default}}) { - $$newflags{$f}="set"; - } - } else { - for my $f (keys %{$$flags{prepend}}, - keys %{$$flags{append}}) { - $$newflags{$f}="set"; - } - } - - if(defined $$opt{'dns-domain'}) { - $dnsdomain=$$opt{'dns-domain'}; - } else { - $dnsdomain=""; - } - - $routerkey = - "${community}\@${routername}" - . (($dnsdomain eq "")?"":".") - . "${dnsdomain}${snmpopt}"; - - $$routers{$routerkey}= { - # rawname is the unprocessed string from the - # command line. - rawname => $rawname, - - # opt is the commandline options which are - # in effect for THIS particular router. - opt => $newopt, - - # noofrouter is the unique number for the - # router. The first router on the command - # line is given number 1, the second number 2 - # and so on. - noofrouter => $$noofrouter, - - # flags contains which --global 'Options[^_$]: flags' - # are effective for THIS particular router. - flags => $newflags, - - # community is the SNMP community used for the router - community => $community, - - # snmpopt is the SNMP options on the form - # [port[:timeout[:retries[:backoff[:version]]]]] - # The empty string simply means that no - # specific SNMP options has been given. - 'snmp-options' => $snmpopt, - snmpopt_current => $snmpopt, - - # dns-domain is a domain which should be added - # to the routers hostname. - # e.g if dns-domain is place.xyz and host is router - # the host "router.place.xyz" will be polled. - # If host is "router.dept" the poll will be against - # "router.dept.place.xyz". - 'dns-domain' => $dnsdomain, - - # routername is the routers name as given on the - # command line but with SNMP community (if given) - # and SNMP options (if given) stripped. - # - # (Yes, routername COULD be on the form - # "host.domain" or "host.subdomain.domain") - routername => $routername, - - # routerkey is the same as the has key used for the - # router, which is the router name with everything - # taken into account: community, dns-domain and - # snmp-options. This is the value used when doing - # SNMP communication with the router. - routerkey => $routerkey, - }; - } -} # end addrouter - -sub html_escape ($) { - my $s = shift || ''; - $s =~ s/&/&/g; - $s =~ s//>/g; - $s =~ s/[\n\r]+([^\n\r])/
\n $1/g; - return $s; -} # end html_escape - -sub oid_pick ($$$;@){ - my $router = shift; - my $v3opt = shift; - my @oids = @_; - local $SNMP_Session::suppress_warnings = 3; - foreach my $oid (@oids){ - local $SNMP_Session::errmsg = undef; - my $counter = snmpget($router,$v3opt,$oid); - if (not defined $SNMP_Session::errmsg and defined $counter and $counter ne '' ) { - debug('base',"oid_pick - found $oid to work for $router"); - return $oid; - } - } - debug('base',"oid_pick - none of ".(join ",",@oids)." seem to work for $router"); - return undef; -} - - -sub parsev3 ($) { - my $opt = shift; - my %v3opt; - if (!exists ($$opt{username})) { - die "SMNP V3 requires a --username paramter as part of the User Security Model"; - } else { - $v3opt{username} = $$opt{username}; - } - $v3opt{contextname} = $$opt{contextname} if exists($$opt{contextname}); - if (exists ($$opt{authkey})) { - die "Can't use both an --authkey and --authpassword in the User Security Model" if exists($$opt{authpassword}); - $v3opt{authkey} = $$opt{authkey}; - } - if (exists ($$opt{authpassword})) { - die "Use of --authpassword requires --contextengineid" if !exists($$opt{contextengineid}); - $v3opt{authpassword} = $$opt{authpassword}; - } - if (exists ($$opt{authprotocol})) { - die "Only sha and md5 are defined for --authprotocol" if $$opt{authprotocol} !~ /^(md5|sha)$/i; - die "--authprotocol can only be used with --authpassword or --authkey" if ! exists($$opt{authpassword}) and ! exists($$opt{authkey}); - ($v3opt{authprotocol}) = (lc($$opt{authprotocol}) =~ /^(md5|sha)$/); - } - if (exists ($$opt{privkey})) { - die "Can't use both an --privkey and --privpassword in the User Security Model" if exists($$opt{privpassword}); - die "Can't have privacy parameters without authentication in the User security Model" if ! exists($$opt{authpassword}) and ! exists($$opt{authkey}); - $v3opt{privkey} = $$opt{privkey}; - } - if (exists ($$opt{privpassword})) { - die "Use of --privpassword requires --contextengineid" if !exists($$opt{contextengineid}); - die "Can't have privacy parameters without authentication in the User security Model" if ! exists($$opt{authpassword}) and ! exists($$opt{authkey}); - $v3opt{privpassword} = $$opt{privpassword}; - } - if (exists ($$opt{privprotocol})) { - die "Only des, 3des, 3desede, aes, aes128 are defined for --privprotocol" if $$opt{privprotocol} !~ /^(?:3?des(?:ede)?|aes(?:128)?)$/; - die "--privprotocol can only be used with --privpassword or --privkey" if ! exists($$opt{privpassword}) and ! exists($$opt{privkey}); - $v3opt{privprotocol} = lc($$opt{privprotocol}); - } - return %v3opt; -} - - -sub init () { - snmpmapOID('sysObjectID' => '1.3.6.1.2.1.1.2.0', - 'CiscolocIfDescr' => '1.3.6.1.4.1.9.2.2.1.1.28', - 'CiscoCatalystPortName' => '1.3.6.1.4.1.9.5.1.4.1.1.4', - 'ppPortName' => '1.3.6.1.4.1.2272.1.4.10.1.1.35', - 'vlanTrunkPortDynamicStatus'=> '1.3.6.1.4.1.9.9.46.1.6.1.1.14', - 'vmVlan' => '1.3.6.1.4.1.9.9.68.1.2.2.1.2', - 'ifAlias' => '1.3.6.1.2.1.31.1.1.1.18'); -} # end init - -__END__ - -=pod - -=head1 NAME - -cfgmaker - Creates mrtg.cfg files (for mrtg-2.17.7) - -=head1 SYNOPSIS - -cfgmaker [options] [community@]router [[options] [community@]router ...] - -=head1 OPTIONS - - --ifref=nr interface references by Interface Number (default) - --ifref=ip ... by Ip Address - --ifref=eth ... by Ethernet Number - --ifref=descr ... by Interface Description - --ifref=name ... by Interface Name - --ifref=type ... by Interface Type - You may also use multiple options separated by commas, - in which case the first available one is used: - e.g. --ifref=ip,name,nr - - --ifdesc=nr interface description uses Interface Number (default) - --ifdesc=ip ... uses Ip Address - --ifdesc=eth ... uses Ethernet Number - --ifdesc=descr ... uses Interface Description - --ifdesc=name ... uses Interface Name - --ifdesc=catname ... uses CatOS Interface Name - --ifdesc=ppname ... uses Passport Port Name - --ifdesc=alias ... uses Interface Alias - --ifdesc=type ... uses Interface Type - You may also use multiple options separated by commas, - in which case the first available one is used: - e.g. --ifdesc=catname,ppname,descr,alias,ip,name,nr - - --if-filter=f Test every interface against filter f to decide wether - or not to include that interface into the collection. - Currently f is being evaluated as a Perl expression - and it's truth value is used to reject or accept the - interface. - (Experimental, under development, might change) - - --if-template=templatefile - Replace the normal target entries for the interfaces - with an entry as specified by the contents in the file - templatefile. The file is supposed to contain Perl - code to be executed to generate the lines for the - target in the configuration file. - (Experimental, under development, might change) - - --host-template=templatefile - In addition to creating targets for a host's interfaces - do also create targets for the host itself as specified - by the contents in the file templatefile. The file is - supposed to contain Perl code to be executed to generate - the lines for the host related targets (such as CPU, - ping response time measurements etc.) in the config- - uration file. - (Experimental, under development, might change) - - --global "x: a" add global config entries - - --no-down do not look at admin or opr status of interfaces - - --show-op-down show interfaces which are operatively down - - --zero-speed=spd use this speed in bits-per-second as the interface - speed for all interfaces that return a speed of 0 - via ifSpeed/ifHighSpeed. 100Mbps = 100000000 - - --subdirs=format give each router its own subdirectory, naming each per - "format", in which HOSTNAME and SNMPNAME will be - replaced by the values of those items -- for instance, - --subdirs=HOSTNAME or --subdirs="HOSTNAME (SNMPNAME)" - - --noreversedns do not reverse lookup ip numbers - - --community=cmty Set the default community string to "cmty" instead of - "public". - - --enable-ipv6 Enable IPv6 support, if the required libraries are - present. Numeric IPv6 addresses must be enclosed - in square brackets, e.g. public@[2001:760:4::1]:161 - - --use-16bit Use 16bit SNMP request IDs to query all routers. - - --snmp-options=:[][:[][:[][:[][:]]]] - - Specify default SNMP options to be appended to all - routers following. Individual fields can be empty. - Routers following might override some or all of the - options given to --snmp-options. - - --dns-domain=domain - Specifies a domain to append to the name of all - routers following. - - --nointerfaces Don't do generate any configuration lines for interfaces, - skip the step of gathering interface information and - don't run any interface template code. - - --interfaces Generate configuration lines for interfaces (this is the - default). The main purpose of this option is to negate - an --nointerfaces appearing earlier on the command line. - - --help brief help message - --man full documentation - --version print the version of cfgmaker - - --output=file output filename default is STDOUT - -=head1 DESCRIPTION - -B creates MRTG configuration files based on information -pulled from a router or another SNMP manageable device. - -[IB<@>]I - -I is the community name of the device you want to create a -configuration for. If not specified, it defaults to 'B'; you might -want to try this first if you do not know the community name of a -device. If you are using the wrong community name you will get no -response from the device. - -I is the DNS name or the IP number of an SNMP-managable device. -Following the name you can specify 6 further options separated by -colons. The full syntax looks like this: - -B[:[B][:[B][:[B][:[B][:B]]]]] - -Of special interest may be the last parameter, B. If you set this to -'2' then your device will be queried with SNMP version 2 requests. This -allows you to poll the 64 bit traffic counters in the device and will thus work -much better with fast interfaces (no more counter overrun). Note that the -order in which the routers are specified on the command line do matter as -the same order is used when the configuration file is generated. The first -specified router has it's configuration lines genrated first, followed by -the lines belonging to the next router and so on. - -Note that the first line of the generated cfg file will contain all the -commandline options you used for generating it. This is to allow for the -easy 'regeneration' in case you want to add newhosts or make some other -global change. - -=head2 Configuration - -Except for the B<--output> and B<--global> options, all options affect -only the routers following them on the command line. If an option -specified earlier on the command line reappears later on the command -line with another value, the new value overrides the old value as far as -remaining routers are concerned. This way options might be tailored for -groups of routers or for individual routers. - -See B<--output> and B<--global> for how their behaviour is affected by -where or how many times they appear on the command line. - -See the B below on how to set an option differently for -multiple routers. - -=over - -=item B<--help> - -Print a brief help message and exit. - -=item B<--man> - -Prints the manual page and exits. - -=item B<--version> - -Print the version of cfgmaker. This should match the version of MRTG -for which config files are being created. - -=item B<--ifref> B|B|B|B|B - -Select the interface identification method. Default is B which -identifies the router interfaces by their number. Unfortunately the -interface numbering scheme in an SNMP tree can change. Some routers -change their numbering when new interfaces are added, others change -thier numbering every full moon just for fun. - -To work around this sad problem MRTG can identify interfaces by 4 -other properties. None of these works for all interfaces, but you -should be able to find one which does fine for you. Note that -especially ethernet addrsses can be problematic as some routers have -the same ethernet address on most of their interface cards. - -Select B to identify the interface by its IP number. Use B to -use the ethernet address for identification. Use B to use -the Interface description. Or use B to use the Interface name. - -You can specify multiple properties if you wish, separated by commas. -In this case, cfgmaker will use the first item in the list which -can provide unique identification. This allows you to specify, for -example, to use IP address and to use ifName if this is not defined: - --ifref ip,name - -If your chosen method does not allow unique interface identification on -the device you are querying, B will tell you about it. - -=item B<--ifdesc> B|B|B|B|B|B|B - -Select what to use as the description of the interface. The description -appears in the C property for the target as well as the text header -in the HTML code defined in the target's C. Default is to use -B which is just the interface number which isn't always useful -to the viewer of the graphs. - -There are 6 other properties which could be used. Use B if you want -to use the interface's IP-address. Use B if you want to use the -interface's ethernet address. If you want a better description, you can -use either B, B or B. Exactly what each of these do -varies between different equipment so you might need to experiment. For -instance, for a serial interface on a Cisco router running IOS using B -might result in C<"S0"> being the interface description , B might result -in C<"Serial0"> and B might result in C<"Link to HQ"> (provided that is -what is used as the interface's C in the router's configuration). - -Finally, if you want to describe the interface by it's Btype -(i.e C<"ethernetCSMA">, C<"propPointtoPoint"> etc) you can use B. - -You can specify multiple properties if you wish, separated by commas. -In this case, cfgmaker will use the first item in the list which -is available for this interface. This allows you to specify, for -example, to use any of the different aliases in order of preference. - -=item B<--if-filter> 'B' - -First of all, this is under some development and is experimental. - -Use this if you want to have better control over what interfaces gets -included into the configuration. The B is evaluated -as a piece of Perl code and is expected -to return a truth value. If true, include the interface and if false, -exclude the interface. - -For a further discussion on how these filters work, see the section -L
below. - -=item B<--if-template> B - -First of all, this is under some development and is experimental. - -Use this if you want to control what the line for each target should look -like in the configuration file. The contents of the file B -will be evaluated as a Perl program which generates the lines using certain -variables for input and output. - -For a further discussion on how these templates work, see the section -L
below. - -=item B<--host-template> B - -First of all, this is under some development and is experimental. - -Use this if you want to have some extra targets related to the host itself -such as CPU utilization, ping response time to the host, number of busy -modems etc. The contents of the file B will be evaluated -once per host as a Perl program which generates the lines using certain -variables for input and output. - -For a further discussion on how these templates work, see the section -L
below. - -=item B<--community> B - -Use this to set the community for the routers following on the command -line to B. Individual routers might overrride this -community string by using the syntax BB<@>B. - -=item B<--enable-ipv6> - -This option enables IPv6 support. It requires the appropriate perl -modules; if they are not found then IPv6 is disabled (see the ipv6 -documentation). - -cfgmaker will use IPv6 or IPv4 depending on the target. If the target -is a numeric address, the protocol depends on the type of address. If the -target is a hostname, cfgmaker will try to resolve the name first to an -IPv6 address then to an IPv4 address. - -IPv6 numeric addresses must be specified between square braces. - -For example: - - cfgmaker --enable-ipv6 [2001:760:4::1]:165:::2 - -If the target has both an IPv6 address and an IPv4 address with the same -hostname, cfgmaker first queries the target using IPv6 and falls back to -IPv4 if it fails. This is useful for targets which don't support SNMP -over IPv6. - -=item B<--use-16bit> - -This option forces the use of 16bit SNMP request IDs. Some broken SNMP -agents do not accept 32bit request IDs. Try to avoid this option as much -as possible, complain to your agent vendor instead. - -=item B<--snmp-options> :[B][:[B][:[B][:[B][:B]]]] - -Use this to set the default SNMP options for all routers following on the -command line. Individual values might be omitted as well as trailing -colons. Note that routers might override individual (or all) values -specified by B<--snmp-options> by using the syntax - -B[:[B][:[B][:[B][:[B][:B]]]]] - -=item B<--global> B<">IB<"> - -Use this to add global options to the generated config file. -You can call B<--global> several times to add multiple options. -The line will appear in the configuration just before the config for -the next router appearing on the command line. - - --global "workdir: /home/mrtg" - -If you want some default Options you might want to put - - --global "options[_]: growright,bits" - -Specifying B<--global> after the last router on the command line will -create a line in the configuration file which will appear after all the -routers. - -=item B<--noreversedns> - -Do not try to reverse lookup IP numbers ... a must for DNS free environments. - -=item B<--no-down> - -Normally cfgmaker will not include interfaces which are marked -anything but administratively and operationally UP. With this -switch you get them all. - -=item B<--show-op-down> - -Include interfaces which are operatively down. - -=item B<--zero-speed> I - -Assign this speed in bits-per-second to all interfaces which return 0 -for ifSpeed and ifHighSpeed. Some switches, notably Foundry equipment, -return a speed of zero for some interfaces. For example, to have -all interfaces reporting zero set to 100Mbps, use ---zero-speed=100000000. - -=item B<--subdirs> I - -Give each router its own subdirectory for the HTML and graphics (or -.rrd) files. The directory name is the given I string with a -couple of pattern replacements. The string "HOSTNAME" will be -replaced by the hostname of the router (however you specified it on -the B commandline -- it may be an actual hostname or just an -IP address), and "SNMPNAME" will be replaced with the device's idea of -its own name (the same name that appears on the right side of the -"Title" lines). For instance, a call like: - - cfgmaker --subdirs=HOSTNAME__SNMPNAME public@10.10.0.18 - -would result in the generation of lines looking something like: - - Directory[10.10.0.18_1]: 10.10.0.18__fp2200-bothrip-1.3 - -=item B<--output> I - -Write the output from B into the file I. The default -is to use C. B<--output> is expected to appear only once on the -command line. If used multiple times, the file specified by the last -B<--output> will be used. - -=item B<--nointerfaces> - -Don't generate configuration lines for interfaces. - -This makes cfgmaker skip all steps related to interfaces which means -it will not do any polling of the router to retrieve interface -information which speeds up the execution of cfgmaker and it will -neither run any interface templates. - -=item B<--interfaces> - -This makes cfgmaker generate configuration lines for interfaces (the -default behaviour). - -The main usage of this option is to negate an --nointerfaces appearing -earlier on the command line. - - -=back - -=head2 SNMP V3 Options - -B supports SNMP V3 using the B perl module. There are optional -parameters affecting SNMP operation. - -=over - -=item --enablesnmpv3 {yes|no} - -The B<--enablesnmpv3> option is an optional flag to check for the presence of -the B libraries. B will try to determine whether this flag is -required and will set the values automatically. - -=back - -=head3 SNMPv3 Arguments - -A SNMP context is a collection of management information accessible by a SNMP -entity. An item of management information may exist in more than one context -and a SNMP entity potentially has access to many contexts. The combination of -a contextEngineID and a contextName unambiguously identifies a context within -an administrative domain. In a SNMPv3 message, the contextEngineID and -contextName are included as part of the scopedPDU. All methods that generate -a SNMP message optionally take a B<--contextengineid> and B<--contextname> -argument to configure these fields. - -=over - -=item Context Engine ID - -The B<--contextengineid> argument expects a hexadecimal string representing -the desired contextEngineID. The string must be 10 to 64 characters (5 to -32 octets) long and can be prefixed with an optional "0x". Once the -B<--contextengineid> is specified it stays with the object until it is changed -again or reset to default by passing in the undefined value. By default, the -contextEngineID is set to match the authoritativeEngineID of the authoritative -SNMP engine. - -=item Context Name - -The contextName is passed as a string which must be 0 to 32 octets in length -using the B<--contextname> argument. The contextName stays with the object -until it is changed. The contextName defaults to an empty string which -represents the "default" context. - -=back - -=head3 User-based Security Model Arguments - -The User-based Security Model (USM) used by SNMPv3 requires that a securityName -be specified using the B<--username> argument. The creation of a Net::SNMP -object with the version set to SNMPv3 will fail if the B<--username> argument -is not present. The B<--username> argument expects a string 1 to 32 octets -in length. - -Different levels of security are allowed by the User-based Security Model which -address authentication and privacy concerns. A SNMPv3 target will -derive the security level (securityLevel) based on which of the following -arguments are specified. - -By default a securityLevel of 'noAuthNoPriv' is assumed. If the B<--authkey> -or B<--authpassword> arguments are specified, the securityLevel becomes -'authNoPriv'. The B<--authpassword> argument expects a string which is at -least 1 octet in length. Optionally, the B<--authkey> argument can be used so -that a plain text password does not have to be specified in a script. The -B<--authkey> argument expects a hexadecimal string produced by localizing the -password with the authoritativeEngineID for the specific destination device. -The C utility included with the Net::SNMP distribution can be used to create -the hexadecimal string (see L). - -Two different hash algorithms are defined by SNMPv3 which can be used by the -Security Model for authentication. These algorithms are HMAC-MD5-96 "MD5" -(RFC 1321) and HMAC-SHA-96 "SHA-1" (NIST FIPS PUB 180-1). The default -algorithm used by the module is HMAC-MD5-96. This behavior can be changed by -using the B<--authprotocol> argument. This argument expects either the string -'md5' or 'sha' to be passed to modify the hash algorithm. - -By specifying the arguments B<--privkey> or B<--privpassword> the securityLevel -associated with the object becomes 'authPriv'. According to SNMPv3, privacy -requires the use of authentication. Therefore, if either of these two -arguments are present and the B<--authkey> or B<--authpassword> arguments are -missing, the creation of the object fails. The B<--privkey> and -B<--privpassword> arguments expect the same input as the B<--authkey> and -B<--authpassword> arguments respectively. - -The User-based Security Model described in RFC 3414 defines a single encryption -protocol to be used for privacy. This protocol, CBC-DES "DES" (NIST FIPS PUB -46-1), is used by default or if the string 'des' is passed to the -B<--privprotocol> argument. By working with the Extended Security Options -Consortium http://www.snmp.com/eso/, the module also supports additional -protocols which have been defined in draft specifications. The draft -http://www.snmp.com/eso/draft-reeder-snmpv3-usm-3desede-00.txt -defines the support of CBC-3DES-EDE "Triple-DES" (NIST FIPS 46-3) in the -User-based Security Model. This protocol can be selected using the -B<--privprotocol> argument with the string '3desede'. The draft -http://www.snmp.com/eso/draft-blumenthal-aes-usm-04.txt -describes the use of CFB128-AES-128/192/256 "AES" (NIST FIPS PUB 197) in the -USM. The three AES encryption protocols, differentiated by their key sizes, -can be selected by passing 'aescfb128', 'aescfb192', or 'aescfb256' to the -B<-privprotocol> argument. - -=head2 Details on Filters - -The purpose of the filters is to decide which interfaces to accept and -which interfaces to reject. This decision is done for each interface by -evaluating the filter expression as a piece of Perl code and investigating -the result of the evaluation. If true, accept the interface otherwise -reject it. - -When working with filters, remember that Perl has it's own idea of what truth -and false is. The empty string "" and the string "0" are false, all other -strings are true. This further imples that any integer value of 0 is -false as well as any undef value. It also implies that all references -are considered true. - -As the filter is evaluated as a Perl expression, several useful constructs -in Perl are worth mentioning: - -Expressions might be grouped by using parentheses "()". Expressions might -be combined using boolean operators such as the following: - -=over - -=item "B" (equivalent with "B<&&>") - -Boolean "and" of the two expressions, is only true if both expressions are -true. Example: I B I - -=item "B" (equivalent with "B<||>") - -Boolean "or" of the two expressions, is true if either or both expressions -are true. Example: I B I - -=item "B" (equivalent with "B") - -Boolean negation of a single expression. Example: B I . -Yet another example: BI - -=back - -(For more details on this I recommend a book on Perl) - -=head3 Predefined Filter Variables - -To facilitate, there are a number of predefined values available to use -in the filter. Note that these variables are also available when templates -interfaces are evaluated (but not host templates). - -Caveat: All these variables' names begin with a dollar sign ($), which -is a syntactic requirement for scalar variables in Perl. The danger here -is that the dollar sign in many shells is an active character (often -used for shell variables exactly as in Perl variables) so it is important -to ensure that the Perl expression isn't evaluated by the command line -shell as shell code before being passed to cfgmaker as command line -arguments. In shells like Bourne shell, ksh shell or bash shell, placing -the entire expression within single qoutes will avoid such accidental -evaluation: - - '--if-filter=($default_iftype && $if_admin)' - -=over - -=item B<$if_type> - -This is an integer specifying the interface type as -per the SNMP standards and as reported by the polled device. A complete list -of interface types would be impractical for this document , but there are -a number predefined varables below. Normally, cfgmaker puts in the target's -PageTop this iftype value within paranthesis after the name of the interface -type. (e.g "propPointToPointSerial (22)"). - -Here's a list of some of the most common interface types by number: - - 6 ethernetCsmacd - 7 iso88023Csmacd - 9 iso88025TokenRing - 15 fddi - 19 E1 - 20 basicISDN - 21 primaryISDN - 22 propPointToPointSerial - 23 ppp - 24 softwareLoopback - 30 ds3 - 32 frame-relay - 33 rs232 - 37 atm - 39 sonet - 44 frameRelayService - 46 hssi - 49 aal5 - 53 propVirtual - 62 Fast Ethernet (100BaseT) - 63 ISDN & X.25 - 69 Full Duplex Fast Ethernet (100BaseFX) - 94 Asymetric Digital Subscriber Loop (ADSL) - 117 Gigabit Ethernet - 134 ATM Sub Interface - -=item B<$default> - -True if and only if cfgmaker normally should -accepted the interface based on the interfaces administrative and -operational state (taking the flags B<--no-down> and B<--show-op-down> into -account) and it's type (and a few other things). - -=item B<$default_ifstate> - -True if and only if cfgmaker would have accepted the -interface based on it's operational and administrative states (also taking -into account the presence of the flags B<--no-down> and B<--show-op-down>). - -=item B<$default_iftype> - -True if and only if cfgmaker would have accepted the -interface based on it's type (and a few type specific details in addition). - -=item B<$if_admin> - -True if and only if the interface is in an adminstrative up -state. - -=item B<$if_oper> - -True if and only if the interface is in an operational up -state. - -=back - -A number of variables are also predefined to easily decide if an interface -belong to a certain cathegory or not. Below is all those variables listed -together with which if_type numbers each variable will be true for. Note -that some variables refer to other variables as well. - -=over - -=item B<$if_is_ethernet> - -True for ethernet interfaces (nr 6, 7, 26, 62, 69 and 117). - -=item B<$if_is_isdn> - -True for various ISDN interface types (nr 20, 21, 63, 75, 76 and 77) - -=item B<$if_is_dialup> - -True for dial-up interfaces such as PPP as well -as ISDN. (nr 23, 81, 82 and 108 in addition to the numbers of -B<$if_is_isdn>). - -=item B<$if_is_atm> - -True for miscellaneous ATM related interface types (nr 37, 49, 107, 105, -106, 114 and 134). - -=item B<$if_is_wan> - -True for WAN interfaces point to point, Frame Relay and High Speed Serial ( 22,32,44,46) - -=item B<$if_is_lan> - -True for LAN interfaces (8, 9, 11, 15, 26, 55, 59, 60 and 115 in addition -to the numbers of B<$if_is_ethernet>). - -=item B<$if_is_dsl> - -True for ADSL, RDSL, HDSL and SDSL (nr 94, 95, 96, 97) - -=item B<$if_is_loopback> - -True for software loopback interfaces (nr 24) - -=item B<$if_is_ciscovlan> - -True for Cisco VLAN interfaces (interfaces with the -word Vlan or VLAN in their ifdescs) - -=item B<$if_vlan_id> - -Returns the vlan id associated with a specific port -on Cisco Catalyst switches under both Catalyst OS -and IOS, and 3Com switches. If it is not a vlan interface, will return undef. - -=item B<$if_cisco_trunk> - -Returns the trunking state of a specific port -on Cisco Catalyst switches under both Catalyst OS -and IOS. Returns "1" if the interface is a trunk, undef otherwise. - -=item B<$if_MTU> - -Returns the Maximum Transfer Unit associated with a specific port. - -=back - -Besides that, you can also use the variables defined for templates below. -Further, all the variables available in cfgmaker is at the scripts disposal -even if the use of such features is discouraged. More "shortcuts" in the -form of variables and functions will be made available in the future instead. - -=head3 Examples on Filters - -The following filter will not affect which interfaces get's included or -excluded, it will make cfgmaker behave as normally. - - '--if-filter=$default' - -The following filter will make cfgmaker exclude PPP (23) interfaces: - - '--if-filter=$default && $if_type!=23' - -The following filter will make cfgmaker behave as usual except that it will -consider the operational state of an interface irrelevant but still reject -all interfaces which are administratively down. - - '--if-filter=$if_admin && $default_iftype' - -=head2 Details on Templates - -The contents of the template files are evaluated as a Perl program. A -number or Perl variables are available for the program to read and others -are used to be written to. - -As quite a few of the predefined variables has values which are are supposed -to be used in HTML code some of them have an "HTML-escaped" variant, e.g -$html_syslocation is the HTML escaped variant of $syslocation. The HTML -escaping means that the chars "<", ">" and "&" are replaced by "<", -">" and "&" and that newlines embedded in the string are prepended -with "
" and appended with a space character (if a newline is last in the -string it is not touched). - -=head3 Writable Template Variables - -These are the variables available to store the configuration lines in. -Some of them are initialized prior to the evaluation of the template but -such content normally is comments for inclusion in the final configuration -file so those variables might be reset to the empty string in the template -code to eliminate the comments. The other way around is also possible, the -contents of these variables might be extended with further information -for various reasons such as debugging etc. - -Once the template has been evaluated, the following happens: if the -template is a interface template and the actual interface for some reason -is rejected and thus needs to be commented out, all the lines in the -variable B<$target_lines> are turned into comments by adding a hash mark -("#") at their beginning. Then all the variables B<$head_lines>, -B<$problem_lines> , B<$target_lines> and B<$separator_lines> -are concatenated together to form the lines to add to the configuration file. - -=over - -=item B<$target_lines> - -This variable is the placeholder for the configuration lines created -by the template. B<$target_lines> is predefined to be empty when -the template code is evaluated. - -=item B<$head_lines> - -This variable is intended to be the placeholder for the comment line -appearing just before the target in the configuration file. It is -initialized with that comment line before the evaluation of the template -code and if the template doesn't modify B<$head_lines> during evaluation, -the comment will look like usual in the config file. - -=item B<$problem_lines> - -This variable is intended to be the placholder for the comment lines -describing any problems which might have been encountered when trying -to add the target into the configuration. For host templates it's -normally not used and for those it's predefined as the empty string. -For interface templates B<$problem_lines> is predefined with -the error description comments which cfgmaker normally would use for -rejected interfaces or as the empty string for accepted interfaces. - -It is possible to test against B<$problem_lines> to find out if -an interface will be included or rejected but this is not recommended. -Test against B<$if_ok> instead. - -=item B<$separator_lines> - -This variable is the placeholder for the string to use as the separator -between the code for individual targets. The contents of this variable -is put after each target (so the lines will appear after the end of the -last target in the config as well). - -=back - -=head3 Predefined Template Variables - -All the variables below are available for interface templates to use. -For host templates, only those listed under L -are available. - -For interface templates the variables listed under -L are also available. - -=head3 Host and System Variables - -=over - -=item B<$router_name> - -This is the fully qualified name for the router. It is affected by the -following items on the command line: the router name itself and -B<--dns-domain>. - -=item B<$router_connect> - -This is the reference string for the router being polled. It is on the -form community@router possibly followed by some snmp options. It is -affected by the following items on the command line: the router name -itself, B<--community>, B<--snmp-options> and B<--dns-domain>. -(There's no HTML escaped variant available) - -=item B<$directory_name> - -This variable should contain the directory name as cfgmaker normally would -use as the value for the "Directory[]" directive. The value is determined -by the B<--subdirs> command line option. If B<--subdirs> isn't specified -B<$directory_name> will be the empty string. (There's no HTML escaped -variant available) - -=item B<$syscontact> - -This variable is the router's SNMP sysContact value. (HTML escaped -variant: B<$html_syscontact>) - -=item B<$sysname> - -This variable is the router's SNMP sysName value. (No HTML escaped -variant available) - -=item B<$syslocation> - -This variable is the router's SNMP sysLocation value. (HTML escaped -variant: B<$html_syslocation>) - -=item B<$sysdescr> - -This variable is the router's SNMP sysDescr value. It is normally not used -by cfgmaker but might be useful in a template. (HTML escaped variant: -B<$html_sysdescr>) - -=back - -=head3 Interface Target Related Variables - -=over - -=item B<$target_name> - -This is what cfgmaker normally would use as the the name of the target. -The target name is what is found within the square brackets, "[]", for target -directives. (There's no HTML escaped variant available) - -=item B<$if_ref> - -This the reference string for the interface. It is expected to be used -in the "Target[xyz]" directive to distinguish what interface to use. The -value of this variable is affected by the B<--ifref> command line option. -It is normally used together with B<$router_connect>. -(There's no HTML escaped variant available) - -=item B<$if_ok> - -This variable is true if the interface is going to be included into the -configuration file, otherwise false. Don't test against other variables -such as B<$problem_lines> to find out if an interface will be rejected -or not, use this B<$if_ok> instead. - -=item B<$default_target_lines> - -This variable contains all the target lines which cfgmaker by default outputs -for this interface. It's useful if you want to have the "standard target" -but want to add some extra lines to it by using a template. - -=back - -By default cfgmaker uses the following directives for each target it -generates: Target[], SetEnv[], MaxBytes[], Title[], PageTop[] and if -there is any directory specified also the Directory[] directive. - -To facilitate the creation of templates which generates target configs -which are similar to the default one, each of the above mentioned -directive lines have a corresponding variable containing the line as -cfgmaker would have output it by default. - -Note that none of these have a HTML escaped variant, text in them is -HTML escaped where needed. Also note that they do not have any newline -at the end. - -=over - -=item B<$default_target_directive> - -This variable contains the default string for the Target[] directive line. - -=item B<$default_setenv_directive> - -This variable contains the default string for the SetEnv[] directive line. - -=item B<$default_directory_directive> - -This variable contains the default string for the Directory[] directive line -which means it is an empty string (with no newline) if there's no directory. - -=item B<$default_maxbytes_directive> - -This variable contains the default string for the MaxBytes[] directive line. - -=item B<$default_title_directive> - -This variable contains the default string for the Title[] directive line. - -=item B<$default_pagetop_directive> - -This variable contains the default string for the PageTop[] directive lines. - -=back - -=head3 Interface Network Configuration Variables - -=over - -=item B<$if_ip> - -This variable should contain the IP-address of the interface, if any has -been assigned to it. (There's no HTML escaped variant available) - -=item B<$ifindex> - -This variable is the SNMP ifIndex for the interface which per definition -always is an integer. (There's no HTML escaped variant available) - -=item B<$if_index> - -Equivalent with B<$ifindex>. - -=item B<$if_eth> - -Contains the ethernet address of the interface, if any. (There's no HTML -escaped variant available) - -=item B<$if_speed> - -This variable is the speed in bytes/second (with prefixes). (There's no -HTML escaped variant available) - -=item B<$if_speed_str> - -This variable is a cooked speed description which is either in bits or -bytes depending on wether or not the bits option is active and also with -the proper prefix for the speed (k, M, G etc). (No HTML escaped variant -available) - -=item B<$if_type_desc> - -This variable is a textual description of the interface type. (HTML -escaped variant: B<$html_if_type_desc>) - -=item B<$if_type_num> - -This variable the integer value corresponding to the interface type (for a -listing for the value for the more common interface types, see the section -DETAILS ON FILTERS above). (No HTML escaped variant available) - -=item B<$if_dns_name> - -This is the DNS name for the interface. (No HTML escaped variant available) - -=back - -=head3 Interface Name, Description and Alias Variables - -It might seem confusing with both I, I and I in -this context and to some extent it is. I and I are -usually supported on most equipment but how they are used varies, both -between manufacturers as well as between different cathegories of equipment -from the same manufacturer. The I is at least supported by Cisco -IOS, and that variable contains whatever is used in the IOS statement -called "description" for the interface (not to be confused with the SNMP -variables for I). - -For better control from the command line consider B<$if_title_desc> which contents -are controlled by the B<--if-descr> command line option. - -=over - -=item B<$if_snmp_descr> - -This variable should contain the "raw" description of the interface as -determined by the SNMP polling of the router. (HTML escaped variant: -B<$html_if_snmp_descr>) - -=item B<$if_snmp_name> - -The "raw" name for the interface as provided by SNMP polling. (HTML escaped -variant: B<$html_if_snmp_name>) - -=item B<$if_snmp_alias> - -The "raw" ifAlias for the interface as provided by SNMP polling. (HTML -escaped variant: B<$html_if_snmp_alias>) - -=item B<$if_cisco_descr> - -The "raw" CiscolocIfDescr for the interface as provided by SNMP polling. -(HTML escaped variant: B<$html_if_cisco_descr>) - -=item B<$if_description> - -This is the "cooked" description string for the interface, taking into account -the SNMP values found for the interface's RDescr, ifAlias and -CiscolocIfDescr. (HTML escaped variant: B<$html_if_description>) - -=item B<$if_title> - -The full string cfgmaker by default would have used for the Title[] directive -in the configuration as well as the content of the topmost H1 tag in the -PageTop[]. Is composed by the contents of B<$desc_prefix>, -B<$if_title_desc> and B<$sysname>. - -As B<$if_title> depends on B<$if_title_desc>, it is possible to indirectly -control B<$if_title> by using the command line option B<--if-descr>. - -(HTML escaped variant: B<$html_if_title>) - -=item B<$if_port_name> - -If the host is a Cisco Catalyst LAN switch, this variable is the name of -that port. (No HTML escaped variant available) - -=item B<$if_pp_port_name> - -If the host is a Nortel Passport LAN switch, this variable is the name of -that port. (No HTML escaped variant available) - -=item B<$desc_prefix> - -This variable is a prefix of the description of what the target is to use in -the "Title[]" directive and in the H1 section of the "PageTop[]". Default is -"Traffic analysis for ". (HTML escaped variant: B<$html_desc_prefix>) - -=item B<$if_title_desc> - -This is the description of the interface normally used by cfgmaker as part -of the variable B<$if_title>. The latter is used as the full string in the -"Title[]" directove and the H1 section in the PageTop[]. - -B<$if_title_desc> is controlled by the command line option B<--if-descr> -which indirectly controls the contents of B<$if_title> - -(HTML escaped variant: B<$html_if_title_desc>) - -=back - -=head3 Help Functions for Templates - -The following functions exists to facilitate the writing of host and -interface templates. - -=over - -=item B)> - -B takes a string as an argument and returns a new string -where the following substitutions has been done: the chars "<", ">" and -"&" are replaced by "<", ">" and "&" and that newlines embedded -in the string are prepended with "
" and appended with a space character -(newlines at the end of the string are not touched). - -=item B - -This function will try to poll each of the oids specified until -it is successful or has run out of oids. It will return the name of the -first oid that worked or undef if it is not successful - -=back - -=head3 Example Template Files - -=head4 Template Example 1: Eliminating Rejected Targets From Appearing - -This template file generates exactly the same configuration code per -interface as cfgmaker does by default, with the exception that it eliminates -all lines (comments as well as config code) for an interface if the -interface happens to be rejected. - - if(not $problem_lines) - { - $target_lines .= <$html_desc_prefix$html_if_title_desc -- $sysname -
- - - - - - - - - - - - - - - - - - - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - ECHO - } else { - $head_lines=""; - $problem_lines=""; - $target_lines=""; - $separator_lines=""; - } - -=head3 Template Example 2: Simplier Version of Example 1 - -Example 1 was partly intended to demonstrate how to customize the generation -of interface targets but also to provide a hint of how the variables are -used in the "default" template which one could consider that cfgmaker -normally uses. - -If you're only intrested in the easiest way of entirely eliminating those -reject interfaces, the template below would do the job as well by using -B<$default_target_lines>. - - if($if_ok) { - $target_lines = $default_target_lines; - } else { - $head_lines=""; - $problem_lines=""; - $target_lines=""; - $separator_lines=""; - } - -=head3 Template Example 3: Creating CPU Targets for Hosts - -Below is an example of a host template. - - $head_lines .= <$router_name CPU load -
-
System:$sysname in $html_syslocation
Maintainer:$html_syscontact
Description:$html_if_description
ifType:$html_if_type_desc ($if_type_num)
ifName:$html_if_snmp_name
Port Name:$if_port_name
Port Name:$if_pp_port_name
Max Speed:$if_speed_str
Ip:$if_ip ($if_dns_name)
- - - - - - - - - - - - - - - - -
System:$router_name in $html_syslocation
Maintainer:$html_syscontact
Description:$html_sysdescr
Resource:CPU.
-
- ECHO - - -=head1 EXAMPLES - -The first example creates a config file for I: the router -has the community name I. Interfaces get identified by their -IP number. Two global options get added to the config file. The -config file gets redirected to I. The '\' signs at the end -of the line mean that this command should be written on a single line. - - cfgmaker --global "WorkDir: /home/tobi" \ - --global "Options[_]: growright,bits" \ - --ifref=ip \ - public@router.place.xyz > mrtg.cfg - -Note: if cfgmaker is not in your path, but you are in the directory where -cfgmaker is stored, you can start it with ./cfgmaker - -The next example creates a config file for four devices: -I, I, I and -I all with the community I. - -The two routers will have B<--ifref> set to B whilst the two -switches will use B<--ifref> set to B. Further the routers will -use B<--ifdesc> set to B and I will use -B<--ifdesc> set to B whilst I use B instead. - -Finally, there will be two Options lines inserted in the configuration: -One will be in the beginning, whilst the other will be inserted after -the lines related to the two routers but before those lines related -to the switches. - - cfgmaker --global "WorkDir: /home/tobi" \ - --global "Options[_]: growright,bits" \ - --ifref=descr \ - --ifdesc=alias \ - public@router1.place.xyz \ - public@router2.place.xyz \ - --global "Options[_]: growright" \ - --ifref=name \ - --ifdesc=descr \ - public@switch1.place.xyz \ - --ifdesc=name \ - public@switch2.place.xyz > mrtg.cfg - - -The next example demonstrates how to use the B<--community>, -B<--snmp-options> and B<--dns-domain> to make the command line -simpler. All the equipment will use the community I, except for -the ppp-server which use community I. All equipment uses these -SNMP options: B<1s timeout>, B<1 retry> and B (B and -B is unspecified which means they use the default values). -The exception again is the ppp-server which uses B. -Finally, all the equipment is part of the domain I, except -for the ppp-server which is part of the domain I. -Note that the latter is achieved simply by specifying the name -of the ppp-server to be I> . - - cfgmaker --global "WorkDir: /home/tobi" \ - --global "Options[_]: growright,bits" \ - --dns-domain=place.xyz \ - --community=hidden \ - --snmp-options=::1:1::2 \ - router1 \ - router2 \ - router3 \ - router4 \ - router5 \ - switch1 \ - switch2 \ - switch3 \ - switch4 \ - switch5 \ - switch6 \ - switch7 \ - access@ppp-server.remote:::::1 > mrtg.cfg - - -=head1 SEE ALSO - -L - -=head1 AUTHOR - -Tobias Oetiker Etobi@oetiker.chE and -Jakob Ilves Ejakob.ilves@oracle.comE - - -=head1 LICENSE - -GNU General Public License - -=head1 COPYRIGHT - -Cfgmaker is Copyright 2000 by Tobias Oetiker Etobi@oetiker.chE - -=cut diff --git a/bin/cfgmaker.lib64 b/bin/cfgmaker.lib64 deleted file mode 100755 index 703791d..0000000 --- a/bin/cfgmaker.lib64 +++ /dev/null @@ -1,2892 +0,0 @@ -#! /usr/bin/perl -w -# -*- mode: Perl -*- -################################################################## -# MRTG 2.17.7 -- Config file creator -################################################################## -# Created by Tobias Oetiker -# this produces an mrtg config file for one router or more routers -# by pulling info off the router via snmp -################################################################## -# Distributed under the GNU copyleft -# Copyright 2000 by Tobias Oetiker -################################################################## - -# DEBUG TARGETS -# base - basic program flow -# snpo - SNMP Polling -# snpd - SNMP Detail -#@main::DEBUG=qw(base); -@main::DEBUG=qw(base snpo snpd); - -require 5.005; -use strict; - -BEGIN { - # Automatic OS detection ... do NOT touch - if ( $^O =~ /^(?:(ms)?(dos|win(32|nt)?))/i ) { - $main::OS = 'NT'; - $main::SL = '\\'; - $main::PS = ';'; - } elsif ( $^O =~ /^NetWare$/i ) { - $main::OS = 'NW'; - $main::SL = '/'; - $main::PS = ';'; - } elsif ( $^O =~ /^VMS$/i ) { - $main::OS = 'VMS'; - $main::SL = '.'; - $main::PS = ':'; - } else { - $main::OS = 'UNIX'; - $main::SL = '/'; - $main::PS = ':'; - } -} - -use FindBin; -use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; - -use MRTG_lib "2.100015"; -use Getopt::Long; -use Pod::Usage; -use Socket; - - -sub main { - - # assign defaults - my %opt = ( - 'enable-ipv6' => 0, - 'use-16bit' => 0, - 'community' => 'public', - 'interfaces' => 1, - 'enablesnmpv3' => 0, -# 'snmp-options' => ':::::2' - ); - my %routers; - my %confcache; - my $ipv4only; - my %v3opt; - $opt{fullcmd} = - "$0 ".(join " ", - map {$_ =~ /[ \[\]\*\{\}\;\>\<\&]/ ? qq{"$_"} : $_ } @ARGV); - options(\%opt,\%routers); - - # Check for IPv6 libraries if IPv6 is enabled. - # If the check fails, IPv6 is disabled. - $ipv4only = 1; - if ($opt{'enable-ipv6'} == 1) { - if ((eval {local $SIG{__DIE__};require Socket6;}) && (eval {local $SIG{__DIE__};require IO::Socket::INET6;})) { - debug ('base',"IPv6 libraries found, IPv6 enabled."); - $ipv4only = 0; - } else { - warn "WARNING: IPv6 libraries not found, IPv6 disabled.\n"; - $opt{'enable-ipv6'} = 0; - } - } - - if ($opt{'use-16bit'} == 1) { - warn "WARNING: Using 16bit RequestIDs\n"; - no warnings; - $SNMP_Session::default_use_16bit_request_ids=1; - } - # Check for SNMP V3 - # - if (exists($opt{username}) or lc($opt{enablesnmpv3}) eq "yes" ) { - if (eval {local $SIG{__DIE__};require Net_SNMP_util;}) { - import Net_SNMP_util; - debug('base', "SNMP V3 libraries found, SNMP V3 enabled."); - $opt{enablesnmpv3} = "yes"; - push @{$opt{global}}, "enablesnmpv3: yes"; - } else { - warn "WARNING: SNMP V3 libraries not found, SNMP V3 disabled. Falling back to V2c.\n"; - require SNMP_util; - import SNMP_util; - $opt{enablesnmpv3} = "revert"; - } - } - else { - require SNMP_util; - import SNMP_util; - $opt{enablesnmpv3} = "no"; - } - - init(); - - foreach my $router - (sort - {($routers{$a}{noofrouter}) <=> ($routers{$b}{noofrouter})} - keys %routers) - { - my @snmpopt = split(/:/,$routers{$router}{'snmp-options'}); - if ($snmpopt[5] and $snmpopt[5] == 3) { - if ($opt{enablesnmpv3} eq "revert") { - $snmpopt[5] = 2; - warn "reverting to snmpV2c for router $router\n"; - $routers{$router}{'snmp-options'} = join(":",@snmpopt); - $routers{$router}{snmpopt_current} = $routers{$router}{'snmp-options'}; - } else { - die "SNMP V3 requires a --username parameter as part of the User Security Model for router $routers{$router}{routerkey}" if $opt{enablesnmpv3} eq "no"; - $routers{$router}{enablesnmpv3} = $opt{enablesnmpv3}; - %v3opt = parsev3(\%opt); - } - } else { - debug('base',"snmpv3 available, but using v1/v2c for $routers{$router}{routerkey}") if $opt{enablesnmpv3} eq "yes"; - } - - - # pod2usage(-verbose=>1,-message=>"ERROR: Could not Parse $router\n") - # unless $router =~ /.*\@.*/; - debug('base',"Get Device Info on $router"); - $routers{$router}{ipv4only} = $ipv4only; - if ( my $devinfo = DeviceInfo($router,\%routers,\%v3opt) ) { - $routers{$router}{deviceinfo} = $devinfo; - } else { - warn "WARNING: Skipping $router as no info could be retrieved\n\n"; - sleep 5; - next; - } - - if ($opt{interfaces}) { - debug('base',"Populating confcache"); - populateconfcache(\%confcache,$router,$routers{$router}{ipv4only},1,\%v3opt); - debug('base',"Get Interface Info"); - InterfaceInfo(\%confcache,\%routers,$router,\%opt,\%v3opt); - } - } - GenConf(\%opt,\%routers,\%confcache,\%v3opt); -} # end main - -main; -exit 0; - -sub InterfaceInfo($$$$$) { - my $confcache = shift; - my $routers = shift; - my $router = shift; - my $opt = shift; - my $v3opt = shift; - my @Variables = qw (ifIndex ifType ifAdminStatus ifOperStatus ifMtu); - - my $snmphost = v4onlyifnecessary($router, $routers->{$router}{ipv4only}); - - if ($routers->{$router}{deviceinfo}{Vendor} eq 'cisco' && - $routers->{$router}{deviceinfo}{sysDescr} =~ m/Version\s+(\d+\.\d+)/) { - push @Variables, ($1 > 11.0 or $1 < 10.0 ) ? "ifAlias" : "CiscolocIfDescr"; - if ($1 > 11.2) {push @Variables, "vmVlan";}; - if ($1 > 11.3) {push @Variables, "vlanTrunkPortDynamicStatus";}; - } elsif ( $routers->{$router}{deviceinfo}{Vendor} =~ /(?:hp|juniper|dlink|wwp|foundry|dellLan|force10|3com|extremenetworks|openBSD|arista|enterasys|zyxel|vyatta|dcn|brocade)/i) { - push @Variables, "ifAlias"; - } - - my $descr = $routers->{$router}{deviceinfo}{sysDescr}; - if ($routers->{$router}{deviceinfo}{Vendor} eq 'cisco' && - $descr =~ m/Catalyst\sOperating\sSystem|Cisco\sSystems\sWS-C2900/ ) { - push @Variables, "CiscoCatalystPortName"; - push @Variables, "vmVlan"; - } - if ($routers->{$router}{deviceinfo}{Vendor} eq 'cisco' && - $descr =~ m/Catalyst/ ) { - push @Variables, "vlanTrunkPortDynamicStatus"; - } - if ($descr =~ m/Passport-8610/ || $descr =~ m/MERS-8610/ ) { - push @Variables, "ppPortName"; - } - - foreach my $var (@Variables) { - debug('base',"Walking $var"); - foreach my $tuple (snmpwalk($snmphost,$v3opt, $var)){ - my($if,$value) = split /:/, $tuple, 2; - $value =~ s/[\0- ]+$//; # no trailing space - $routers->{$router}{$if}{$var} = $value; - debug('snpd'," $router -> $if -> $var = $value"); - } - } - - # interface speed var depends on snmp version - - my $snmp_version = (split(':', $router, 6))[5] || 1; - if ( $snmp_version =~ /[23]/ ) { - debug('base',"Walking ifSpeed"); - my @ifSpeed = snmpwalk($snmphost, $v3opt,'ifSpeed'); - debug('snpd',"\@ifSpeed = @ifSpeed\n"); - debug('base',"Walking ifHighSpeed"); - my @ifHighSpeed = snmpwalk($snmphost,$v3opt, 'ifHighSpeed'); - for ( my $i=0; $i<=$#ifSpeed; $i++ ) { - my ($if,$value) = split /:/, $ifSpeed[$i], 2; -# the mib entry on ifSpeed says -# "An estimate of the interface's current bandwidth in bits -# per second. For interfaces which do not vary in bandwidth -# or for those where no accurate estimation can be made, this -# object should contain the nominal bandwidth. If the -# bandwidth of the interface is greater than the maximum value -# reportable by this object then this object should report its -# maximum value (4,294,967,295) and ifHighSpeed must be used -# to report the interace's speed. For a sub-layer which has -# no concept of bandwidth, this object should be zero." - if ( (not defined $value) || ($value == 2**32-1) ) { - ($if, $value) = split /:/, $ifHighSpeed[$i], 2; - $value = $value * 1000000; # highSpeed = contador * 10^6 - debug('base',"Speed: $if - $value"); - } - if ( ($descr =~ m/MERS-8610/ ) && (defined $ifHighSpeed[$i]) ) { - ($if, $value) = split /:/, $ifHighSpeed[$i], 2; - $value = $value * 1000000; # highSpeed = contador * 10^6 - debug('base',"Speed: $if - $value"); - - } - $routers->{$router}{$if}{'ifSpeed'} = $value; - } - } else { - debug('base',"Walking ifSpeed"); - foreach my $tuple (snmpwalk($snmphost,$v3opt, 'ifSpeed')){ - my($if,$value) = split /:/, $tuple, 2; - $routers->{$router}{$if}{'ifSpeed'} = $value; - debug('snpd'," $router -> $if -> ifSpeed = $value"); - } - } - - # magic speed determination for portmaster IFs - - if ($routers->{$router}{deviceinfo}{Vendor} eq 'portmaster') { - # We can only approximate speeds - # - # so we think that ppp can do 76800 bit/s, and slip 38400. - # (actualy, slip is a bit faster, but usualy users with newer modems - # use ppp). Alternatively, you can set async speed to 115200 or - # 230400 (the maximum speed supported by portmaster). - # - # But if the interface is ptpW (sync), max speed is 128000 - # change it to your needs. On various Portmasters there are - # various numbers of sync interfaces, so modify it. - # - # The most commonly used PM-2ER has only one sync. - # - # Paul Makeev (mac@redline.ru) - # - foreach my $if (keys %{$routers->{$router}}) { - next unless $if =~ /^\d+$/; - my $ift = $routers->{$router}{$if}{ifType}; - my $ifd = $routers->{$router}{$if}{Descr}; - if ($ift == 23) { - if ($ifd eq 'ptpW1') { - $routers->{$router}{$if}{ifSpeed} = 128000; - } else { - $routers->{$router}{$if}{ifSpeed} = 76800; - } - } elsif ($ift == 28) { - $routers->{$router}{$if}{ifSpeed} = 38400; - } elsif ($ift == 6) { - $routers->{$router}{$if}{ifSpeed} = 10000000; - } - } - } - - # match confcache info into tree - my $cachekey = cleanhostkey $router; - - foreach my $method (keys %{$$confcache{$cachekey}}) { - foreach my $key (keys %{$$confcache{$cachekey}{$method}}) { - my $if = readfromcache($confcache,$router,$method,$key); - next unless $if =~ /^\d+$/; - $routers->{$router}{$if}{$method} = $key; - for ($method) { - #fix special chars in ifdescr - # no need for this we fix if references later on - # /^Descr|Name$/ && do { - # $routers->{$router}{$if}{"R$method"} = $routers->{$router}{$if}{$method}; - # $routers->{$router}{$if}{$method} =~ s/(:)/\\$1/g; - # next; - # }; - - #find hostname of IF - !$$opt{noreversedns} && /^Ip$/ and do { - my $name = - gethostbyaddr( - pack('C4', - split(/\./, - $routers->{$router}{$if}{$method})), - AF_INET); - $routers->{$router}{$if}{DNSName} = ($name or ""); - next; - }; - } - } - } -} # end InterfaceInfo - -sub GenConf ($$$$) { - my $opt = shift; - my $routers = shift; - my $confcache = shift; - my $v3opt = shift; - my $conf = "# Created by \n# $$opt{fullcmd}\n\n"; - - # print global options - $conf .= < ($$routers{$b}{noofrouter})} - keys %$routers ) { - my $router_ref = $$routers{$router}; - my $router_opt = $$router_ref{opt}; - my $router_dev = $$router_ref{deviceinfo}; - - # Did any global options appear on the command line - # before this router? If so, include them into the - # configuration file. - if (defined $$router_opt{global}) { - foreach my $key (@{$$router_opt{global}}) { - $conf .= "$key\n"; - } - } - - # If IPv6 is enabled, add IPv4Only target directive for targets - # that do not support SNMP over IPv6. - my $ipv4only_directive; - my $router_ipv4only = ($$opt{'enable-ipv6'} == 1) && $$router_ref{ipv4only}; - - my $syscontact = $$router_dev{sysContact}; - my $html_syscontact = html_escape($syscontact); - my $syslocation = $$router_dev{sysLocation}; - my $html_syslocation = html_escape($syslocation); - my $sysname = $$router_dev{sysName}; - my $sysdescr = $$router_dev{sysDescr}; - my $comment_sysdescr = $sysdescr; - # make sure embeded newlines do not harm us here - $comment_sysdescr =~ s/[\n\r]+/\n\# /g; - my $community = $$router_ref{community}; - $community =~ s/([@ ])/\\$1/g; - my $router_connect = "$community\@$$router_ref{routername}$$router_ref{snmpopt_current}"; - my @v3options; - foreach my $v3op (keys %$v3opt) { - push @v3options, $v3op."=>'".$$v3opt{$v3op}."'"; - } - my $v3options = join(",",@v3options) if $$router_ref{'snmp-options'} =~ /(?::[^:]*){4}:3/ ; - my $html_sysdescr = html_escape($sysdescr); - my $router_name = - ($$router_ref{routername} - . (($$router_ref{'dns-domain'})?'.':'') - . $$router_ref{'dns-domain'}); - - # James Overbeck 2001/09/20 - # Moved $directory_name definition from within the interface - # foreach loop to here. In its previous location, $directory_name - # was not accessible to host templates. $directory_name is not - # changed per-interface so it might as well be here instead of - # where it was. - - my $directory_name = ""; - - if (defined $$router_opt{subdirs}) { - $directory_name = $$router_opt{subdirs}; - $directory_name =~ s/HOSTNAME/$router_name/g; - $directory_name =~ s/SNMPNAME/$$router_dev{sysName}/g; - } - - - my $target_lines = ""; - my $problem_lines = ""; - my $head_lines = " -###################################################################### -# System: $sysname -# Description: $comment_sysdescr -# Contact: $syscontact -# Location: $syslocation -###################################################################### -"; - - my $separator_lines = "\n\n"; - - # Host specific config lines generation code starts HERE - - if(defined $$router_opt{'host-template'}) { - # First test if the file exists and is readable, die if not. - die "File $$router_opt{'host-template'} didn't exist.\n" - unless (-e $$router_opt{'host-template'} - and -r $$router_opt{'host-template'}); - # Open the file (or die). - open IF_TEMPLATE, $$router_opt{'host-template'} - or die "File $$router_opt{'host-template'} couldn't be opened.\n"; - - my @template_lines = readline *IF_TEMPLATE; - close IF_TEMPLATE; - $@ = undef; - eval ('local $SIG{__DIE__};'.join("", @template_lines)); - - die "ERROR Evaluation of the contents in the file \n\n". - "$$router_opt{'host-template'}\ngave the error \n\n\"$@\"\n\nExiting cfgmaker\n" if $@; - } - - $conf .= ($head_lines - . $problem_lines - . $target_lines - . $separator_lines); - - # Host specific config lines generation code ends HERE - - - if ($$router_opt{'interfaces'}) { - foreach my $ifindex (sort {int($a) <=> int($b)} grep /^\d+$/, keys %$router_ref) { - my $i = $$router_ref{$ifindex}; - - # Now define a number of variables used for this interface. - # Some variables are just used internally by cfgmaker to - # process the interface, others are provided for usage in - # host and interface templates and for interface filters. - - my $if_index = $ifindex; - my $if_eth = $$i{Eth} || 'No Ethernet Id'; - - # does it make sense to look at the interface ? - my @prob; - my $default_ifstate = 1; # State assumed up. - my $default_iftype = 1; # iftype assumed ok. - my $if_ok = 1; # - - my $if_admin = (($$i{ifAdminStatus} || 0) == 1); - my $if_oper = (($$i{ifOperStatus} || 0) == 1); - - my $if_type = $$i{ifType} || -1; - my $if_is_ethernet = 0 < scalar(grep {$_ == $if_type;} - (6,7,26,62,69,117)); - my $if_is_isdn = (0 < scalar (grep {$_ == $if_type;} - (20,21,63,75,76,77))); - my $if_is_dialup = $if_is_isdn || - (0 < scalar (grep {$_ == $if_type;} - (23,81,82,108))); - my $if_is_atm = (0 < scalar(grep {$_ == $if_type;} - (37,49,107,105,106,114,134))); - - my $if_is_wan = 0 < scalar(grep {$_ == $if_type;} - (22,30,32,39,44,46)); - - my $if_is_lan = $if_is_ethernet || - (0 < scalar (grep {$_ == $if_type;} - (8,9,11,15,26,55,59,60,115))); - my $if_is_dsl = (0 < scalar(grep {$_ == $if_type;} - (94,95,96,97))); - my $if_is_loopback = $if_type == 24; - my $if_is_ciscovlan = - ($$router_dev{Vendor} eq 'cisco' - and $$i{Descr} =~ /^(unrouted )?[- ]?VLAN[- ]?\d*$/i); - - my $if_ip = $$i{Ip} || 'No Ip'; - my $if_snmp_descr = $$i{Descr} || 'No Description'; - $if_snmp_descr =~ s/\n$//; # no you don't want to know who does this - # ok ... dell 3524 - $if_snmp_descr =~ s/ /-/g; - my $if_type_num = $$i{ifType} || 'Unknown Type'; - $$i{ifType} ||= -1; - my $if_snmp_name = $$i{Name} || 'No Name'; - my $if_snmp_alias = $$i{ifAlias} || ''; - my $if_cisco_descr = $$i{CiscolocIfDescr} || ''; - my $if_dns_name = $$i{DNSName} || 'No DNS name'; - my $if_vlan_id = $$i{vmVlan} || 'No Vlan'; - my $if_cisco_trunk = ($$i{vlanTrunkPortDynamicStatus} || 0 == 1); - my $if_MTU = $$i{ifMtu} || 'No MTU'; - - # For Nokia IPSO, find non-ethernet interfaces with IP addresses - # and add missing MAC address and Port Speed information to - # to the LOGICAL and LOGICAL+VLAN interfaces. - if ( $$router_dev{Vendor} eq 'nokiaipsofw' ) { - if ($$i{ifType} ne "6" and - $$router_dev{sysDescr} =~ / IPSO / && - $$i{Ip} =~ /^\d+/ and - (not $$i{Eth} or - not $$i{ifSpeed} or - $$i{ifSpeed} < 10 ) - ) { - my $logical_if_name = $$i{Name}; - - # Split the LOGICAL interface name in attempt - # to match with base PHYSICAL interface detail. - my ($logical_if_HEAD, $logical_if_TAIL) = - $logical_if_name =~ /^(.*)(c\d+)$/; - - foreach my $ifindexTMP (sort {int($a) <=> int($b)} - grep /^\d+$/, keys %$router_ref) { - next unless $ifindexTMP =~ /^\d+$/; - - my $physical_if_name = $$router_ref{$ifindexTMP}; - - if ($$physical_if_name{ifType} == 6 && - $logical_if_HEAD eq $$physical_if_name{Name} ) { - $$i{Eth} ||= $$physical_if_name{Eth}; - $$i{ifSpeed} = $$physical_if_name{ifSpeed} - if ( not $$i{ifSpeed} or $$i{ifSpeed} < 10 ); - } - } - } - } - - # First investigate the state of the interface. - if (not defined $$router_opt{'no-down'}) { - if (($$i{ifAdminStatus} || 0 )== 2) { - push @prob, "it is administratively DOWN"; - $default_ifstate = 0; - } elsif (($$i{ifAdminStatus} || 0 ) == 3) { - push @prob, "it is in administrative TEST mode"; - $default_ifstate = 0; - } - - if (not defined $$router_opt{'show-op-down'}) { - if (($$i{ifOperStatus} || 0 ) == 2) { - push @prob, "it is operationally DOWN"; - $default_ifstate = 0; - } elsif (($$i{ifOperStatus} || 0 ) == 3) { - push @prob, "it is in operational TEST mode"; - $default_ifstate = 0; - } - } - } - - - # Investigate the type of the interface. - if ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 18) { # by fwo@obsidian.co.za - push @prob, "it is a DS1 controllers"; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 19) { # by fwo@obsidian.co.za - push @prob, "it is a E1 controllers"; - $default_iftype = 0; - } elsif ($$i{ifType} == 24) { - push @prob, "it is a Software Loopback interface" ; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 30) { # by blube@floridadigital.net - push @prob, "it is a DS3 controller"; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 102) { # by dan.mcdonald@austinenergy.com - push @prob, "it is a Voice controller"; - $default_iftype = 0; - } elsif ($$router_dev{Vendor} eq 'cisco' && $$i{ifType} == 103) { # by dan.mcdonald@austinenergy.com - push @prob, "it is a Voice dial peer"; - $default_iftype = 0; - } elsif ($$i{ifType} == 162) { - push @prob, "it is a CEF Sub-interface"; # John Begley - } elsif ($$router_dev{Vendor} eq 'cisco' - and $$i{Descr} eq 'Null0') { - push @prob, "it is a cisco Null0 interface"; - $default_iftype = 0; - } - my $default = $default_iftype && $default_ifstate; - - # Do some futher investigation if the interface makes - # sense to collect on - - # I debated whether to insert the zero-speed check before - # or after the "will always be zero" sections below. - # I settled on before since I'll assume the user knows - # what speed the zero-speed interfaces should be better - # than the simple logic below. - if (not $$i{ifSpeed} and $$router_opt{'zero-speed'}) { - # Set all interfaces with zero speed to the value specified - # by the --zero-speed= command line option. - - # Be sure the value specified is a valid integer. - # It seems like this could be done once when - # $$router_opt is set, but I didn't see any example - # of input validation in that part of cfgmaker, - # so it gets done here, more times than are - # really necessary. ;-) - unless ($$router_opt{'zero-speed'} =~ /^\d+$/) { - die "ERROR: zero-speed specified with non-integer speed: $$router_opt{'zero-speed'}"; - } - $$i{ifSpeed} = $$router_opt{'zero-speed'}; - } - - if (not $$i{ifSpeed} and $$router_dev{Vendor} eq 'foundry' and $$i{ifType} and $$i{ifType} == 194) { - # foundry ATM subinterfaces always report 0 speed, make it 155Mbps instead. - $$i{ifSpeed} = 155000000; - } elsif (not $$i{ifSpeed} and $$router_dev{Vendor} eq 'foundry' and $$i{ifType} and $$i{ifType} == 135) { - # Foundry virtual Ethernet interfaces always report 0 speed, make it 1GB instead. - $$i{ifSpeed} = 1000000000; - } elsif (not $$i{ifSpeed} and $$router_dev{Vendor} eq 'cisco' and $$i{sysDescr} and $$i{sysDescr} =~ /FWSM-Firewall / ) { - # Cisco PIX Firewall Switch Modules have effective backplane speed of 600 Megs - $$i{ifSpeed} = 600000000; - } elsif (not $$i{ifSpeed} and $$router_dev{Vendor} eq '3com' and $$i{Descr} and $$i{Descr} =~ /RMON VLAN (\d+)/ ) { - $$i{ifSpeed} = 100000000; - $if_vlan_id = $1; - } elsif (not $$i{ifSpeed}) { - push @prob, "has no ifSpeed property"; - $$i{ifSpeed} = 0; - $if_ok = 0; - } - - my $message; - my $nohc =0; - if ($message = IsCounterBroken($ifindex, $router_ref,$v3opt)) { - # set snmpopt_current to working snmp options - if ($message eq '1') { - $nohc = 1; - } else { - push @prob, "got '$message' from interface when trying to query"; - $if_ok = 0; - } - } - - my $community = $$router_ref{community}; - $community =~ s/([@ ])/\\$1/g; - my $router_connect = "$community\@$$router_ref{routername}$$router_ref{snmpopt_current}"; - - my $v3options = join(",",@v3options) if $$router_ref{snmpopt_current} =~ /(?::[^:]*){4}:3/ ; - - # determine interface reference - my $if_ref; - if (defined $$router_opt{ifref}) { - foreach (split /,/,$$router_opt{ifref}) { - /^ip$/ && do { if($$i{Ip} ){ $if_ref = "/".$$i{Ip}; last;} next}; - /^eth$/ && do { if($$i{Eth} ){ $if_ref = "!".$$i{Eth}; last;} next}; - /^descr?$/&& do { if($$i{Descr}){ $if_ref = "\\".$$i{Descr};last;} next}; - /^name$/ && do { if($$i{Name} ){ $if_ref = "#".$$i{Name}; last;} next}; - /^type$/ && do { if($$i{Type} ){ $if_ref = "%".$$i{Type}; last;} next}; - /^nr$/ && do { $if_ref = $ifindex; last }; - die "ERROR: Invalid value for --ifref: $$router_opt{ifref} ($_)\n"; - } - if (not defined $if_ref) { - push @prob, "--ifref=$$router_opt{ifref} is not unique for this interface"; - $if_ref = $ifindex; - $if_ok = 0; - } - } else { - $if_ref = $ifindex; - } - - # generate Target name - my $trim_if_ref = $if_ref; - $trim_if_ref =~ s/[\#!\/\\:\s\@%]+/_/g; - $trim_if_ref =~ s/^_*(.+?)_*$/$1/; - my $target_name = "${router_name}_$trim_if_ref"; - my $if_title_desc = $if_ref; - $if_title_desc =~ s/^[^\d]//; - my $if_speed = int($$i{ifSpeed} / 8); - my $if_speed_str = fmi($if_speed,$$router_ref{flags}); - my $if_type_desc = IfType($$i{ifType}); - my $html_if_type_desc = html_escape($if_type_desc); - my $desc_prefix = 'Traffic Analysis for '; - - my $port_dot = $$i{Name} || 'Unknown'; - $port_dot =~ s/\//./g; - my $if_port_name = $$router_ref{$port_dot}{CiscoCatalystPortName}; - my $if_pp_port_name = $$router_ref{$ifindex}{ppPortName}; - - if (defined $$router_opt{ifdesc}) { - $desc_prefix = ''; - foreach (split /,/,$$router_opt{ifdesc}) { - /^ip$/ && do { if($$i{Ip}) { $if_title_desc = $$i{Ip}; last } next }; - /^eth$/ && do { if($$i{Eth}) { $if_title_desc = $$i{Eth}; last } next }; - /^descr?$/ && do { if($$i{Descr}){ $if_title_desc = $if_snmp_descr; last } next }; - /^alias$/ && do { if($$i{ifAlias}){ $if_title_desc = "$if_snmp_descr $if_snmp_alias $if_cisco_descr"; last } next }; - /^name$/ && do { if($$i{Name}) {$if_title_desc = "#".$$i{Name}; last } next }; - /^catname$/ && do {$if_title_desc = $if_port_name; last }; - /^ppname$/ && do {$if_title_desc = $if_pp_port_name; last}; - /^type$/ && do { if($$i{Type}) { $if_title_desc = "%".$$i{Type}; last } next }; - /^nr$/ && do {$if_title_desc = "Interface $ifindex"; last}; - /^$/ && do {$if_title_desc = $if_type_desc || $if_snmp_descr; last }; - die "ERROR: Invalid value for --ifdesc: '$$router_opt{ifdesc} ($_)'\n"; - } - } - - # Now setup a large number of variables needed for the - # generation of the configuration lines. - - $if_title_desc =~ s/\\([:@\\\/\# ])/$1/g; # unescape - $if_title_desc = $if_snmp_name if not $if_title_desc; - my $html_if_title_desc = html_escape($if_title_desc); - my $html_desc_prefix = html_escape($desc_prefix); - - my $html_if_snmp_descr = html_escape($if_snmp_descr); - my $html_if_snmp_name = html_escape($if_snmp_name); - my $html_if_snmp_alias = html_escape($if_snmp_alias); - my $html_if_cisco_descr = html_escape($if_cisco_descr); - my $if_description = "$if_snmp_descr $if_snmp_alias $if_cisco_descr"; - my $html_if_description = html_escape($if_description); - my $if_title = "$desc_prefix$if_title_desc -- $sysname"; - my $html_if_title = html_escape($if_title); - - my $head_lines = "### Interface $ifindex >> Descr: '$if_snmp_descr' |". - " Name: '$if_snmp_name' | Ip: '$if_ip' | Eth: '$if_eth' ###\n"; - - my $target_lines = ""; - my $separator_lines = "\n\n"; - - # escape the if reference - $if_ref =~ s/([& :])/\\$1/g; - my $default_target_directive = "Target[$target_name]: $if_ref:$router_connect"; - $default_target_directive .= "\nSnmpOptions[$target_name]: $v3options" if $$router_ref{snmpopt_current} =~ /(?::[^:]*){4}:3/ ; - $default_target_directive .= "\nnoHC[$target_name]: yes" if $nohc == 1; - my $if_snmp_descr_save = $if_snmp_descr; - $if_snmp_descr_save =~ s/"/'/g; - my $default_setenv_directive = "SetEnv[$target_name]: MRTG_INT_IP=\"$if_ip\" MRTG_INT_DESCR=\"$if_snmp_descr_save\""; - my $default_directory_directive = ($directory_name ? "Directory[$target_name]: $directory_name" : ""); - my $default_maxbytes_directive = "MaxBytes[$target_name]: $if_speed"; - - $ipv4only_directive = $router_ipv4only ? "IPv4Only[$target_name]: yes" : ""; - - my $default_title_directive = "Title[$target_name]: $html_desc_prefix$html_if_title_desc -- $sysname"; - my $default_pagetop_directive = - "PageTop[$target_name]:

$html_desc_prefix$html_if_title_desc -- $sysname

-
- - - - - - - - - - - - - - - - - - - - - "; - $default_pagetop_directive .= " - - - - " if defined $if_port_name; - $default_pagetop_directive .= " - - - - " if defined $if_pp_port_name; - $default_pagetop_directive .= " - - - - "; - $default_pagetop_directive .= " - - - - " if $if_ip; - $default_pagetop_directive .= " -
System:$sysname in $html_syslocation
Maintainer:$html_syscontact
Description:$html_if_description
ifType:$html_if_type_desc ($if_type_num)
ifName:$html_if_snmp_name
Port Name:$if_port_name
Port Name:$if_pp_port_name
Max Speed:$if_speed_str
Ip:$if_ip ($if_dns_name)
-
"; - - my $default_target_lines = - ("\n" - . $default_target_directive . "\n" - . $default_setenv_directive . "\n" - . ($default_directory_directive - ? ($default_directory_directive . "\n") - : "") - . $default_maxbytes_directive . "\n" - . ($ipv4only_directive - ? ($ipv4only_directive . "\n") - : "") - . $default_title_directive . "\n" - . $default_pagetop_directive . "\n"); - - - # If --if-filter is provided, evalutat that. If it - # returns true, clear @prob. If it returns false, - # instead add a complaint to @prob. - - if (defined $$router_opt{'if-filter'}) { - $@ = undef; - if (eval('local $SIG{__DIE__};'.$$router_opt{'if-filter'})) { - @prob = (); - } else { - push @prob, "filter specified by --if-filter rejected the interface"; - $if_ok = 0; - } - die "ERROR: with if-filter $$router_opt{'if-filter'}: $@" if $@; - } - - - # issue problem report - - my $problem_lines = ""; - - if (@prob) { - $problem_lines .= "### The following interface is commented out because:\n"; - map {$problem_lines .= "### * $_\n"} @prob; - $if_ok = 0; - } - - # The target config generation code starts HERE. - - - if (defined $$router_opt{'if-template'}) { - # First test if the file exists and is readable, - # die if not. - die "File $$router_opt{'if-template'} didn't exist.\n" unless (-e $$router_opt{'if-template'} - and -r $$router_opt{'if-template'}); - # Open the file (or die). - open IF_TEMPLATE, $$router_opt{'if-template'} - or die "File $$router_opt{'if-template'} couldn't be opened.\n"; - my @template_lines = readline *IF_TEMPLATE; - - $@ = undef; - eval ('local $SIG{__DIE__};'.join( "", @template_lines)); - die "Evaluation of the contents in the file \n\n$$router_opt{'if-template'}\n". - "gave the error \n\n\"$@\"\n\nExiting cfgmaker\n" if $@; - } else { - $target_lines = $default_target_lines; - } - - - if ($target_lines && not $if_ok) { # comment out the target lines if needed - $target_lines =~ s/^/\# /gm; - } - - $conf .= ($head_lines - . $problem_lines - . $target_lines - . $separator_lines); - - } - # Target generation code ends HERE. - } - } - - # print any global options which might have - # appeared on the command line after the last - # router. - if (defined $$opt{global}) { - foreach my $key (@{$$opt{global}}) { - $conf .= "$key\n"; - } - } - - if ($$opt{output}) { - debug ('base', "Writing $$opt{output}"); - open X, ">$$opt{output}" or die "ERROR: creating $$opt{output}: $!\n"; - print X $conf; - close X; - } else { - print $conf; - } -} # end GenConf - -sub IsCounterBroken ($$$) { - my $if = shift; - my $router_ref = shift; - my $v3opt = shift; - my $router = $$router_ref{routerkey}; - my $fallback = 0; - local $SNMP_Session::suppress_warnings = 3; - local $Net_SNMP_util::suppress_warnings = 3; - - my $ipv4only = $$router_ref{ipv4only}; - my $snmphost = v4onlyifnecessary($router, $ipv4only); - - if ($router =~ /:[\d.]*:[\d.]*:[\d.]*:[23]/) { - my $speed = (snmpget($snmphost, $v3opt, 'ifHighSpeed.'.$if))[0] || 'unknown'; - debug('base',"snmpget $snmphost for ifHighSpeed.$if -> $speed Mb/s"); - $SNMP_Session::errmsg = undef; - $Net_SNMP_util::ErrorMessage = undef; - my $counter = (snmpget($snmphost,$v3opt, 'ifHCInOctets.'.$if))[0] || 'unknown'; - debug('base',"snmpget $snmphost for ifHCInOctets.$if -> $counter"); - if( $speed eq 'unknown' or $counter !~ /^\d+$/ or $SNMP_Session::errmsg or $Net_SNMP_util::ErrorMessage){ - $SNMP_Session::errmsg = undef; - $Net_SNMP_util::ErrorMessage = undef; - $fallback = 1; - debug('base',"check for HighspeedCounters failed ... Dropping back to V1"); - } else { - return 0; - } - } - if ( $fallback == 1 or $$router_ref{snmpopt_current} !~ /:[\d.]*:[\d.]*:[\d.]*:[23]/) { - my $counter = (snmpget($snmphost, 'ifInOctets.'.$if))[0]; - if (defined $SNMP_Session::errmsg) { - my $error = $SNMP_Session::errmsg; - $SNMP_Session::errmsg = undef; - $error =~ s/\n/\n### /g; - return $error; - } elsif (defined $Net_SNMP_util::ErrorMessage and $Net_SNMP_util::ErrorMessage =~ /\w/) { - my $error = $Net_SNMP_util::ErrorMessage; - $Net_SNMP_util::ErrorMessage = undef; - $error =~ s/\n/\n### /g; - return $error; - } elsif (not defined $counter or $counter eq '' or $counter =~ /\D/) { - return "No counter exists for $if"; - } - } - return $fallback; -} # end IsCounterBroken - -# DeviceInfo does fallback between IPv6 and IPv4: if an IPv6 snmpwalk returns -# undef values (= an error) and the target is a hostname, then it repeats the -# query using IPv4 in case the target does not support SNMP over IPv6. -# If DeviceInfo falls back to IPv4, it sets the ipv4only field for the target -# in the routers hash. -sub DeviceInfo ($$$) { - my $router=shift; - my $routers=shift; - my $v3opt=shift; - my %DevInfo; - my $ipv4only = $$routers{$router}{ipv4only}; - - my @variables = snmpwalk(v4onlyifnecessary($router, $ipv4only),$v3opt,'1.3.6.1.2.1.1'); # walk system - if (!(defined $variables[0])) { - # Do we need to fall back to IPv4? - my ($commmunity, $host) = ($1, $2) if ($router =~ /^(.*)@([^@]+)$/); - if ( ( ! $ipv4only ) && ( $host !~ /^\[(.*)\]/) ) { - # Not using IPv4, not an IPv6 address, so a hostname - debug ('base',"No response using IPv6 for $router, trying again using IPv4"); - $$routers{$router}{ipv4only} = 1; - @variables = snmpwalk(v4onlyifnecessary($router, 1),$v3opt, '1.3.6.1.2.1.1'); - } - } - if ( defined $variables[0] ) { - my (%DevInfo, %revOIDS); - - if ($$routers{$router}{enablesnmpv3} || '' eq "yes") { - no warnings; - %revOIDS = reverse %Net_SNMP_util::OIDS; - } - else { - no warnings; - %revOIDS = reverse %SNMP_util::OIDS; - } - foreach my $variable ( @variables ) { - my ($oid, $value) = split ( ':', $variable, 2); - if ($revOIDS{'1.3.6.1.2.1.1.'.$oid}){ - $DevInfo{ $revOIDS{'1.3.6.1.2.1.1.'.$oid} } = $value; - } - } - # vendor identification - my %vendorIDs = ( - # Add your vendor here - # sysObjectID Vendora - '1.3.6.1.4.1.11863.' => 'tplink', - '1.3.6.1.4.1.43.' => '3com', - '1.3.6.1.4.1.11.' => 'hp', - '1.3.6.1.4.1.9.' => 'cisco', - '1.3.6.1.4.1.171.' => 'dlink', - '1.3.6.1.4.1.6141.' => 'wwp', - '1.3.6.1.4.1.674.10895.' => 'dellLan', - '1.3.6.1.4.1.1916.' => 'extremenetworks', - '1.3.6.1.4.1.1991.' => 'foundry', - '1.3.6.1.4.1.6027.' => 'force10', - '1.3.6.1.4.1.2636.' => 'juniper', - '1.3.6.1.4.1.94.' => 'nokiaipsofw', - '1.3.6.1.4.1.307.' => 'portmaster', - '1.3.6.1.4.1.890.' => 'zyxel', - '1.3.6.1.4.1.2272.30' => 'nortel', - '1.3.6.1.4.1.6339' => 'DCN', - '1.3.6.1.4.1.30155.' => 'openBSD', - '1.3.6.1.4.1.30065.' => 'arista', - '1.3.6.1.4.1.5624.' => 'enterasys', - '1.3.6.1.4.1.30803.' => 'Vyatta', - '1.3.6.1.4.1.3955.' => 'LinkSys', - '1.3.6.1.4.1.1588.' => 'brocade' - ); - $DevInfo{Vendor} = 'Unknown Vendor - '.$DevInfo{sysObjectID}; - foreach (keys %vendorIDs) { - $DevInfo{Vendor} = $vendorIDs{$_} if ($DevInfo{sysObjectID} =~ /\Q$_\E/); - } - debug('base',"Vendor Id: $DevInfo{Vendor}"); - return \%DevInfo; - } else { - # we just die because the snmp module has already complained - return undef; - } -} # end DeviceInfo - - -sub fmi ($$) { - my $number = shift; - my $flags = shift; - my(@short); - if ($$flags{bits}){ - $number*=8; - @short = ("bits/s","kbits/s","Mbits/s","Gbits/s"); - } else { - @short = ("Bytes/s","kBytes/s","MBytes/s","GBytes/s"); - } - my $digits=length("".$number); - my $divm=0; - while ($digits-$divm*3 > 4) { $divm++; } - my $divnum = $number/10**($divm*3); - return sprintf("%1.1f %s",$divnum,$short[$divm]); -} # end fmi - - -sub IfType ($) { - my $id = shift; - my $ift = { - '1'=>'Other', - '2'=>'regular1822', - '3'=>'hdh1822', - '4'=>'ddnX25', - '5'=>'rfc877x25', - '6'=>'ethernetCsmacd', - '7'=>'iso88023Csmacd', - '8'=>'iso88024TokenBus', - '9'=>'iso88025TokenRing', - '10'=>'iso88026Man', - '11'=>'starLan', - '12'=>'proteon10Mbit', - '13'=>'proteon80Mbit', - '14'=>'hyperchannel', - '15'=>'fddi', - '16'=>'lapb', - '17'=>'sdlc', - '18'=>'ds1', - '19'=>'e1', - '20'=>'basicISDN', - '21'=>'primaryISDN', - '22'=>'propPointToPointSerial', - '23'=>'ppp', - '24'=>'softwareLoopback', - '25'=>'eon', - '26'=>'ethernet-3Mbit', - '27'=>'nsip', - '28'=>'slip', - '29'=>'ultra', - '30'=>'ds3', - '31'=>'sip', - '32'=>'frame-relay', - '33'=>'rs232', - '34'=>'para', - '35'=>'arcnet', - '36'=>'arcnetPlus', - '37'=>'atm', - '38'=>'miox25', - '39'=>'sonet', - '40'=>'x25ple', - '41'=>'iso88022llc', - '42'=>'localTalk', - '43'=>'smdsDxi', - '44'=>'frameRelayService', - '45'=>'v35', - '46'=>'hssi', - '47'=>'hippi', - '48'=>'modem', - '49'=>'aal5', - '50'=>'sonetPath', - '51'=>'sonetVT', - '52'=>'smdsIcip', - '53'=>'propVirtual', - '54'=>'propMultiplexor', - '55'=>'100BaseVG', - '56'=>'Fibre Channel', - '57'=>'HIPPI Interface', - '58'=>'Obsolete for FrameRelay', - '59'=>'ATM Emulation of 802.3 LAN', - '60'=>'ATM Emulation of 802.5 LAN', - '61'=>'ATM Emulation of a Circuit', - '62'=>'FastEthernet (100BaseT)', - '63'=>'ISDN & X.25', - '64'=>'CCITT V.11/X.21', - '65'=>'CCITT V.36', - '66'=>'CCITT G703 at 64Kbps', - '67'=>'Obsolete G702 see DS1-MIB', - '68'=>'SNA QLLC', - '69'=>'Full Duplex Fast Ethernet (100BaseFX)', - '70'=>'Channel', - '71'=>'Radio Spread Spectrum (802.11)', - '72'=>'IBM System 360/370 OEMI Channel', - '73'=>'IBM Enterprise Systems Connection', - '74'=>'Data Link Switching', - '75'=>'ISDN S/T Interface', - '76'=>'ISDN U Interface', - '77'=>'Link Access Protocol D (LAPD)', - '78'=>'IP Switching Opjects', - '79'=>'Remote Source Route Bridging', - '80'=>'ATM Logical Port', - '81'=>'AT&T DS0 Point (64 Kbps)', - '82'=>'AT&T Group of DS0 on a single DS1', - '83'=>'BiSync Protocol (BSC)', - '84'=>'Asynchronous Protocol', - '85'=>'Combat Net Radio', - '86'=>'ISO 802.5r DTR', - '87'=>'Ext Pos Loc Report Sys', - '88'=>'Apple Talk Remote Access Protocol', - '89'=>'Proprietary Connectionless Protocol', - '90'=>'CCITT-ITU X.29 PAD Protocol', - '91'=>'CCITT-ITU X.3 PAD Facility', - '92'=>'MultiProtocol Connection over Frame/Relay', - '93'=>'CCITT-ITU X213', - '94'=>'Asymetric Digitial Subscriber Loop (ADSL)', - '95'=>'Rate-Adapt Digital Subscriber Loop (RDSL)', - '96'=>'Symetric Digitial Subscriber Loop (SDSL)', - '97'=>'Very High Speed Digitial Subscriber Loop (HDSL)', - '98'=>'ISO 802.5 CRFP', - '99'=>'Myricom Myrinet', - '100'=>'Voice recEive and transMit (voiceEM)', - '101'=>'Voice Foreign eXchange Office (voiceFXO)', - '102'=>'Voice Foreign eXchange Station (voiceFXS)', - '103'=>'Voice Encapulation', - '104'=>'Voice Over IP Encapulation', - '105'=>'ATM DXI', - '106'=>'ATM FUNI', - '107'=>'ATM IMA', - '108'=>'PPP Multilink Bundle', - '109'=>'IBM IP over CDLC', - '110'=>'IBM Common Link Access to Workstation', - '111'=>'IBM Stack to Stack', - '112'=>'IBM Virtual IP Address (VIPA)', - '113'=>'IBM Multi-Protocol Channel Support', - '114'=>'IBM IP over ATM', - '115'=>'ISO 802.5j Fiber Token Ring', - '116'=>'IBM Twinaxial Data Link Control (TDLC)', - '117'=>'Gigabit Ethernet', - '118'=>'Higher Data Link Control (HDLC)', - '119'=>'Link Access Protocol F (LAPF)', - '120'=>'CCITT V.37', - '121'=>'CCITT X.25 Multi-Link Protocol', - '122'=>'CCITT X.25 Hunt Group', - '123'=>'Transp HDLC', - '124'=>'Interleave Channel', - '125'=>'Fast Channel', - '126'=>'IP (for APPN HPR in IP Networks)', - '127'=>'CATV MAC Layer', - '128'=>'CATV Downstream Interface', - '129'=>'CATV Upstream Interface', - '130'=>'Avalon Parallel Processor', - '131'=>'Encapsulation Interface', - '132'=>'Coffee Pot', - '133'=>'Circuit Emulation Service', - '134'=>'ATM Sub Interface', - '135'=>'Layer 2 Virtual LAN using 802.1Q', - '136'=>'Layer 3 Virtual LAN using IP', - '137'=>'Layer 3 Virtual LAN using IPX', - '138'=>'IP Over Power Lines', - '139'=>'Multi-Media Mail over IP', - '140'=>'Dynamic synchronous Transfer Mode (DTM)', - '141'=>'Data Communications Network', - '142'=>'IP Forwarding Interface', - '144'=>'IEEE1394 High Performance Serial Bus', - '150'=>'MPLS Tunnel Virtual Interface', - '161'=>'IEEE 802.3ad Link Aggregate', - '162'=>'Cisco Express Forwarding Interface', - '166'=>'MPLS', - '238'=>'Asymmetric Digital Subscriber Loop', - '246'=>'Pseudowire interface type', - '251'=>'Very high speed digital subscriber line', - }; - return ($ift->{$id} || "Uknown Interface Type"); -} # end IfType - - - - - -sub options ($$) { - my $opt = shift; - my $routers = shift; - - my $noofrouter = 0; # How many routers we've seen on cmdline. - - # The $flags hash stores what we've seen in Options[_], - # Options[^] and Options[$] so far. - # A cmdline arg like --global 'Options[_]: bits' will insert - # the element $$flags{default}{bits}="set". - # Similarly --global 'Options[$]:' will delete all elements - # in $$flags{append} - # - # This was originally created to manipulate the "bits" flag - # so fmi should know when to use "bits" or "bytes". It might - # be overkill to use such a comples solution but it makes life - # easier if cfgmaker in the future has to be extended to be - # aware of other Options[] settings like gauge, growright etc. - - my %flags; - { - my $def = {}; - my $pre = {}; - my $app = {}; - %flags = (default => $def, - prepend => $pre, - append => $app); - } - - my $addrouter_ornf = addrouter($opt, - $routers, - \$noofrouter, - \%flags); - - Getopt::Long::Configure("permute"); - GetOptions( $opt, - 'help|?', - 'man', - 'subdirs=s', - 'no-down', - 'show-op-down', - 'noreversedns', - 'ifref=s', - 'ifdesc=s', - 'if-filter=s', - 'if-template=s', - 'interfaces!', - 'host-template=s', - 'community=s', - 'username=s', - 'authkey=s', - 'authpassword=s', - 'authprotocol=s', - 'contextengineid=s', - 'contextname=s', - 'privkey=s', - 'privpassword=s', - 'privprotocol=s', - 'snmp-options=s', - 'dns-domain=s', - 'version', - 'output=s', - 'global=s@', - 'enable-ipv6', - 'enablesnmpv3', - 'use-16bit', - 'zero-speed=s', - '<>', $addrouter_ornf) or pod2usage(2); - - die("cfgmaker for mrtg-2.17.7\n") if $$opt{version}; - pod2usage(-exitval => 0, -verbose => 2) if $$opt{man}; - pod2usage(-verbose => 1) if not keys %$routers; -} # end options - -# The callback routine used by GetOptions to process "non-option -# strings" (routers) among the arguments is given only ONE argument. -# However, I want it to be able to specify both the %options hash -# (for read access) and the %routers hash (for modifying) as well -# as the router's name. This makes for three arguments. -# -# The solution is to use a closure. addrouter takes a opt hash, a -# routers hash, an index to the current number of routers and a flags -# hash and then returns a function which "remembers" these -# values (the closure) but also takes an argument (the router name). - -sub addrouter() { - my $opt = shift; - my $routers = shift; - my $noofrouter = shift; - my $flags = shift; - - return sub { - my $rawname = shift; - - $$noofrouter++; # First increase the number of routers seen. - - my ($community,$routername,$routerkey,$snmpopt,$dnsdomain,$tmpname,@tmpsnmp); - - # Now make sure that the router is defined with the - # proper community, domainname and SNMP options. - # Dissect the rawname to find out what it contains. - - # First check for community: - if ($rawname =~ /^(.+)\@([^@]+)$/) { - # Community was given explicitly! - $community = $1; - $tmpname = $2 - } else { - $community = $$opt{community}; - $tmpname = $rawname; - } - # Now separate the router name from the options. We - # can't just split on the : character because a numeric - # IPv6 address contains a variable number of :'s - if( ($tmpname =~ /^(\[.*\]):(.*)$/) || ($tmpname =~ /^(\[.*\])$/) ){ - # Numeric IPv6 address between [] - ($routername, $snmpopt) = ($1, $2); - } else { - # Hostname or numeric IPv4 address - ($routername, $snmpopt) = split(':', $tmpname, 2); - } - @tmpsnmp = split(':', $snmpopt || ''); - - $routername =~ s/\.$//; # if the hostname ends in a '.' remove it - # it seems to cause trouble in some other - # parts of mrtg - - # Now setup the SNMP options. - if (not defined $$opt{'snmp-options'}) { - $snmpopt = ':' . (join ':', @tmpsnmp); # No merge needed. - } else { - my ($t,$o,@s); - my @optsnmp = split ':',$$opt{'snmp-options'}; - - # Trim first element as the SNMP options start - # with a colon and thus the first element is a - # dummy "" string not corresponding to any SNMP option - # (or rather, corresponding to a router, if there had - # been one...) - shift @optsnmp; - - while ((scalar @tmpsnmp > 0) - or (scalar @optsnmp > 0)) { - $t = shift @tmpsnmp; - $o = shift @optsnmp; - - if(not defined $t) {$t = "";} - if(not defined $o) {$o = "";} - - if($t ne "") - { - push @s, $t; - } else { - push @s, $o; - } - } - - $snmpopt = ':' . (join ':', @s); - } - - my $newopt={}; # Perhaps unecessary initialization but... - - foreach my $o (keys %$opt) { - my $ovalue = $$opt{$o}; - - $$newopt{$o} = $ovalue - unless - ($o =~ /^fullcmd$/ or - $o =~ /^community$/ or - $o =~ /^snmp-options$/ or - $o =~ /^global$/ or - $o =~ /^output$/ - ); - - # Ok, copy the --globals array from $$opt so we know - # that which global(s) to print out into the config. - push @{$$newopt{$o}}, @{$$opt{$o}} if ($o =~ /^global$/); - - # Go through these --global statements one by one. - # If anyone of them contains Options[] for any of the - # targets [_], [^] or [_], process those statements - # tenderly and populate the $$flags{}{} hashes accordingly. - for my $g (@{$$opt{"global"}}) { - if ($g =~ /^options\[([_^\$])\]:\s*(.*)$/i){ - my ($t,$fs); - $t = $1; - $fs = $2; - $t =~ s/_/default/; - $t =~ s/\^/prepend/; - $t =~ s/\$/append/; - - # If a line like "options[X]:" is found clear - # all flags for that category and then go to next - # --global 'Options[..' line if any. - if ($fs =~ /^\s*$/) { - $$flags{$t} = {}; - next; - } else { - for my $f (split /\s*,\s*/,$fs) { - $$flags{$t}{$f} = "set"; - } - } - } - } - $$opt{$o} = [] if ($o =~ /^global$/); - } - - # Now let this router get it's own copy of - # the "currently effective" flags. - # Note, Options[_] should only be considered - # if Options[^] and Options[$] both are absent. - - my $newflags = {}; - - if((0 == keys %{$$flags{prepend}}) - and (0== keys %{$$flags{append}})) { - for my $f (keys %{$$flags{default}}) { - $$newflags{$f}="set"; - } - } else { - for my $f (keys %{$$flags{prepend}}, - keys %{$$flags{append}}) { - $$newflags{$f}="set"; - } - } - - if(defined $$opt{'dns-domain'}) { - $dnsdomain=$$opt{'dns-domain'}; - } else { - $dnsdomain=""; - } - - $routerkey = - "${community}\@${routername}" - . (($dnsdomain eq "")?"":".") - . "${dnsdomain}${snmpopt}"; - - $$routers{$routerkey}= { - # rawname is the unprocessed string from the - # command line. - rawname => $rawname, - - # opt is the commandline options which are - # in effect for THIS particular router. - opt => $newopt, - - # noofrouter is the unique number for the - # router. The first router on the command - # line is given number 1, the second number 2 - # and so on. - noofrouter => $$noofrouter, - - # flags contains which --global 'Options[^_$]: flags' - # are effective for THIS particular router. - flags => $newflags, - - # community is the SNMP community used for the router - community => $community, - - # snmpopt is the SNMP options on the form - # [port[:timeout[:retries[:backoff[:version]]]]] - # The empty string simply means that no - # specific SNMP options has been given. - 'snmp-options' => $snmpopt, - snmpopt_current => $snmpopt, - - # dns-domain is a domain which should be added - # to the routers hostname. - # e.g if dns-domain is place.xyz and host is router - # the host "router.place.xyz" will be polled. - # If host is "router.dept" the poll will be against - # "router.dept.place.xyz". - 'dns-domain' => $dnsdomain, - - # routername is the routers name as given on the - # command line but with SNMP community (if given) - # and SNMP options (if given) stripped. - # - # (Yes, routername COULD be on the form - # "host.domain" or "host.subdomain.domain") - routername => $routername, - - # routerkey is the same as the has key used for the - # router, which is the router name with everything - # taken into account: community, dns-domain and - # snmp-options. This is the value used when doing - # SNMP communication with the router. - routerkey => $routerkey, - }; - } -} # end addrouter - -sub html_escape ($) { - my $s = shift || ''; - $s =~ s/&/&/g; - $s =~ s//>/g; - $s =~ s/[\n\r]+([^\n\r])/
\n $1/g; - return $s; -} # end html_escape - -sub oid_pick ($$$;@){ - my $router = shift; - my $v3opt = shift; - my @oids = @_; - local $SNMP_Session::suppress_warnings = 3; - foreach my $oid (@oids){ - local $SNMP_Session::errmsg = undef; - my $counter = snmpget($router,$v3opt,$oid); - if (not defined $SNMP_Session::errmsg and defined $counter and $counter ne '' ) { - debug('base',"oid_pick - found $oid to work for $router"); - return $oid; - } - } - debug('base',"oid_pick - none of ".(join ",",@oids)." seem to work for $router"); - return undef; -} - - -sub parsev3 ($) { - my $opt = shift; - my %v3opt; - if (!exists ($$opt{username})) { - die "SMNP V3 requires a --username paramter as part of the User Security Model"; - } else { - $v3opt{username} = $$opt{username}; - } - $v3opt{contextname} = $$opt{contextname} if exists($$opt{contextname}); - if (exists ($$opt{authkey})) { - die "Can't use both an --authkey and --authpassword in the User Security Model" if exists($$opt{authpassword}); - $v3opt{authkey} = $$opt{authkey}; - } - if (exists ($$opt{authpassword})) { - die "Use of --authpassword requires --contextengineid" if !exists($$opt{contextengineid}); - $v3opt{authpassword} = $$opt{authpassword}; - } - if (exists ($$opt{authprotocol})) { - die "Only sha and md5 are defined for --authprotocol" if $$opt{authprotocol} !~ /^(md5|sha)$/i; - die "--authprotocol can only be used with --authpassword or --authkey" if ! exists($$opt{authpassword}) and ! exists($$opt{authkey}); - ($v3opt{authprotocol}) = (lc($$opt{authprotocol}) =~ /^(md5|sha)$/); - } - if (exists ($$opt{privkey})) { - die "Can't use both an --privkey and --privpassword in the User Security Model" if exists($$opt{privpassword}); - die "Can't have privacy parameters without authentication in the User security Model" if ! exists($$opt{authpassword}) and ! exists($$opt{authkey}); - $v3opt{privkey} = $$opt{privkey}; - } - if (exists ($$opt{privpassword})) { - die "Use of --privpassword requires --contextengineid" if !exists($$opt{contextengineid}); - die "Can't have privacy parameters without authentication in the User security Model" if ! exists($$opt{authpassword}) and ! exists($$opt{authkey}); - $v3opt{privpassword} = $$opt{privpassword}; - } - if (exists ($$opt{privprotocol})) { - die "Only des, 3des, 3desede, aes, aes128 are defined for --privprotocol" if $$opt{privprotocol} !~ /^(?:3?des(?:ede)?|aes(?:128)?)$/; - die "--privprotocol can only be used with --privpassword or --privkey" if ! exists($$opt{privpassword}) and ! exists($$opt{privkey}); - $v3opt{privprotocol} = lc($$opt{privprotocol}); - } - return %v3opt; -} - - -sub init () { - snmpmapOID('sysObjectID' => '1.3.6.1.2.1.1.2.0', - 'CiscolocIfDescr' => '1.3.6.1.4.1.9.2.2.1.1.28', - 'CiscoCatalystPortName' => '1.3.6.1.4.1.9.5.1.4.1.1.4', - 'ppPortName' => '1.3.6.1.4.1.2272.1.4.10.1.1.35', - 'vlanTrunkPortDynamicStatus'=> '1.3.6.1.4.1.9.9.46.1.6.1.1.14', - 'vmVlan' => '1.3.6.1.4.1.9.9.68.1.2.2.1.2', - 'ifAlias' => '1.3.6.1.2.1.31.1.1.1.18'); -} # end init - -__END__ - -=pod - -=head1 NAME - -cfgmaker - Creates mrtg.cfg files (for mrtg-2.17.7) - -=head1 SYNOPSIS - -cfgmaker [options] [community@]router [[options] [community@]router ...] - -=head1 OPTIONS - - --ifref=nr interface references by Interface Number (default) - --ifref=ip ... by Ip Address - --ifref=eth ... by Ethernet Number - --ifref=descr ... by Interface Description - --ifref=name ... by Interface Name - --ifref=type ... by Interface Type - You may also use multiple options separated by commas, - in which case the first available one is used: - e.g. --ifref=ip,name,nr - - --ifdesc=nr interface description uses Interface Number (default) - --ifdesc=ip ... uses Ip Address - --ifdesc=eth ... uses Ethernet Number - --ifdesc=descr ... uses Interface Description - --ifdesc=name ... uses Interface Name - --ifdesc=catname ... uses CatOS Interface Name - --ifdesc=ppname ... uses Passport Port Name - --ifdesc=alias ... uses Interface Alias - --ifdesc=type ... uses Interface Type - You may also use multiple options separated by commas, - in which case the first available one is used: - e.g. --ifdesc=catname,ppname,descr,alias,ip,name,nr - - --if-filter=f Test every interface against filter f to decide wether - or not to include that interface into the collection. - Currently f is being evaluated as a Perl expression - and it's truth value is used to reject or accept the - interface. - (Experimental, under development, might change) - - --if-template=templatefile - Replace the normal target entries for the interfaces - with an entry as specified by the contents in the file - templatefile. The file is supposed to contain Perl - code to be executed to generate the lines for the - target in the configuration file. - (Experimental, under development, might change) - - --host-template=templatefile - In addition to creating targets for a host's interfaces - do also create targets for the host itself as specified - by the contents in the file templatefile. The file is - supposed to contain Perl code to be executed to generate - the lines for the host related targets (such as CPU, - ping response time measurements etc.) in the config- - uration file. - (Experimental, under development, might change) - - --global "x: a" add global config entries - - --no-down do not look at admin or opr status of interfaces - - --show-op-down show interfaces which are operatively down - - --zero-speed=spd use this speed in bits-per-second as the interface - speed for all interfaces that return a speed of 0 - via ifSpeed/ifHighSpeed. 100Mbps = 100000000 - - --subdirs=format give each router its own subdirectory, naming each per - "format", in which HOSTNAME and SNMPNAME will be - replaced by the values of those items -- for instance, - --subdirs=HOSTNAME or --subdirs="HOSTNAME (SNMPNAME)" - - --noreversedns do not reverse lookup ip numbers - - --community=cmty Set the default community string to "cmty" instead of - "public". - - --enable-ipv6 Enable IPv6 support, if the required libraries are - present. Numeric IPv6 addresses must be enclosed - in square brackets, e.g. public@[2001:760:4::1]:161 - - --use-16bit Use 16bit SNMP request IDs to query all routers. - - --snmp-options=:[][:[][:[][:[][:]]]] - - Specify default SNMP options to be appended to all - routers following. Individual fields can be empty. - Routers following might override some or all of the - options given to --snmp-options. - - --dns-domain=domain - Specifies a domain to append to the name of all - routers following. - - --nointerfaces Don't do generate any configuration lines for interfaces, - skip the step of gathering interface information and - don't run any interface template code. - - --interfaces Generate configuration lines for interfaces (this is the - default). The main purpose of this option is to negate - an --nointerfaces appearing earlier on the command line. - - --help brief help message - --man full documentation - --version print the version of cfgmaker - - --output=file output filename default is STDOUT - -=head1 DESCRIPTION - -B creates MRTG configuration files based on information -pulled from a router or another SNMP manageable device. - -[IB<@>]I - -I is the community name of the device you want to create a -configuration for. If not specified, it defaults to 'B'; you might -want to try this first if you do not know the community name of a -device. If you are using the wrong community name you will get no -response from the device. - -I is the DNS name or the IP number of an SNMP-managable device. -Following the name you can specify 6 further options separated by -colons. The full syntax looks like this: - -B[:[B][:[B][:[B][:[B][:B]]]]] - -Of special interest may be the last parameter, B. If you set this to -'2' then your device will be queried with SNMP version 2 requests. This -allows you to poll the 64 bit traffic counters in the device and will thus work -much better with fast interfaces (no more counter overrun). Note that the -order in which the routers are specified on the command line do matter as -the same order is used when the configuration file is generated. The first -specified router has it's configuration lines genrated first, followed by -the lines belonging to the next router and so on. - -Note that the first line of the generated cfg file will contain all the -commandline options you used for generating it. This is to allow for the -easy 'regeneration' in case you want to add newhosts or make some other -global change. - -=head2 Configuration - -Except for the B<--output> and B<--global> options, all options affect -only the routers following them on the command line. If an option -specified earlier on the command line reappears later on the command -line with another value, the new value overrides the old value as far as -remaining routers are concerned. This way options might be tailored for -groups of routers or for individual routers. - -See B<--output> and B<--global> for how their behaviour is affected by -where or how many times they appear on the command line. - -See the B below on how to set an option differently for -multiple routers. - -=over - -=item B<--help> - -Print a brief help message and exit. - -=item B<--man> - -Prints the manual page and exits. - -=item B<--version> - -Print the version of cfgmaker. This should match the version of MRTG -for which config files are being created. - -=item B<--ifref> B|B|B|B|B - -Select the interface identification method. Default is B which -identifies the router interfaces by their number. Unfortunately the -interface numbering scheme in an SNMP tree can change. Some routers -change their numbering when new interfaces are added, others change -thier numbering every full moon just for fun. - -To work around this sad problem MRTG can identify interfaces by 4 -other properties. None of these works for all interfaces, but you -should be able to find one which does fine for you. Note that -especially ethernet addrsses can be problematic as some routers have -the same ethernet address on most of their interface cards. - -Select B to identify the interface by its IP number. Use B to -use the ethernet address for identification. Use B to use -the Interface description. Or use B to use the Interface name. - -You can specify multiple properties if you wish, separated by commas. -In this case, cfgmaker will use the first item in the list which -can provide unique identification. This allows you to specify, for -example, to use IP address and to use ifName if this is not defined: - --ifref ip,name - -If your chosen method does not allow unique interface identification on -the device you are querying, B will tell you about it. - -=item B<--ifdesc> B|B|B|B|B|B|B - -Select what to use as the description of the interface. The description -appears in the C property for the target as well as the text header -in the HTML code defined in the target's C. Default is to use -B which is just the interface number which isn't always useful -to the viewer of the graphs. - -There are 6 other properties which could be used. Use B if you want -to use the interface's IP-address. Use B if you want to use the -interface's ethernet address. If you want a better description, you can -use either B, B or B. Exactly what each of these do -varies between different equipment so you might need to experiment. For -instance, for a serial interface on a Cisco router running IOS using B -might result in C<"S0"> being the interface description , B might result -in C<"Serial0"> and B might result in C<"Link to HQ"> (provided that is -what is used as the interface's C in the router's configuration). - -Finally, if you want to describe the interface by it's Btype -(i.e C<"ethernetCSMA">, C<"propPointtoPoint"> etc) you can use B. - -You can specify multiple properties if you wish, separated by commas. -In this case, cfgmaker will use the first item in the list which -is available for this interface. This allows you to specify, for -example, to use any of the different aliases in order of preference. - -=item B<--if-filter> 'B' - -First of all, this is under some development and is experimental. - -Use this if you want to have better control over what interfaces gets -included into the configuration. The B is evaluated -as a piece of Perl code and is expected -to return a truth value. If true, include the interface and if false, -exclude the interface. - -For a further discussion on how these filters work, see the section -L
below. - -=item B<--if-template> B - -First of all, this is under some development and is experimental. - -Use this if you want to control what the line for each target should look -like in the configuration file. The contents of the file B -will be evaluated as a Perl program which generates the lines using certain -variables for input and output. - -For a further discussion on how these templates work, see the section -L
below. - -=item B<--host-template> B - -First of all, this is under some development and is experimental. - -Use this if you want to have some extra targets related to the host itself -such as CPU utilization, ping response time to the host, number of busy -modems etc. The contents of the file B will be evaluated -once per host as a Perl program which generates the lines using certain -variables for input and output. - -For a further discussion on how these templates work, see the section -L
below. - -=item B<--community> B - -Use this to set the community for the routers following on the command -line to B. Individual routers might overrride this -community string by using the syntax BB<@>B. - -=item B<--enable-ipv6> - -This option enables IPv6 support. It requires the appropriate perl -modules; if they are not found then IPv6 is disabled (see the ipv6 -documentation). - -cfgmaker will use IPv6 or IPv4 depending on the target. If the target -is a numeric address, the protocol depends on the type of address. If the -target is a hostname, cfgmaker will try to resolve the name first to an -IPv6 address then to an IPv4 address. - -IPv6 numeric addresses must be specified between square braces. - -For example: - - cfgmaker --enable-ipv6 [2001:760:4::1]:165:::2 - -If the target has both an IPv6 address and an IPv4 address with the same -hostname, cfgmaker first queries the target using IPv6 and falls back to -IPv4 if it fails. This is useful for targets which don't support SNMP -over IPv6. - -=item B<--use-16bit> - -This option forces the use of 16bit SNMP request IDs. Some broken SNMP -agents do not accept 32bit request IDs. Try to avoid this option as much -as possible, complain to your agent vendor instead. - -=item B<--snmp-options> :[B][:[B][:[B][:[B][:B]]]] - -Use this to set the default SNMP options for all routers following on the -command line. Individual values might be omitted as well as trailing -colons. Note that routers might override individual (or all) values -specified by B<--snmp-options> by using the syntax - -B[:[B][:[B][:[B][:[B][:B]]]]] - -=item B<--global> B<">IB<"> - -Use this to add global options to the generated config file. -You can call B<--global> several times to add multiple options. -The line will appear in the configuration just before the config for -the next router appearing on the command line. - - --global "workdir: /home/mrtg" - -If you want some default Options you might want to put - - --global "options[_]: growright,bits" - -Specifying B<--global> after the last router on the command line will -create a line in the configuration file which will appear after all the -routers. - -=item B<--noreversedns> - -Do not try to reverse lookup IP numbers ... a must for DNS free environments. - -=item B<--no-down> - -Normally cfgmaker will not include interfaces which are marked -anything but administratively and operationally UP. With this -switch you get them all. - -=item B<--show-op-down> - -Include interfaces which are operatively down. - -=item B<--zero-speed> I - -Assign this speed in bits-per-second to all interfaces which return 0 -for ifSpeed and ifHighSpeed. Some switches, notably Foundry equipment, -return a speed of zero for some interfaces. For example, to have -all interfaces reporting zero set to 100Mbps, use ---zero-speed=100000000. - -=item B<--subdirs> I - -Give each router its own subdirectory for the HTML and graphics (or -.rrd) files. The directory name is the given I string with a -couple of pattern replacements. The string "HOSTNAME" will be -replaced by the hostname of the router (however you specified it on -the B commandline -- it may be an actual hostname or just an -IP address), and "SNMPNAME" will be replaced with the device's idea of -its own name (the same name that appears on the right side of the -"Title" lines). For instance, a call like: - - cfgmaker --subdirs=HOSTNAME__SNMPNAME public@10.10.0.18 - -would result in the generation of lines looking something like: - - Directory[10.10.0.18_1]: 10.10.0.18__fp2200-bothrip-1.3 - -=item B<--output> I - -Write the output from B into the file I. The default -is to use C. B<--output> is expected to appear only once on the -command line. If used multiple times, the file specified by the last -B<--output> will be used. - -=item B<--nointerfaces> - -Don't generate configuration lines for interfaces. - -This makes cfgmaker skip all steps related to interfaces which means -it will not do any polling of the router to retrieve interface -information which speeds up the execution of cfgmaker and it will -neither run any interface templates. - -=item B<--interfaces> - -This makes cfgmaker generate configuration lines for interfaces (the -default behaviour). - -The main usage of this option is to negate an --nointerfaces appearing -earlier on the command line. - - -=back - -=head2 SNMP V3 Options - -B supports SNMP V3 using the B perl module. There are optional -parameters affecting SNMP operation. - -=over - -=item --enablesnmpv3 {yes|no} - -The B<--enablesnmpv3> option is an optional flag to check for the presence of -the B libraries. B will try to determine whether this flag is -required and will set the values automatically. - -=back - -=head3 SNMPv3 Arguments - -A SNMP context is a collection of management information accessible by a SNMP -entity. An item of management information may exist in more than one context -and a SNMP entity potentially has access to many contexts. The combination of -a contextEngineID and a contextName unambiguously identifies a context within -an administrative domain. In a SNMPv3 message, the contextEngineID and -contextName are included as part of the scopedPDU. All methods that generate -a SNMP message optionally take a B<--contextengineid> and B<--contextname> -argument to configure these fields. - -=over - -=item Context Engine ID - -The B<--contextengineid> argument expects a hexadecimal string representing -the desired contextEngineID. The string must be 10 to 64 characters (5 to -32 octets) long and can be prefixed with an optional "0x". Once the -B<--contextengineid> is specified it stays with the object until it is changed -again or reset to default by passing in the undefined value. By default, the -contextEngineID is set to match the authoritativeEngineID of the authoritative -SNMP engine. - -=item Context Name - -The contextName is passed as a string which must be 0 to 32 octets in length -using the B<--contextname> argument. The contextName stays with the object -until it is changed. The contextName defaults to an empty string which -represents the "default" context. - -=back - -=head3 User-based Security Model Arguments - -The User-based Security Model (USM) used by SNMPv3 requires that a securityName -be specified using the B<--username> argument. The creation of a Net::SNMP -object with the version set to SNMPv3 will fail if the B<--username> argument -is not present. The B<--username> argument expects a string 1 to 32 octets -in length. - -Different levels of security are allowed by the User-based Security Model which -address authentication and privacy concerns. A SNMPv3 target will -derive the security level (securityLevel) based on which of the following -arguments are specified. - -By default a securityLevel of 'noAuthNoPriv' is assumed. If the B<--authkey> -or B<--authpassword> arguments are specified, the securityLevel becomes -'authNoPriv'. The B<--authpassword> argument expects a string which is at -least 1 octet in length. Optionally, the B<--authkey> argument can be used so -that a plain text password does not have to be specified in a script. The -B<--authkey> argument expects a hexadecimal string produced by localizing the -password with the authoritativeEngineID for the specific destination device. -The C utility included with the Net::SNMP distribution can be used to create -the hexadecimal string (see L). - -Two different hash algorithms are defined by SNMPv3 which can be used by the -Security Model for authentication. These algorithms are HMAC-MD5-96 "MD5" -(RFC 1321) and HMAC-SHA-96 "SHA-1" (NIST FIPS PUB 180-1). The default -algorithm used by the module is HMAC-MD5-96. This behavior can be changed by -using the B<--authprotocol> argument. This argument expects either the string -'md5' or 'sha' to be passed to modify the hash algorithm. - -By specifying the arguments B<--privkey> or B<--privpassword> the securityLevel -associated with the object becomes 'authPriv'. According to SNMPv3, privacy -requires the use of authentication. Therefore, if either of these two -arguments are present and the B<--authkey> or B<--authpassword> arguments are -missing, the creation of the object fails. The B<--privkey> and -B<--privpassword> arguments expect the same input as the B<--authkey> and -B<--authpassword> arguments respectively. - -The User-based Security Model described in RFC 3414 defines a single encryption -protocol to be used for privacy. This protocol, CBC-DES "DES" (NIST FIPS PUB -46-1), is used by default or if the string 'des' is passed to the -B<--privprotocol> argument. By working with the Extended Security Options -Consortium http://www.snmp.com/eso/, the module also supports additional -protocols which have been defined in draft specifications. The draft -http://www.snmp.com/eso/draft-reeder-snmpv3-usm-3desede-00.txt -defines the support of CBC-3DES-EDE "Triple-DES" (NIST FIPS 46-3) in the -User-based Security Model. This protocol can be selected using the -B<--privprotocol> argument with the string '3desede'. The draft -http://www.snmp.com/eso/draft-blumenthal-aes-usm-04.txt -describes the use of CFB128-AES-128/192/256 "AES" (NIST FIPS PUB 197) in the -USM. The three AES encryption protocols, differentiated by their key sizes, -can be selected by passing 'aescfb128', 'aescfb192', or 'aescfb256' to the -B<-privprotocol> argument. - -=head2 Details on Filters - -The purpose of the filters is to decide which interfaces to accept and -which interfaces to reject. This decision is done for each interface by -evaluating the filter expression as a piece of Perl code and investigating -the result of the evaluation. If true, accept the interface otherwise -reject it. - -When working with filters, remember that Perl has it's own idea of what truth -and false is. The empty string "" and the string "0" are false, all other -strings are true. This further imples that any integer value of 0 is -false as well as any undef value. It also implies that all references -are considered true. - -As the filter is evaluated as a Perl expression, several useful constructs -in Perl are worth mentioning: - -Expressions might be grouped by using parentheses "()". Expressions might -be combined using boolean operators such as the following: - -=over - -=item "B" (equivalent with "B<&&>") - -Boolean "and" of the two expressions, is only true if both expressions are -true. Example: I B I - -=item "B" (equivalent with "B<||>") - -Boolean "or" of the two expressions, is true if either or both expressions -are true. Example: I B I - -=item "B" (equivalent with "B") - -Boolean negation of a single expression. Example: B I . -Yet another example: BI - -=back - -(For more details on this I recommend a book on Perl) - -=head3 Predefined Filter Variables - -To facilitate, there are a number of predefined values available to use -in the filter. Note that these variables are also available when templates -interfaces are evaluated (but not host templates). - -Caveat: All these variables' names begin with a dollar sign ($), which -is a syntactic requirement for scalar variables in Perl. The danger here -is that the dollar sign in many shells is an active character (often -used for shell variables exactly as in Perl variables) so it is important -to ensure that the Perl expression isn't evaluated by the command line -shell as shell code before being passed to cfgmaker as command line -arguments. In shells like Bourne shell, ksh shell or bash shell, placing -the entire expression within single qoutes will avoid such accidental -evaluation: - - '--if-filter=($default_iftype && $if_admin)' - -=over - -=item B<$if_type> - -This is an integer specifying the interface type as -per the SNMP standards and as reported by the polled device. A complete list -of interface types would be impractical for this document , but there are -a number predefined varables below. Normally, cfgmaker puts in the target's -PageTop this iftype value within paranthesis after the name of the interface -type. (e.g "propPointToPointSerial (22)"). - -Here's a list of some of the most common interface types by number: - - 6 ethernetCsmacd - 7 iso88023Csmacd - 9 iso88025TokenRing - 15 fddi - 19 E1 - 20 basicISDN - 21 primaryISDN - 22 propPointToPointSerial - 23 ppp - 24 softwareLoopback - 30 ds3 - 32 frame-relay - 33 rs232 - 37 atm - 39 sonet - 44 frameRelayService - 46 hssi - 49 aal5 - 53 propVirtual - 62 Fast Ethernet (100BaseT) - 63 ISDN & X.25 - 69 Full Duplex Fast Ethernet (100BaseFX) - 94 Asymetric Digital Subscriber Loop (ADSL) - 117 Gigabit Ethernet - 134 ATM Sub Interface - -=item B<$default> - -True if and only if cfgmaker normally should -accepted the interface based on the interfaces administrative and -operational state (taking the flags B<--no-down> and B<--show-op-down> into -account) and it's type (and a few other things). - -=item B<$default_ifstate> - -True if and only if cfgmaker would have accepted the -interface based on it's operational and administrative states (also taking -into account the presence of the flags B<--no-down> and B<--show-op-down>). - -=item B<$default_iftype> - -True if and only if cfgmaker would have accepted the -interface based on it's type (and a few type specific details in addition). - -=item B<$if_admin> - -True if and only if the interface is in an adminstrative up -state. - -=item B<$if_oper> - -True if and only if the interface is in an operational up -state. - -=back - -A number of variables are also predefined to easily decide if an interface -belong to a certain cathegory or not. Below is all those variables listed -together with which if_type numbers each variable will be true for. Note -that some variables refer to other variables as well. - -=over - -=item B<$if_is_ethernet> - -True for ethernet interfaces (nr 6, 7, 26, 62, 69 and 117). - -=item B<$if_is_isdn> - -True for various ISDN interface types (nr 20, 21, 63, 75, 76 and 77) - -=item B<$if_is_dialup> - -True for dial-up interfaces such as PPP as well -as ISDN. (nr 23, 81, 82 and 108 in addition to the numbers of -B<$if_is_isdn>). - -=item B<$if_is_atm> - -True for miscellaneous ATM related interface types (nr 37, 49, 107, 105, -106, 114 and 134). - -=item B<$if_is_wan> - -True for WAN interfaces point to point, Frame Relay and High Speed Serial ( 22,32,44,46) - -=item B<$if_is_lan> - -True for LAN interfaces (8, 9, 11, 15, 26, 55, 59, 60 and 115 in addition -to the numbers of B<$if_is_ethernet>). - -=item B<$if_is_dsl> - -True for ADSL, RDSL, HDSL and SDSL (nr 94, 95, 96, 97) - -=item B<$if_is_loopback> - -True for software loopback interfaces (nr 24) - -=item B<$if_is_ciscovlan> - -True for Cisco VLAN interfaces (interfaces with the -word Vlan or VLAN in their ifdescs) - -=item B<$if_vlan_id> - -Returns the vlan id associated with a specific port -on Cisco Catalyst switches under both Catalyst OS -and IOS, and 3Com switches. If it is not a vlan interface, will return undef. - -=item B<$if_cisco_trunk> - -Returns the trunking state of a specific port -on Cisco Catalyst switches under both Catalyst OS -and IOS. Returns "1" if the interface is a trunk, undef otherwise. - -=item B<$if_MTU> - -Returns the Maximum Transfer Unit associated with a specific port. - -=back - -Besides that, you can also use the variables defined for templates below. -Further, all the variables available in cfgmaker is at the scripts disposal -even if the use of such features is discouraged. More "shortcuts" in the -form of variables and functions will be made available in the future instead. - -=head3 Examples on Filters - -The following filter will not affect which interfaces get's included or -excluded, it will make cfgmaker behave as normally. - - '--if-filter=$default' - -The following filter will make cfgmaker exclude PPP (23) interfaces: - - '--if-filter=$default && $if_type!=23' - -The following filter will make cfgmaker behave as usual except that it will -consider the operational state of an interface irrelevant but still reject -all interfaces which are administratively down. - - '--if-filter=$if_admin && $default_iftype' - -=head2 Details on Templates - -The contents of the template files are evaluated as a Perl program. A -number or Perl variables are available for the program to read and others -are used to be written to. - -As quite a few of the predefined variables has values which are are supposed -to be used in HTML code some of them have an "HTML-escaped" variant, e.g -$html_syslocation is the HTML escaped variant of $syslocation. The HTML -escaping means that the chars "<", ">" and "&" are replaced by "<", -">" and "&" and that newlines embedded in the string are prepended -with "
" and appended with a space character (if a newline is last in the -string it is not touched). - -=head3 Writable Template Variables - -These are the variables available to store the configuration lines in. -Some of them are initialized prior to the evaluation of the template but -such content normally is comments for inclusion in the final configuration -file so those variables might be reset to the empty string in the template -code to eliminate the comments. The other way around is also possible, the -contents of these variables might be extended with further information -for various reasons such as debugging etc. - -Once the template has been evaluated, the following happens: if the -template is a interface template and the actual interface for some reason -is rejected and thus needs to be commented out, all the lines in the -variable B<$target_lines> are turned into comments by adding a hash mark -("#") at their beginning. Then all the variables B<$head_lines>, -B<$problem_lines> , B<$target_lines> and B<$separator_lines> -are concatenated together to form the lines to add to the configuration file. - -=over - -=item B<$target_lines> - -This variable is the placeholder for the configuration lines created -by the template. B<$target_lines> is predefined to be empty when -the template code is evaluated. - -=item B<$head_lines> - -This variable is intended to be the placeholder for the comment line -appearing just before the target in the configuration file. It is -initialized with that comment line before the evaluation of the template -code and if the template doesn't modify B<$head_lines> during evaluation, -the comment will look like usual in the config file. - -=item B<$problem_lines> - -This variable is intended to be the placholder for the comment lines -describing any problems which might have been encountered when trying -to add the target into the configuration. For host templates it's -normally not used and for those it's predefined as the empty string. -For interface templates B<$problem_lines> is predefined with -the error description comments which cfgmaker normally would use for -rejected interfaces or as the empty string for accepted interfaces. - -It is possible to test against B<$problem_lines> to find out if -an interface will be included or rejected but this is not recommended. -Test against B<$if_ok> instead. - -=item B<$separator_lines> - -This variable is the placeholder for the string to use as the separator -between the code for individual targets. The contents of this variable -is put after each target (so the lines will appear after the end of the -last target in the config as well). - -=back - -=head3 Predefined Template Variables - -All the variables below are available for interface templates to use. -For host templates, only those listed under L -are available. - -For interface templates the variables listed under -L are also available. - -=head3 Host and System Variables - -=over - -=item B<$router_name> - -This is the fully qualified name for the router. It is affected by the -following items on the command line: the router name itself and -B<--dns-domain>. - -=item B<$router_connect> - -This is the reference string for the router being polled. It is on the -form community@router possibly followed by some snmp options. It is -affected by the following items on the command line: the router name -itself, B<--community>, B<--snmp-options> and B<--dns-domain>. -(There's no HTML escaped variant available) - -=item B<$directory_name> - -This variable should contain the directory name as cfgmaker normally would -use as the value for the "Directory[]" directive. The value is determined -by the B<--subdirs> command line option. If B<--subdirs> isn't specified -B<$directory_name> will be the empty string. (There's no HTML escaped -variant available) - -=item B<$syscontact> - -This variable is the router's SNMP sysContact value. (HTML escaped -variant: B<$html_syscontact>) - -=item B<$sysname> - -This variable is the router's SNMP sysName value. (No HTML escaped -variant available) - -=item B<$syslocation> - -This variable is the router's SNMP sysLocation value. (HTML escaped -variant: B<$html_syslocation>) - -=item B<$sysdescr> - -This variable is the router's SNMP sysDescr value. It is normally not used -by cfgmaker but might be useful in a template. (HTML escaped variant: -B<$html_sysdescr>) - -=back - -=head3 Interface Target Related Variables - -=over - -=item B<$target_name> - -This is what cfgmaker normally would use as the the name of the target. -The target name is what is found within the square brackets, "[]", for target -directives. (There's no HTML escaped variant available) - -=item B<$if_ref> - -This the reference string for the interface. It is expected to be used -in the "Target[xyz]" directive to distinguish what interface to use. The -value of this variable is affected by the B<--ifref> command line option. -It is normally used together with B<$router_connect>. -(There's no HTML escaped variant available) - -=item B<$if_ok> - -This variable is true if the interface is going to be included into the -configuration file, otherwise false. Don't test against other variables -such as B<$problem_lines> to find out if an interface will be rejected -or not, use this B<$if_ok> instead. - -=item B<$default_target_lines> - -This variable contains all the target lines which cfgmaker by default outputs -for this interface. It's useful if you want to have the "standard target" -but want to add some extra lines to it by using a template. - -=back - -By default cfgmaker uses the following directives for each target it -generates: Target[], SetEnv[], MaxBytes[], Title[], PageTop[] and if -there is any directory specified also the Directory[] directive. - -To facilitate the creation of templates which generates target configs -which are similar to the default one, each of the above mentioned -directive lines have a corresponding variable containing the line as -cfgmaker would have output it by default. - -Note that none of these have a HTML escaped variant, text in them is -HTML escaped where needed. Also note that they do not have any newline -at the end. - -=over - -=item B<$default_target_directive> - -This variable contains the default string for the Target[] directive line. - -=item B<$default_setenv_directive> - -This variable contains the default string for the SetEnv[] directive line. - -=item B<$default_directory_directive> - -This variable contains the default string for the Directory[] directive line -which means it is an empty string (with no newline) if there's no directory. - -=item B<$default_maxbytes_directive> - -This variable contains the default string for the MaxBytes[] directive line. - -=item B<$default_title_directive> - -This variable contains the default string for the Title[] directive line. - -=item B<$default_pagetop_directive> - -This variable contains the default string for the PageTop[] directive lines. - -=back - -=head3 Interface Network Configuration Variables - -=over - -=item B<$if_ip> - -This variable should contain the IP-address of the interface, if any has -been assigned to it. (There's no HTML escaped variant available) - -=item B<$ifindex> - -This variable is the SNMP ifIndex for the interface which per definition -always is an integer. (There's no HTML escaped variant available) - -=item B<$if_index> - -Equivalent with B<$ifindex>. - -=item B<$if_eth> - -Contains the ethernet address of the interface, if any. (There's no HTML -escaped variant available) - -=item B<$if_speed> - -This variable is the speed in bytes/second (with prefixes). (There's no -HTML escaped variant available) - -=item B<$if_speed_str> - -This variable is a cooked speed description which is either in bits or -bytes depending on wether or not the bits option is active and also with -the proper prefix for the speed (k, M, G etc). (No HTML escaped variant -available) - -=item B<$if_type_desc> - -This variable is a textual description of the interface type. (HTML -escaped variant: B<$html_if_type_desc>) - -=item B<$if_type_num> - -This variable the integer value corresponding to the interface type (for a -listing for the value for the more common interface types, see the section -DETAILS ON FILTERS above). (No HTML escaped variant available) - -=item B<$if_dns_name> - -This is the DNS name for the interface. (No HTML escaped variant available) - -=back - -=head3 Interface Name, Description and Alias Variables - -It might seem confusing with both I, I and I in -this context and to some extent it is. I and I are -usually supported on most equipment but how they are used varies, both -between manufacturers as well as between different cathegories of equipment -from the same manufacturer. The I is at least supported by Cisco -IOS, and that variable contains whatever is used in the IOS statement -called "description" for the interface (not to be confused with the SNMP -variables for I). - -For better control from the command line consider B<$if_title_desc> which contents -are controlled by the B<--if-descr> command line option. - -=over - -=item B<$if_snmp_descr> - -This variable should contain the "raw" description of the interface as -determined by the SNMP polling of the router. (HTML escaped variant: -B<$html_if_snmp_descr>) - -=item B<$if_snmp_name> - -The "raw" name for the interface as provided by SNMP polling. (HTML escaped -variant: B<$html_if_snmp_name>) - -=item B<$if_snmp_alias> - -The "raw" ifAlias for the interface as provided by SNMP polling. (HTML -escaped variant: B<$html_if_snmp_alias>) - -=item B<$if_cisco_descr> - -The "raw" CiscolocIfDescr for the interface as provided by SNMP polling. -(HTML escaped variant: B<$html_if_cisco_descr>) - -=item B<$if_description> - -This is the "cooked" description string for the interface, taking into account -the SNMP values found for the interface's RDescr, ifAlias and -CiscolocIfDescr. (HTML escaped variant: B<$html_if_description>) - -=item B<$if_title> - -The full string cfgmaker by default would have used for the Title[] directive -in the configuration as well as the content of the topmost H1 tag in the -PageTop[]. Is composed by the contents of B<$desc_prefix>, -B<$if_title_desc> and B<$sysname>. - -As B<$if_title> depends on B<$if_title_desc>, it is possible to indirectly -control B<$if_title> by using the command line option B<--if-descr>. - -(HTML escaped variant: B<$html_if_title>) - -=item B<$if_port_name> - -If the host is a Cisco Catalyst LAN switch, this variable is the name of -that port. (No HTML escaped variant available) - -=item B<$if_pp_port_name> - -If the host is a Nortel Passport LAN switch, this variable is the name of -that port. (No HTML escaped variant available) - -=item B<$desc_prefix> - -This variable is a prefix of the description of what the target is to use in -the "Title[]" directive and in the H1 section of the "PageTop[]". Default is -"Traffic analysis for ". (HTML escaped variant: B<$html_desc_prefix>) - -=item B<$if_title_desc> - -This is the description of the interface normally used by cfgmaker as part -of the variable B<$if_title>. The latter is used as the full string in the -"Title[]" directove and the H1 section in the PageTop[]. - -B<$if_title_desc> is controlled by the command line option B<--if-descr> -which indirectly controls the contents of B<$if_title> - -(HTML escaped variant: B<$html_if_title_desc>) - -=back - -=head3 Help Functions for Templates - -The following functions exists to facilitate the writing of host and -interface templates. - -=over - -=item B)> - -B takes a string as an argument and returns a new string -where the following substitutions has been done: the chars "<", ">" and -"&" are replaced by "<", ">" and "&" and that newlines embedded -in the string are prepended with "
" and appended with a space character -(newlines at the end of the string are not touched). - -=item B - -This function will try to poll each of the oids specified until -it is successful or has run out of oids. It will return the name of the -first oid that worked or undef if it is not successful - -=back - -=head3 Example Template Files - -=head4 Template Example 1: Eliminating Rejected Targets From Appearing - -This template file generates exactly the same configuration code per -interface as cfgmaker does by default, with the exception that it eliminates -all lines (comments as well as config code) for an interface if the -interface happens to be rejected. - - if(not $problem_lines) - { - $target_lines .= <$html_desc_prefix$html_if_title_desc -- $sysname -
- - - - - - - - - - - - - - - - - - - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - - - ECHO - - $target_lines .= < - - ECHO - } else { - $head_lines=""; - $problem_lines=""; - $target_lines=""; - $separator_lines=""; - } - -=head3 Template Example 2: Simplier Version of Example 1 - -Example 1 was partly intended to demonstrate how to customize the generation -of interface targets but also to provide a hint of how the variables are -used in the "default" template which one could consider that cfgmaker -normally uses. - -If you're only intrested in the easiest way of entirely eliminating those -reject interfaces, the template below would do the job as well by using -B<$default_target_lines>. - - if($if_ok) { - $target_lines = $default_target_lines; - } else { - $head_lines=""; - $problem_lines=""; - $target_lines=""; - $separator_lines=""; - } - -=head3 Template Example 3: Creating CPU Targets for Hosts - -Below is an example of a host template. - - $head_lines .= <$router_name CPU load -
-
System:$sysname in $html_syslocation
Maintainer:$html_syscontact
Description:$html_if_description
ifType:$html_if_type_desc ($if_type_num)
ifName:$html_if_snmp_name
Port Name:$if_port_name
Port Name:$if_pp_port_name
Max Speed:$if_speed_str
Ip:$if_ip ($if_dns_name)
- - - - - - - - - - - - - - - - -
System:$router_name in $html_syslocation
Maintainer:$html_syscontact
Description:$html_sysdescr
Resource:CPU.
-
- ECHO - - -=head1 EXAMPLES - -The first example creates a config file for I: the router -has the community name I. Interfaces get identified by their -IP number. Two global options get added to the config file. The -config file gets redirected to I. The '\' signs at the end -of the line mean that this command should be written on a single line. - - cfgmaker --global "WorkDir: /home/tobi" \ - --global "Options[_]: growright,bits" \ - --ifref=ip \ - public@router.place.xyz > mrtg.cfg - -Note: if cfgmaker is not in your path, but you are in the directory where -cfgmaker is stored, you can start it with ./cfgmaker - -The next example creates a config file for four devices: -I, I, I and -I all with the community I. - -The two routers will have B<--ifref> set to B whilst the two -switches will use B<--ifref> set to B. Further the routers will -use B<--ifdesc> set to B and I will use -B<--ifdesc> set to B whilst I use B instead. - -Finally, there will be two Options lines inserted in the configuration: -One will be in the beginning, whilst the other will be inserted after -the lines related to the two routers but before those lines related -to the switches. - - cfgmaker --global "WorkDir: /home/tobi" \ - --global "Options[_]: growright,bits" \ - --ifref=descr \ - --ifdesc=alias \ - public@router1.place.xyz \ - public@router2.place.xyz \ - --global "Options[_]: growright" \ - --ifref=name \ - --ifdesc=descr \ - public@switch1.place.xyz \ - --ifdesc=name \ - public@switch2.place.xyz > mrtg.cfg - - -The next example demonstrates how to use the B<--community>, -B<--snmp-options> and B<--dns-domain> to make the command line -simpler. All the equipment will use the community I, except for -the ppp-server which use community I. All equipment uses these -SNMP options: B<1s timeout>, B<1 retry> and B (B and -B is unspecified which means they use the default values). -The exception again is the ppp-server which uses B. -Finally, all the equipment is part of the domain I, except -for the ppp-server which is part of the domain I. -Note that the latter is achieved simply by specifying the name -of the ppp-server to be I> . - - cfgmaker --global "WorkDir: /home/tobi" \ - --global "Options[_]: growright,bits" \ - --dns-domain=place.xyz \ - --community=hidden \ - --snmp-options=::1:1::2 \ - router1 \ - router2 \ - router3 \ - router4 \ - router5 \ - switch1 \ - switch2 \ - switch3 \ - switch4 \ - switch5 \ - switch6 \ - switch7 \ - access@ppp-server.remote:::::1 > mrtg.cfg - - -=head1 SEE ALSO - -L - -=head1 AUTHOR - -Tobias Oetiker Etobi@oetiker.chE and -Jakob Ilves Ejakob.ilves@oracle.comE - - -=head1 LICENSE - -GNU General Public License - -=head1 COPYRIGHT - -Cfgmaker is Copyright 2000 by Tobias Oetiker Etobi@oetiker.chE - -=cut diff --git a/bin/indexmaker b/bin/indexmaker index 6d5a303..4c29c99 100755 --- a/bin/indexmaker +++ b/bin/indexmaker @@ -48,7 +48,7 @@ BEGIN { use FindBin; use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}@@lib@@${main::SL}mrtg2"; +use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; use MRTG_lib "2.100016"; use Getopt::Long; diff --git a/bin/indexmaker.lib64 b/bin/indexmaker.lib64 deleted file mode 100755 index 4c29c99..0000000 --- a/bin/indexmaker.lib64 +++ /dev/null @@ -1,859 +0,0 @@ -#! /usr/bin/perl -w -# -*- mode: Perl -*- -################################################################## -# MRTG 2.17.7 --- Index Generator -################################################################## -# -# This reads a mrtg.cfg file form std input or cmdline argument -# and it takes a regexp on the cmdline to specify which -# targets to look at. -# -# from this info it produces a router index on stdout or -# on the filename specified by the --output option -# -################################################################## -# Distributed under the GNU General Public License -# Copyright 2000 by Tobias Oetiker -################################################################## - -$main::GRAPHFMT="png"; - -require 5.005; -use strict; - -# DEBUG TARGETS -# base - basic program flow -#@main::DEBUG=qw(base); - -BEGIN { - # Automatic OS detection ... do NOT touch - if ( $^O =~ /^(?:(ms)?(dos|win(32|nt)?))/i ) { - $main::OS = 'NT'; - $main::SL = '\\'; - $main::PS = ';'; - } elsif ( $^O =~ /^NetWare$/i ) { - $main::OS = 'NW'; - $main::SL = '/'; - $main::PS = ';'; - } elsif ( $^O =~ /^VMS$/i ) { - $main::OS = 'VMS'; - $main::SL = '.'; - $main::PS = ':'; - } else { - $main::OS = 'UNIX'; - $main::SL = '/'; - $main::PS = ':'; - } -} - -use FindBin; -use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; - -use MRTG_lib "2.100016"; -use Getopt::Long; -use Pod::Usage; - -#---------------------------------------------------------------------- -sub decimal_sort($$) { - my $a = $_[0]; - my $b = $_[1]; - - $a =~ s/([0-9]+)/sprintf("%09u",${1})/ge; - $b =~ s/([0-9]+)/sprintf("%09u",${1})/ge; - return $a cmp $b; - } -#---------------------------------------------------------------------- - -my @argv = @ARGV; -my $argz = "$0"; -foreach my $ar (@argv) { - if ($ar =~ /[ |()]/ ) { - $ar = sprintf qq{"%s"}, $ar; - } - $argz .= " $ar"; -} - - -main(); -exit 0; - -sub main { - # default options - my %opt = ( - sort => 'original', - show => 'day', - section => 'h1', - columns => 2, - addhead => '', - bodyopt => 'bgcolor="#ffffff" text="#000000" '. - 'link="#000000" vlink="#000000" alink="#000000"', - title => 'MRTG Index Page', - headlevel => 1, - pagetopend => '', - pagetop => '', - pageend => '', - prefix => '', - rrdviewer => '/cgi-bin/14all.cgi', - optlog => 1, - bold => 1, - boldon => '', - boldoff => '', - div => 'DIV', - imgborder => 1, - cellspacing => 10, - nolegend => 0, - ); - $opt{headon} = ""; - $opt{headoff} = ""; - # load real options - options(\%opt); - - #adapt some defaults to the current options - die "ERROR: --autoprefix requires --output" - if ($opt{autoprefix} and !defined $opt{output}); - $opt{pageend} = $opt{pagetopend} if (defined $opt{pagetopend} and not $opt{pageend}); - $opt{pagetop} = $opt{pagetopend} if (defined $opt{pagetopend} and not $opt{pagetop}); - $opt{boldon} = $opt{boldoff} = "" if (!$opt{bold}); - $opt{picfirst} = (defined $opt{picfirst}?1:0); - if ($opt{compact}) { - $opt{imgborder} = 0; - $opt{cellspacing} = 0; - $opt{headon} = $opt{boldon}; - $opt{headoff} = $opt{boldoff}; - } - if ($opt{sidebyside}) { - $opt{div} = 'td'; - } - - # slurp config files - my %rcfg; - my %cfg; - my @target; - my @routers; - my $cfgfile; - my %files; - while (@ARGV) { - $cfgfile = shift @ARGV; - readcfg($cfgfile,\@routers,\%cfg,\%rcfg); - if (($opt{sectionhost}) or ($opt{perhost})) { - #We need to cache the "hostname" as appeared in cfgfile, - #since it does change in cfgcheck (for ex. if multiple - #overlapping cfgfiles are specified) - for my $targ (@routers) { - if ( !defined $rcfg{host}{$targ} and - !($rcfg{target}{$targ} =~ m/(?{$targ} = $pref - if (! defined $rcfg{prefixes}->{$targ}); - } - } - } - # generate index page - genindex(\@routers, \%cfg, \%rcfg, \%opt, \%files); -} - -sub cleanurl ($) { - my $url = shift; - $url =~ s|([^/.][^/.]*/)\.\./\1|$1|g; - return $url; -} - -#Take a path the mrtg (usually the mrtg output directory) and the overview -#file, find the relative path from the overview to the directory -sub subpath ($$) { - my $sub = shift; - my $out = shift; - my @s=split /$main::SL/,$sub; - my @o=split /$main::SL/,$out; - pop @o; #Last is a filename; - for my $i (0..$#s) { #cut common dirs - if (defined $s[0] and - defined $o[0] and - $s[0] eq $o[0] ) { - shift @s; - shift @o; - } - } - my $ret = join $main::SL,@s; - for my $i (0..$#o) { - $ret = "..$main::SL$ret"; # ".." == "Directory below this one" for - # dos, windows, unix. What about VMS ? - # Is this correct ? HEH - } - $ret .= $main::SL; #Possibly this should be "/" in order not - #to break on platforms !unix, since it will be - #used for generating urls ? - #Don't degenerate in "/" when really no prefix is needed. - $ret = "" if ($ret eq $main::SL); - return $ret; -} - -sub genindex ($$$$) { - my $routers = shift; - my $cfg = shift; - my $rcfg = shift; - my $opt = shift; - my $cfgfile = shift; - my $index; - my $metaCmdLine; - # ----------------------------------------------------------- - # keep only the items our users want (--filter) - # ----------------------------------------------------------- - my @filtered; - ITEM: foreach my $item (@{$routers}) { - foreach my $filter (@{$$opt{filter}}) { - if ($filter =~ /(.+)([=!]~)(.+)/) { - my ($area,$comp,$regex) = ($1,$2,$3); - my $value; - for ($area) { - /^title|pagetop$/ && - do { $value = $$rcfg{$area}{$item}; last }; - - /^name$/ && - do { $value = $item; last }; - - die "ERROR: unknown filter area $_\n"; - }; - for ($comp) { - /^=~$/ && - do { next ITEM unless $value =~ /$regex/; last }; - /^!~$/ && - do { next ITEM unless $value !~ /$regex/; last }; - die "ERROR: unknown comparison operator $_\n"; - }; - } else { - die "ERROR: invalid filter expression $filter\n"; - } - } - push @filtered, $item; - }; - - # ----------------------------------------------------------- - # get items into proper order (--sort) - # ----------------------------------------------------------- - my @order; - for ($$opt{sort}) { - /^original$/ && do {@order = @filtered; last}; - /^name$/ && do { @order = sort { decimal_sort($a,$b); } @filtered; last}; - /^title$/ && do { @order = - sort { decimal_sort($$rcfg{title}{$a}, $$rcfg{title}{$b}) || $a cmp $b } - @filtered; - last; - }; - /^descr(iption)?$/ && - do { - @order = - sort { - $$rcfg{pagetop}{$a} =~ - m[Description:\s*(?:\S+\s+)?(.+?)]i; - my $aval = lc $1; - $$rcfg{pagetop}{$b} =~ - m[Description:\s*(?:\S+\s+)?(.+?)]i; - my $bval = lc $1; - $aval cmp $bval; - } @filtered; - last; - }; - die "ERROR: unknown sort order '$$opt{sort}'\n"; - } - - die "ERROR: did not find any matching data in cfg file\n" unless @order; - - # ----------------------------------------------------------- - # issue page top - # ----------------------------------------------------------- - my $interval =$$cfg{'interval'} ? $$cfg{'interval'} : 5; - my $expiration = &expistr($interval); - my $refresh = $$cfg{'refresh'} ? $$cfg{'refresh'} : 300; - for ($$opt{show}) { - $refresh = /^week$/ && 1800 - || /^month$/ && 7200 - || /^year$/ && 86400 - || $refresh ; - } - - my $gifPath = ''; - - if ($$cfg{icondir} || $$opt{icondir}) { - $gifPath = $$opt{icondir} || $$cfg{icondir}; - #lets make sure there is a trailing path separator - $gifPath =~ s|/*$|/|; - } else { - $gifPath = "$$cfg{imagehtml}"; - } - - -if ($$opt{optlog}) { - $metaCmdLine = $argz; -} else { - $metaCmdLine = ""; -} - -$metaCmdLine =~ s/&/&/g; # Must be first, otherwise it will affect the following changes -$metaCmdLine =~ s/"/"/g; -$metaCmdLine =~ s//>/g; - my $headeradd = $$opt{headeradd} || ""; - $index = < - - - $$opt{title} - - - - - - - - - - $headeradd -ECHO - - $index .= < -/* commandline was: $argz */ -/* sorry, no style, just abusing this to place the commandline and pass validation */ - -ECHO - - $index .= < - - -ECHO - $index .= <$$opt{subtitle}

-ECHO - - $index .= < - -ECHO - - # ----------------------------------------------------------- - # print the graph items - # ----------------------------------------------------------- - my $itemnr = 0; - my $first = $order[0]; - my $host = $$rcfg{host}{$first}; - if ($host){ - $index .= "$$opt{headon}Interfaces of $host $$opt{headoff}" if $$opt{perhost}; - } else { - $index .= "$$opt{headon}Special Items$$opt{headoff}" if $$opt{perhost}; - } - foreach my $item (@order) { - - if ($$opt{perhost}) { - my $newhost = $$rcfg{host}{$item} || 'unspecified host'; - if (!($host eq $newhost)) { - $host = $newhost; - if ($host){ - $index .= "$$opt{headon}Interfaces of $host $$opt{headoff}\n"; - } else { - $index .= "$$opt{headon}Special Items$$opt{headoff}\n"; - } - $index .= "\n"; - $itemnr=0; - } - } - $$opt{prefix} = $$rcfg{prefixes}->{$item} if ($$opt{autoprefix}); - $itemnr++; - $index .= ""; - my $dirrel = "../" x ($$rcfg{'directory_web'}{$item} =~ tr|/|/|); - - # --- produce graph section title --- - my $section; - for ($$opt{section}) { - /^h1$/ && - do{ - if ($$rcfg{pagetop}{$item} =~ m[+]*>(.+?)Description:\s*\Q$section\E\s*([^< ][^<]+?),i - and $section = $1; - last; - }; - /^portname$/ && - do{ - $section = "No Portname for $item"; - $$rcfg{pagetop}{$item} =~ m,Port Name:\s*(.*?),i - and $section = $1; - last; - }; - /^ifname$/ && - do{ - $section = "No Portname for $item"; - $$rcfg{pagetop}{$item} =~ m,ifname:\s*(.*?),i - and $section = $1; - last; - }; - die "ERROR: unknown sectioning type $_\n"; - }; - if (defined $$rcfg{host}{$item} and - !($section =~ m/\b\Q$$rcfg{host}{$item}\E\b/i)) { - $section = ucfirst $$rcfg{host}{$item} . ": $section"; - } - - # --- write the actual graph ---- - die "ERROR: Unknown show type $$opt{show}\n" - unless $$opt{show} =~ /^day|week|month|year|none$/; - - my $image = "$item-$$opt{show}.${main::GRAPHFMT}" - if $$opt{show} ne 'none'; - - $index .= "<$$opt{div}>" if (!$$opt{sidebyside}); - - if (not $image) { - if ($$cfg{logformat} eq 'rrdtool') { - my $sep = $$opt{rrdviewer} =~ /\?/ ? '&' : '?'; - $index .= - "". - "$$opt{boldon}". - "$section$$opt{boldoff}"; - } else { - $index .= "$$opt{boldon}". - "". - "$section$$opt{boldoff}\n<$$opt{div}>"; - } - } else { - #loop used for reversing (text,images) to (images,text) if req. - for my $picfirstloop (1,0) { - if ( $picfirstloop^$$opt{picfirst} ) { - $index .= "$$opt{boldon}$itemnr. $$opt{boldoff}" - if $$opt{enumerate}; - if ($$opt{clicktext}) { - $index .= "$$opt{boldon}"; - $index .= $section; - $index .= "$$opt{boldoff}"; - } else { - $index .= "$$opt{boldon}$section$$opt{boldoff}"; - } - $index .= "\n<$$opt{div}>" if $picfirstloop; - } - - if ( !($picfirstloop^$$opt{picfirst}) ) { - # figure show name for rrd viewer - if ($$cfg{logformat} eq 'rrdtool') { - my $sep = $$opt{rrdviewer} =~ /\?/ ? '&' : '?'; - $index .= "". - "\"$item" - } else { - $index .= ""; - $index .= "\"$item"; - } - $index .= "\n<$$opt{div}>" if $picfirstloop; - } - } - } - - $index .= "" if (!$$opt{sidebyside}); - $index .= "\n"; - - # --- new table column if necessary ---- - if (($itemnr) % $$opt{columns} == 0) { - $index .= "\n\n"; - } - } - # ----------------------------------------------------------- - # print page end - # ----------------------------------------------------------- - - - my $VERSION = "2.17.7"; - $index .= < - - -ECHO - - $index .= < - - - - - - -
MRTGMulti Router Traffic Grapher
- - - - - -
- version $VERSION - Tobias Oetiker - <tobi\@oetiker.ch> - and Dave Rand <dlr\@bungi.com> -
-ECHO - $index .= < - -ECHO - - # ----------------------------------------------------------- - # write out the index page - # ----------------------------------------------------------- - if ($$opt{output}) { - debug ('base', "Writing $$opt{output}"); - open X, ">$$opt{output}" or die "ERROR: creating $$opt{output}: $!\n"; - print X $index; - close X; - } else { - print $index; - } - -} - -sub options ($) { - my $opt = shift; - my @options = ( - 'help|?', - 'man', - 'output=s', - 'filter=s@', - 'addhead=s', - 'title=s', - 'subtitle=s', - 'bodyopt=s', - 'pagetopend=s', - 'pagetop=s', - 'pageend=s', - 'columns=i', - 'perhost!', - 'sort=s', - 'enumerate', - 'width=i', - 'height=i', - 'show=s', - 'section=s', - 'version', - 'prefix=s', - 'headeradd=s', - 'clicktext!', - 'optlog!', - 'compact!', - 'headlevel=i', - 'bold!', - 'picfirst!', - 'sidebyside!', - 'nolegend', - 'autoprefix!', - 'sectionhost!', - 'icondir=s', - 'rrdviewer=s'); - - #generate --option-file from --option - for ( grep /=s$/,@options ) { - my $fileopt = $_; - $fileopt =~ s/=s$/-file=s/; - push @options, $fileopt; - } - - GetOptions( $opt, @options ) or pod2usage(-verbose => 1); - - if ($$opt{prefix}){ - $$opt{prefix} .= '/'; - $$opt{prefix} =~ s|/+$|/|; - } - die ("Indexmaker for mrtg-2.17.7\n") if $$opt{version}; - pod2usage(-exitval => 1, -verbose => 2) if $$opt{man}; - pod2usage(-verbose => 1) if not @ARGV; - - #take care of --fileoption --> --option - for my $fileopt ( grep /-file$/, keys %{$opt} ) { - my $orgopt = $fileopt; - $orgopt =~ s/-file$//; - $$opt{$orgopt} = &readfile($$opt{$fileopt}); - } -} - -#return the contents of a file -sub readfile($) { - my $file = shift; - open F,"<$file" or die "ERROR: can\'t open $file for read, $!"; - my $sl = $/; - $/ = undef; - my $string = ; - $/ = $sl; - close F; - return $string; -} - -__END__ - -=pod - -=head1 NAME - -indexmaker - Creates index files for mrtg web sites (mrtg-2.17.7) - -=head1 SYNOPSIS - -indexmaker [options] mrtg.cfg [other.cfg ...] - -=head1 OPTIONS - - --output=filename set output filename (default: stdout) - - --filter title=~regexp select targets by matching regexp against titles - --filter pagetop=~regexp select targets by matching regexp against pagetop - --filter name=~regexp select targets by matchin regexp against name - - --addhead=text insert this text between and - --title=text set title of generated index file - --subtitle=text add a subtitle to the generated index file - --bodyopt=text set body tag options - --headlevel=number use at top of page (default: 1) - --pagetop=text insert this text between and

...

- --pageend=text insert this text after the main body - --pagetopend=text use this text for pagetop or pageend if undefined - --nolegend do not add the Mrtg legend at the end of the page - - --columns=number show graphs in a table with x columns (default: 2) - --perhost show graphs of the same host on a row - --compact try to make a vertically more compact page - --optlog log the used command line in the page (default: log) - - --sort=title sort graphs by title - --sort=name sort graphs by their name - --sort=descr sort graphs by their description - --sort=original leave as is (default) - - --enumerate add a sequence number to the title of each graph - - --picfirst place pictures before text (default: text first) - --width=number set width of graphs (default: not set) - --height=number - --sidebyside place text / pictures side by side (default: above/below) - --bold use bold text (default: bold) - --clicktext make the text link to the inner page (like the image) - - --show=day pick which graph to show in the index (default) - --show=week - --show=month - --show=year - --show=none - - --section=h1 h1 tag from pagetop as section heading (default) - --section=title title as section headings for graphs - --section=name graph name as section heading - --section=descr graph description as section heading - --section=ifname interface name (ifName) as section heading - --section=portname port name entry in pagetop as section heading - --sectionhost Try to prepend the host to the section heading if missing - - --rrdviewer=path path to rrdviewer (default: /cgi-bin/14all.cgi) - --icondir=path path to icondir - --prefix=path path from the location of the index.html to the graphs - --headeradd=string add string to the html page header - --autoprefix try to set prefix automatically - - ---file=file read string argument for option from file - -=head1 DESCRIPTION - -B can create web pages which display the status of an -array of mrtg interface status pages. - -=over - -=item B<--output> I - -set output filename (default: stdout) - -=item B<--filter> (B|B<pagetop>|B<name>)(B<=~>|B<!~>)I<regexp> - -Several filters may get set. Each filter can match agains the contents -of a specific section of the mrtg config file. B<Name> refers to the -bit in square brackets (option[name]: bla). - -Depending on the match operator chosen (B<=~> or B<!~>) the match will be -positive or negative. - -Note that some shells consider B<!> a special character. It may be -necessary to type B<\!~> instead. - -=item B<--title> I<text> - -Set title of generated index file (default: regexp) - -=item B<--bodyopt> I<text> - -The value of this argument gets appended to -the E<lt>BODYE<gt> tag. This allows you to set document colors. -By default this option is set to - - bgcolor="#ffffff" text="#000000" link="#000000" vlink="#000000" alink="#000000" - -=item B<--columns> I<number> - -Display graphs in a table with I<number> columns (default: 2) - -=item B<--sort> B<title>|B<name>|B<descr>|B<original> - -Sort the graphs in the page either by B<title>, by B<name>, by interface -B<descr>iption, or leave them as is. - -=item B<--enumerate> - -Add a sequence number to the title of each graph - -=item B<--width> I<number> - -Set width of graphs - -=item B<--height> I<number> - -Set the height of the graphs - -=item B<--show> B<day>|B<week>|B<month>|B<year>|B<none> - -Select which graph to show in the index page. You can supress images -completely with B<--show=none>. - -=item B<--section> B<h1>|B<title>|B<name>|B<description>|B<portname> - -Select what to use as the title for each graph in the page. B<h1> is -the H1 section from pagetop, B<title> is the graph title, B<name> is -the bit in square brackets (option[name]: bla), and B<descr> or -B<description> is the text from the Description field of the PageTop -(the Cisco description text if it's available, otherwise just the -interface description). B<portname> is the C<Port Name:> from pagetop. - -=item B<--sectionhost> - -Extract the hostname from the target line (this does not work if the -target is a mathematial expression). Prepend the hostname (and a colon) -to the section if not already present. - -=item B<--rrdviewer> I<path> - -If you have set the B<LogFormat: rrdtool> property in the mrtg.cfg -file, the index will take this into account. The only thing you must -tell it is the path to your grapher cgi. (default: /cgi-bin/14all.cgi) - -=item B<--prefix> I<path> - -By default we assume that the file generated by indexmaker is stored in -I<WorkDir>. If you want to store it somewhere else, specify how to reach -I<WorkDir> from the place where the Index is stored. Note that you have to -use '/' as path separator as this will be used in urls. Speaking of which, -you can even enter a whole url. - -=item B<--autoprefix> I<path> - -Requires --output. -Try to generate the prefix automatically by comparision of the path to the -output file set with --output and the Htmldir set in the configuration files. -Particulary useful when multiple configuration files are specified, with -different Htmldir settings. - -=item B<--optlog> - -Default is logging in the generated page the command line, suppress with ---nooptlog . Useful if the commandline contains a complex --pagetop=string -which could confuse simple browsers. - -=item B<--someoption-file> I<filename> - -For any someoption which takes a I<string> as parameter you can read the -string from a file by adding <-file> to the option keyword. The whole -content of the file will be read and used as the I<string>. The file must -exist. - -=back - -=head1 AUTHOR - -Tobias Oetiker E<lt>tobi@oetiker.chE<gt> - -=head1 LICENSE - -GNU General Public License - -=head1 COPYRIGHT - -2000-2001 Tobias Oetiker E<lt>tobi@oetiker.chE<gt> - -=cut diff --git a/bin/mrtg b/bin/mrtg index c332665..bc9c316 100755 --- a/bin/mrtg +++ b/bin/mrtg @@ -81,7 +81,7 @@ ERR use FindBin; use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}@@lib@@${main::SL}mrtg2"; +use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; use Getopt::Long; use Math::BigFloat; @@ -302,7 +302,7 @@ sub main { if ($cfg{enableipv6} eq 'yes') { if ( eval {local $SIG{__DIE__};require Socket; require Socket6; require IO::Socket::INET6;}) { import Socket; - Socket6->import(qw(inet_pton getaddrinfo)); + import Socket6; debug('base', "IPv6 libraries found, IPv6 enabled."); } else { warn "$NOW: WARNING: IPv6 libraries not found, IPv6 disabled.\n"; diff --git a/bin/mrtg-traffic-sum b/bin/mrtg-traffic-sum index 864f65f..f57e3a9 100755 --- a/bin/mrtg-traffic-sum +++ b/bin/mrtg-traffic-sum @@ -33,7 +33,7 @@ BEGIN { use FindBin; use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}@@lib@@${main::SL}mrtg2"; +use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; use MRTG_lib "2.090017"; use POSIX qw(mktime); diff --git a/bin/mrtg-traffic-sum.lib64 b/bin/mrtg-traffic-sum.lib64 deleted file mode 100755 index f57e3a9..0000000 --- a/bin/mrtg-traffic-sum.lib64 +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/perl -w - -# Changes in revision 1.9 - -# Added an option for --range, which may be "current" for month-to-date or "previous" for last month -# Changed default date range to 00:00:00 on 1st of month to 23:59:59 at end of month -# Changed the default units to MB, more relevant to non-datacentre users :-) -# Added the option for units=GB, to allow for previous behaviour of this script -# Changed default minimum to 0, to report any info that is available by default -# Updated help section with the above changes, fixed a couple of typos - - -use strict; -use Getopt::Long; -use Pod::Usage; - -BEGIN { - # Automatic OS detection ... do NOT touch - if ( $^O =~ /^(?:(ms)?(dos|win(32|nt)?))/i ) { - $main::OS = 'NT'; - $main::SL = '\\'; - $main::PS = ';'; - } elsif ( $^O =~ /^VMS$/i ) { - $main::OS = 'VMS'; - $main::SL = '.'; - $main::PS = ':'; - } else { - $main::OS = 'UNIX'; - $main::SL = '/'; - $main::PS = ':'; - } -} - -use FindBin; -use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; -use MRTG_lib "2.090017"; -use POSIX qw(mktime); - -'$Revision: 1.9 $ ' =~ /Revision: (\S*)/; -my $Revision = $1; - -my $sendmail = "/usr/lib/sendmail"; -$sendmail = "/usr/sbin/sendmail" if -x "/usr/sbin/sendmail"; - -# main loop -my %opt = ( - 'catch' => '(?:description|port name):</td>\s*<td>\s*([^<\s]+[^<]*?)\s*</td', -# 'catch' => 'td>\s*([^<\s]+COLO[^<]*?)\s*</td', - 'min' => 0, - 'range' => "previous", - 'units' => "MB" - ); -sub walklog ($$$$$); -sub sumlog($$$); -sub mailout($$); - -sub main() -{ - # parse options - GetOptions(\%opt, 'min=i','help|h', 'catch=s', 'email=s','version','range=s','units=s') or exit(1); - if($opt{help}) { pod2usage(1) } - if($opt{man}) { pod2usage(-exitstatus => 0, -verbose => 2) } - if($opt{version}) { print "mrtg-traffic-sum $Revision\n"; exit(0) } - my $cfgfile = shift @ARGV; - pod2usage(1) unless $cfgfile and -r $cfgfile; - my %rcfg; - my %cfg; - my @routers; - my @target; - my $report = "Subject: Traffic total for '$cfgfile' ($Revision) "; - readcfg($cfgfile,\@routers,\%cfg,\%rcfg); - cfgcheck(\@routers, \%cfg, \%rcfg, \@target); - walklog \@routers, \%cfg, \%rcfg,\$report,\%opt; - if ($opt{email}) { - mailout \$report,$opt{email}; - } else { - print $report; - } -} - -main; - -sub walklog ($$$$$){ - my $routers = shift; - my $cfg = shift; - my $rcfg = shift; - my $report = shift; - my $opt = shift; - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime(time); - my $start = mktime(0, 0, 0, 1, $mon-1, $year, 0, 0, 0); - my $end = mktime(0, 0, 0, 1, $mon, $year, 0, 0, 0)-1; - if ($opt{range} eq "previous") { - $start = mktime(0, 0, 0, 1, $mon-1, $year, 0, 0, 0); - $end = mktime(0, 0, 0, 1, $mon, $year, 0, 0, 0)-1; - } - if ($opt{range} eq "current") { - $start = mktime(0, 0, 0, 1, $mon, $year, 0, 0, 0); - $end = mktime(0, 0, 0, 1, $mon+1, $year, 0, 0, 0)-1; - } - - my($m,$y) = (localtime($start))[4,5]; - $y += 1900; - $m ++; - $$report .= sprintf "%04d/%02d\n\n", $y,$m; - - $$report .= "Start: ".localtime($start)."\n"; - $$report .= "End: ".localtime($end)."\n"; -if ($opt{'units'} eq "GB") { - $$report .= sprintf "%-40s %15s\n","Interface","In+Out in GB"; -} else { - $$report .= sprintf "%-40s %15s\n","Interface","In+Out in MB"; -} - $$report .= ("-" x 78)."\n"; - foreach my $r (@$routers) { - my $label; - if ($rcfg->{pagetop}{$r} =~ /$opt{catch}/i ) { - $label = $1; - $label .= " $2" if $2; - my ($in,$out) = sumlog $cfg->{logdir}.$rcfg->{directory}{$r}."$r.log",$start,$end; - my $sum = ($in+$out)/(10**6); -if ($opt{'units'} eq "GB") { - $sum = ($in+$out)/(10**9); -} else { - $sum = ($in+$out)/(10**6); -} - next if $sum < $opt{'min'}; - $$report .= sprintf "%-40s %15.0f\n",$label,$sum; - } - } -} - -sub sumlog ($$$) { - my $log = shift; - my $start = shift; - my $end = shift; - my $in = 0.0; - my $out = 0.0; - if (open L, "<",$log){ - my @this=($end+1,0,0); - my @prev; - while (<L>) { - chomp; - @prev = @this; - @this = (split /\s+/)[0,1,2]; - - if ($prev[0] <= $end) { - my $diff = $prev[0] - $this[0]; - $in += $diff * $prev[1]; - $out += $diff * $prev[2]; - } - last if $this[0] <= $start; - } - close L; - } else { - warn "WARN Skipping $log\n"; - } - return $in,$out; -} - -sub mailout ($$) { - my $data = shift; - my $rec = shift; - open S, "|-" or exec $sendmail,'-f',$rec,$rec; - print S <<MESSAGE; -From: $rec -To: $rec -$$data -MESSAGE - close S; - -} -1 - -__END__ - -=head1 NAME - -mrtg-traffic-sum - Builds monthly traffic summary from mrtg log files - -=head1 SYNOPSIS - -B<mrtg-traffic-sum> [I<options>...] B<config-file> - - --man show man-page and exit - -h, --help display this help and exit - --version output version information and exit - --catch=regexp filter out things that match this in PageTop - --email=address email the result - --min=gigabytes minimal number of gigabites required for report - --range=<when> Specify "current" for month-to-date, "previous" for last complete month - --units=[GB|MB] Display results in gigabytes (default is MB) - -=head1 DESCRIPTION - -The mrtg-traffic-sum goes through the mrtg logs for the targets in the -the config file specified and builds the traffic total for the last -month in Giga Bytes. (Note in communications Giga is 10^9). - -The result of this analysis can be sent via email to an address of -your choice using the B<--email> option. - -With the B<--catch> option you can specify a regular expression which -will be matched against the contents of the PageTop settings. The -regular expression should return a value into $1 and possibly into -$2. This will then be used as description for the appropriate traffic -sum. By default the catch is set to: - - (?:description|port name):</td>\s*<td>\s*([^< ]*?[^<]*?)\s*</td - -By default, all traffic which is over one megabyte gets reported. Use the -B<--min> switch to change this number. - -=head1 COPYRIGHT - -Copyright (c) 2002 by Tobias Oetiker. All rights reserved. - -=head1 LICENSE - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -=head1 AUTHOR - -Tobias Oetiker E<lt>tobi@oetiker.chE<gt>, -Keith Dunnett E<lt>keith@dunnett.nameE<gt> - -=head1 HISTORY - - 2002-07-13 to Initial Version - 2009-12-38 kd New features - -=cut - -# Emacs Configuration -# -# Local Variables: -# mode: cperl -# eval: (cperl-set-style "PerlStyle") -# mode: flyspell -# mode: flyspell-prog -# End: -# -# vi: sw=4 et diff --git a/bin/mrtg.lib64 b/bin/mrtg.lib64 deleted file mode 100755 index bc9c316..0000000 --- a/bin/mrtg.lib64 +++ /dev/null @@ -1,2844 +0,0 @@ -#! /usr/bin/perl -w -# -*- mode: cperl -*- - -################################################################### -# MRTG 2.17.7 Multi Router Traffic Grapher -################################################################### -# Created by Tobias Oetiker <tobi@oetiker.ch> -# and Dave Rand <dlr@bungi.com> -# -# For individual Contributers check the CHANGES file -# -################################################################### -# -# Distributed under the GNU General Public License -# -################################################################### -my @STARTARGS=($0,@ARGV); - -@main::DEBUG=qw(); -# DEBUG TARGETS -# cfg - watch the config file reading -# dir - directory mangeling -# base - basic program flow -# tarp - target parser -# snpo - snmp polling -# snpo2 - more snmp debug -# coca - confcache operations -# repo - track confcache repopulation -# fork - forking view -# time - some timing info -# log - logging of data via rateup or rrdtool -# eval - trace eval experssions -# prof - add timeing info some interesting bits of code - -$main::GRAPHFMT="png"; -# There older perls tend to behave peculiar with -# large integers ... -require 5.005; - -use strict; -# addon jpt -BEGIN { - # Automatic OS detection ... do NOT touch - if ( $^O =~ /^(ms)?(dos|win(32|nt)?)/i ) { - $main::OS = 'NT'; - $main::SL = '\\'; - $main::PS = ';'; - } elsif ( $^O =~ /^NetWare$/i ) { - $main::OS = 'NW'; - $main::SL = '/'; - $main::PS = ';'; - } elsif ( $^O =~ /^VMS$/i ) { - $main::OS = 'VMS'; - $main::SL = '.'; - $main::PS = ':'; - } elsif ( $^O =~ /^os2$/i ) { - $main::OS = 'OS2'; - $main::SL = '/'; - $main::PS = ';'; - } else { - $main::OS = 'UNIX'; - $main::SL = '/'; - $main::PS = ':'; - } - if ( $ENV{LANG} and $ENV{LANG} =~ /UTF.*8/i ){ - my $args = join " ", map { /\s/ ? "\"$_\"" : $_ } @ARGV; - $args ||= ""; - print <<ERR; ------------------------------------------------------------------------ -ERROR: Mrtg will most likely not work properly when the environment - variable LANG is set to UTF-8. Please run mrtg in an environment - where this is not the case. Try the following command to start: - - env LANG=C ${0} $args ------------------------------------------------------------------------ -ERR - exit 0; - } -} - - -use FindBin; -use lib "${FindBin::Bin}"; -use lib "${FindBin::Bin}${main::SL}..${main::SL}lib${main::SL}mrtg2"; -use Getopt::Long; -use Math::BigFloat; - -# search for binaries in the bin and bin/../lib directory -use MRTG_lib "2.100016"; - -my $NOW = timestamp; - -my $graphiteObj; - -# $SNMP_Session::suppress_warnings = 2; -use locales_mrtg "0.07"; - -# Do not Flash Console Windows for the forked rateup process -BEGIN { - if ($^O eq 'MSWin32'){ - eval {local $SIG{__DIE__};require Win32; Win32::SetChildShowWindow(0)}; - warn "WARNING: $@\n" if $@; - } -} - -$main::STARTTIME = time; - -%main::verified_rrd = (); - -if ($MRTG_lib::OS eq 'OS2') { -# in daemon mode we will pause 3 seconds to be sure that parent died - require OS2::Process; - if (OS2::Process::my_type() eq 'DETACH') {sleep(3);} -} - -if ($MRTG_lib::OS eq 'UNIX') { - $SIG{INT} = $SIG{TERM} = - sub { unlink ${main::Cleanfile} - if defined $main::Cleanfile; - unlink ${main::Cleanfile2} - if defined $main::Cleanfile2; - unlink ${main::Cleanfile3} - if defined $main::Cleanfile3; - warn "$NOW: ERROR: Bailout after SIG $_[0]\n"; - exit 1; - }; - $SIG{HUP} = sub { - unlink ${main::Cleanfile} - if defined $main::Cleanfile; - unlink ${main::Cleanfile2} - if defined $main::Cleanfile2; - unlink ${main::Cleanfile3} - if defined $main::Cleanfile3; - die "$NOW: ERROR: Bailout after SIG $_[0]\n"; - }; -} - - -END { - local($?, $!); - unlink ${main::Cleanfile} if defined $main::Cleanfile; - unlink ${main::Cleanfile2} if defined $main::Cleanfile2; -} - -&main; - -exit(0); - -#### Functions ################################################ - -sub main { - - - # read in the config file - my @routers; - my %cfg; - my %rcfg; - my %opts; - my $EXITCODE = 0; - - GetOptions(\%opts, 'user=s', 'group=s', 'lock-file=s','confcache-file=s','logging=s', 'check', 'fhs', 'daemon', 'pid-file=s','debug=s', 'log-only') or die "Please use valid Options\n"; - - if (defined $opts{debug}){ - @main::DEBUG = split /\s*,\s*/, $opts{debug}; - if (defined $SNMP_util::Debug){ - $SNMP_util::Debug = 1 if grep /^snpo2$/, @main::DEBUG; - } - } - if (grep /^prof$/, @main::DEBUG){ - require Time::HiRes; - eval "sub gettimeofday() {return Time::HiRes::time()}"; - # note this will crash if the module is missing - # so only use the --debug=prof if you have Time::HiRes installed - } else { - eval "sub gettimeofday() {return time()}"; - } - debug 'time', "prog start ".localtime(time); - - my $uid = $<; - my $gid = $(; - - if (defined $opts{group}) { - $gid = getgrnam($opts{group}); - die "$NOW: ERROR: Unknown Group: $opts{group})\n" if not defined $gid; - } - - if (defined $opts{user}) { - $uid = getpwnam($opts{user}); - die "$NOW: ERROR: Unknown User: $opts{user})\n" if not defined $uid; - } - - # If we've specified using FHS (http://www.pathname.com/fhs/) on the command line, - # use the relevant path definitions (can be overridden later): - - my $confcachefile; - my $pidfile; - my $lockfile; - my $templock; - my $logfile; - - if (defined $opts{"fhs"}) { - $confcachefile = "/var/cache/mrtg/mrtg.ok"; - $pidfile = "/var/run/mrtg.pid"; - $lockfile = "/var/cache/mrtg/mrtg.lck"; - $templock = "/var/cache/mrtg/mrtg.lck.$$"; - $logfile = "/var/log/mrtg.log"; - } - - my $cfgfile = shift @ARGV; - - if ( !defined $cfgfile and -r "/etc/mrtg.cfg" ) { $cfgfile = "/etc/mrtg.cfg"; } - - printusage() unless defined $cfgfile; - - # PID file code, used later if daemonizing... - if ( !defined($pidfile) ) { - $pidfile = $cfgfile; - $pidfile =~ s/\.[^.\/]+$//; - $pidfile .= '.pid'; - } - $pidfile = $opts{"pid-file"} || $pidfile; - - # Run as a daemon, specified on command line (required for FHS compliant daemon) - if (defined $opts{"daemon"}) { - # Create a pidfile, then chown it so we can use it once we change user - &create_pid($pidfile); - chown $uid, $gid, $pidfile; - } - - ($(,$)) = ($gid,$gid) ; - ($<,$>) = ($uid,$uid) ; - die "$NOW: ERROR failed to set UID to $uid\n" unless ($< == $uid and $> == $uid); - - $logfile = $opts{logging} || $logfile; - if (defined $logfile){ - setup_loghandlers $logfile; - warn "Started mrtg with config \'$cfgfile\'\n"; - } - - # lets make sure that there are not two mrtgs running in parallel. - # so we lock on the cfg file. Nothing fancy, just a lockfile - - $lockfile = $opts{"lock-file"} || $lockfile; - - if (! defined $lockfile) { - $lockfile = $cfgfile."_l"; - } - if (! defined $templock) { - $templock = $lockfile."_" . $$ ; - } - - debug('base', "Creating Lockfiles $lockfile,$templock"); - &lockit($lockfile,$templock); - - debug('base', "Reading Config File: $cfgfile"); - my $cfgfile_age = -M $cfgfile; - readcfg($cfgfile,\@routers,\%cfg,\%rcfg); - - imggen($cfg{icondir} || $cfg{imagedir} || $cfg{workdir}); - - # Enable or disable snmpv3 - if(defined $cfg{enablesnmpv3}) { - $cfg{enablesnmpv3} = lc($cfg{enablesnmpv3}); - } else { - $cfg{enablesnmpv3} = 'no'; - } - - if ($cfg{threshmailserver}) { - if (eval {local $SIG{__DIE__};require Net::SMTP;}) { - import Net::SMTP; - debug('base', "Loaded Net::SMTP module for ThreshMail."); - } else { - die "$NOW: WARNING: Can't load Net::SMTP module. This is required for ThreshMail."; - } - } - # Check we have the necessary libraries for IPv6 support - if ($cfg{enablesnmpv3} eq 'yes') { - if (eval {local $SIG{__DIE__};require Net_SNMP_util;}) { - import Net_SNMP_util; - debug('base', "SNMP V3 libraries found, SNMP V3 enabled."); - } else { - warn "$NOW: WARNING: SNMP V3 libraries not found, SNMP V3 disabled.\n"; - $cfg{enablesnmpv3} = 'no'; - require SNMP_util; - import SNMP_util; - } - } - else { # load V1/V2 libraries - require SNMP_util; - import SNMP_util; - } - - - # Enable or disable IPv6 - if(defined $cfg{enableipv6}) { - $cfg{enableipv6} = lc($cfg{enableipv6}); - } else { - $cfg{enableipv6} = 'no'; - } - - # Check we have the necessary libraries for IPv6 support - if ($cfg{enableipv6} eq 'yes') { - if ( eval {local $SIG{__DIE__};require Socket; require Socket6; require IO::Socket::INET6;}) { - import Socket; - import Socket6; - debug('base', "IPv6 libraries found, IPv6 enabled."); - } else { - warn "$NOW: WARNING: IPv6 libraries not found, IPv6 disabled.\n"; - $cfg{enableipv6} = 'no'; - } - } - - # Module invocation for sending a copy of the time series data to graphite. - if(defined $cfg{sendtographite}) { - require Net::Graphite; - } - - # from our last run we kept some info about - # the configuration of our devices around - debug('base', "Reading Interface Config cache"); - $confcachefile = $opts{"confcache-file"} || $confcachefile; - if ( !defined($confcachefile) ) { - $confcachefile = $cfgfile; - $confcachefile =~ s/\.[^.\/]+$//; - $confcachefile .= ".ok"; - } - my $confcache = readconfcache($confcachefile); - - # Check the config and create the target object - debug('base', "Checking Config File"); - my @target; - cfgcheck(\@routers, \%cfg, \%rcfg, \@target, \%opts); - - # exit here if we only check the config file - # in case of an error, cfgcheck() already exited - if (defined $opts{check}) { - debug('base', "Remove Lock Files"); - close LOCK; unlink ($templock, $lockfile); - debug('base', "Exit after successful config file check"); - exit 0; - } - - # postload rrdtool support - if ($cfg{logformat} eq 'rrdtool'){ - debug('base', "Loading RRD support"); - require 'RRDs.pm'; - } - - # set the locale - my $LOC; - if ( $cfg{'language'} and defined($lang2tran::LOCALE{"\L$cfg{'language'}\E"})) { - debug('base', "Loading Locale for ".$cfg{'language'}); - $LOC=$lang2tran::LOCALE{"\L$cfg{'language'}\E"}; - } else { - debug('base', "Loading default Locale"); - $LOC=$lang2tran::LOCALE{'default'}; - } - - # Daemon Code - my $last_time=0; - my $curent_time; - my $sleep_time; - if (defined $opts{"daemon"}) { $cfg{'runasdaemon'} = "yes"; } - &demonize_me($pidfile,$cfgfile) if defined $cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i and $MRTG_lib::OS ne 'VMS' - and not (defined $cfg{'nodetach'} and $cfg{'nodetach'} =~ /y/i); - # auto restart on die if running as demon - - $SIG{__DIE__} = sub { - warn $_[0]; - warn "*** Restarting after 10 seconds in an attempt to recover from the error above\n"; - sleep 10; - exec @STARTARGS; - } if $cfg{'runasdaemon'}; - - debug('base', "Starting main Loop"); - do { # Do this loop once for native mode and forever in daemon mode - my $router; - $NOW = timestamp; # get the time - debug 'time', "loop start ".localtime(time); - - #if we run as daemon, we sleep in between collection cycles - $sleep_time= (int($cfg{interval}*60))-(time-$last_time); - if ($sleep_time > 0 ) { #If greater than 0 the sleep that amount of time - debug('time', "Sleep time $sleep_time seconds"); - sleep ($sleep_time); - } elsif ($last_time > 0) { - warn "$NOW: WARNING: data collection did not complete within interval!\n"; - } - $last_time=time; - - # set meta expires if there is an index file - # 2000/05/03 Bill McGonigle <bill@zettabyte.net> - if (defined $cfg{'writeexpires'}) { - my $exp = &expistr($cfg{'interval'}); - my $fil; - $fil = "$cfg{'htmldir'}index.html" if -e "$cfg{'htmldir'}index.html"; - $fil = "$cfg{'htmldir'}index.htm" if -e "$cfg{'htmldir'}index.htm"; - if (defined $fil) { - open(META, ">$fil.meta"); - print META "Expires: $exp\n"; - close(META); - } - } - - - # Use SNMP to populate the target object - debug('base', "Populate Target object by polling SNMP and". - " external Datasources"); - debug 'time', "snmp read start ".localtime(time); - readtargets($confcache,\@target, \%cfg); - - $NOW = timestamp; # get the time - # collect data for each router or pseudo target (`executable`) - debug 'time', "target loop start ".localtime(time); - foreach $router (@routers) { - debug('base', "Act on Router/Target $router"); - if (defined $rcfg{'setenv'}{$router}) { - my $line = $rcfg{'setenv'}{$router}; - while ( $line =~ s/([^=]+)=\"([^\"]*)\"\s*// ) # " - unconfuse the highliter - { - $ENV{$1}=$2; - } - } - my($savetz) = $ENV{'TZ'}; - if (defined $rcfg{'timezone'}{$router}) { - $ENV{'TZ'} = $rcfg{'timezone'}{$router}; - if ( $main::OS eq 'UNIX' ){ - require 'POSIX.pm'; - POSIX::tzset(); - } - } - - my ($inlast, $outlast, $uptime, $name, $time) = - getcurrent(\@target, $router, \%rcfg, \%cfg); - - if ( defined($inlast) and defined($outlast)) { - $EXITCODE = $EXITCODE | 1; - } - else { - $EXITCODE = $EXITCODE | 2; - } - - debug('base', "Get Current values: in:".( defined $inlast ? $inlast : "undef").", out:". - ( defined $outlast? $outlast : "undef").", up:". - ( defined $uptime ? $uptime : "undef").", name:". - ( defined $name ? $name : "undef").", time:". - ( defined $time ? $time : "undef")); - - #abort, if the router is not responding. - if ($cfg{'logformat'} ne 'rrdtool') { - # undefined values are ok for rrdtool ! - #if ( not defined $inlast or not defined $outlast){ - # warn "$NOW: WARNING: Skipping Update of $router, inlast is not defined\n" - # unless defined $inlast; - # warn "$NOW: WARNING: Skipping Update of $router, outlast is not defined\n" - # unless defined $outlast; - # next; - #} - - if (defined $inlast and $inlast < 0) { - $inlast += 2**31; - # this is likely to be a broken snmp counter ... lets compensate - } - if (defined $outlast and $outlast < 0) { - $outlast += 2**31; - # this is likely to be a broken snmp counter ... lets compensate - } - } - - my ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent,$avmxin, $avmxout, - $cuin, $cuout, $cupercent); - debug('base', "Create Graphics"); - if ($rcfg{'options'}{'dorelpercent'}{$router}) { - ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent, - $cuin, $cuout, $cupercent, $avmxin, $avmxout) = - writegraphics($router, \%cfg, \%rcfg, $inlast, $outlast, $time,$LOC, \%opts); - } else { - ($maxin, $maxout ,$avin, $avout, $cuin, $cuout, $avmxin, $avmxout) = - writegraphics($router, \%cfg, \%rcfg, $inlast, $outlast, $time,$LOC, \%opts); - } - # skip this update if we did not get anything usefull out of - # writegraphics - next if not defined $maxin; - - debug('base', "Check for Thresholds"); - threshcheck(\%cfg,\%rcfg,$cfgfile,$router,$cuin,$cuout); - - if (defined $opts{'log-only'}){ - debug('base', "Disable Graph and HTML generation"); - } - - if ($cfg{logformat} eq 'rateup' and not defined $opts{'log-only'} ){ - debug('base', "Check for Write HTML Pages"); - writehtml($router, \%cfg, \%rcfg, - $maxin, $maxout, $maxpercent, $avin, $avout, $avmxin, $avmxout, $avpercent, - $cuin, $cuout, $cupercent, $uptime, $name, $LOC) - } - - # - clonedirectory($router,\%cfg, \%rcfg); - # - - #put TZ things back in shape ... - if ($savetz) { - $ENV{'TZ'} = $savetz; - } else { - delete $ENV{'TZ'}; - } - if ( $main::OS eq 'UNIX' ){ - require 'POSIX.pm'; - POSIX::tzset(); - }; - } - # Has the cfg file been modified since we started? if so, reload it. - if ( -M $cfgfile < $cfgfile_age and - $cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i ) { - # reload the configuration - $cfgfile_age = -M $cfgfile; - debug('base', "Re-reading Config File: $cfgfile"); - @routers = (); %cfg = (); %rcfg = (); - readcfg($cfgfile,\@routers,\%cfg,\%rcfg); - cfgcheck(\@routers, \%cfg, \%rcfg, \@target, \%opts); - } - debug('base', "End of main Loop"); - } while ($cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i ); #In daemon mode run forever - debug('base', "Exit main Loop"); - # OK we are done, remove the lock files ... - - debug('base', "Remove Lock Files"); - close LOCK; unlink ($templock, $lockfile); - - debug('base', "Store Interface Config Cache"); - delete $$confcache{___updated} if exists $$confcache{___updated}; # make sure everything gets written out not only the updated entries - writeconfcache($confcache,$confcachefile); - - if ( ! $cfg{'runasdaemon'} or $cfg{'runasdaemon'} !~ /y/i ) { - if ( ($EXITCODE & 1) and ($EXITCODE & 2) ) { - # At least one target was sucessful - exit 91; - } - elsif ( not ($EXITCODE & 1) and ($EXITCODE & 2) ) { - # All targets failed - exit 92; - } - } -} - -# ( $inlast, $outlast, $uptime, $name, $time ) = -# &getcurrent( $target, $rou, $rcfg, $cfg ) -# Calculate monitored data for device $rou based on information in @$target -# and referring to configuration data in %$rcfg and %$cfg. In the returned -# list, $inlast and $outlast are the input and output monitored data values, -# $uptime is the device uptime, $name is the device name, and $time is the -# current time when the calculation was performed. -sub getcurrent { - my( $target, $rou, $rcfg, $cfg ) = @_; - # Hash indexed by $mode for conveniently saving $inlast and $outlast - my %last; - # Initialize uptime, device name, and data collection time to empty strings - my $uptime = ''; - my $name = ''; - my $time = ''; - - # Calculate input and output monitored data - foreach my $mode( qw( _IN_ _OUT_ ) ) { - # Initialize monitored data, warning message, and death message - # to empty strings - my $data; - my $warning; - my $death; - { - # Code block used to calculate monitoring data - # Localize warning and death exception handlers to capture - # error message less any leading and trailing white space - local $SIG{ __WARN__ } = - sub { $_[0] =~ /^\s*(.+?)\s*$/; $warning = $1; }; - local $SIG{ __DIE__ } = - sub { $_[0] =~ /^\s*(.+?)\s*$/; $death = $1; }; - # Calculate monitoring data. $rcfg->{ target }{ $rou } contains - # a Perl expression for the calculation. - $data = eval "$rcfg->{target}{$rou}"; - } - # Test for various exceptions occurring in the calculation - if( $warning ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (warn): $warning\n"; - $data = undef; - } elsif( $death ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (kill): $death\n"; - $data = undef; - } elsif( $@ ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (eval): $@\n"; - $data = undef; - } elsif( not defined $data ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' did not eval into defined data\n"; - $data = undef; - } elsif( $data and $data !~ /^[-+]?\d+(\.\d*)?([eE][+-]?[0-9]+)?$/ ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' evaluated to '$data' instead of a number\n"; - $data = undef; - } elsif( length( $data ) > 190 ) { - warn "$NOW: ERROR: $mode value: '$data' is way to long ...\n"; - $data = undef; - } else { - # At this point data is considered valid. Round to an integer - # unless RRDTool is in use and this is a gauge - if (not ( $cfg->{ logformat } eq 'rrdtool' - and defined $rcfg->{ options }{ gauge }{ $rou })){ - if (ref $data and ref $data eq 'Math::BigFloat') { - $data->ffround( 0 ) - } else { - $data = sprintf "%.0f", $data; - } - } - # Remove any leading plus sign - $data =~ s/^\+//; - } - $last{ $mode } = $data; - } - - # Set $u to the unique index of the @$target array referred to in the - # monitored data calculation for the current device. $u will be set to - # -1 if that index is not unique. - my $u = $rcfg->{ uniqueTarget }{ $rou }; - - # Get the uptime, device name, and data collection time from the @$target - # array if the monitored data calculation refers only to one target. - # Otherwise it doesn't make sense to do this. - if( $u >= 0 ) { - $uptime = $target->[ $u ]{ _UPTIME_ }; - $name = $target->[ $u ]{ _NAME_ }; - $time = $target->[ $u ]{ _TIME_ }; - - if ($time =~ /^([-0-9.]+)$/) { - $time = $1; - } - - } - - # Set the time to the current time if it was not set above - $time = time unless $time; - - # Cache uptime location for reading name - my( $uploc ); - - # Get the uptime and device name from the alternate location (community@host or - # (OID:community@host or OID) that may have been specified with the RouterUptime - # target keyword - if( defined $rcfg->{ routeruptime }{ $rou } ) { - my( $noid, $nloc ) = split( /:/, $rcfg->{ routeruptime }{ $rou }, 2 ); - # If only location (community@host) was specified then - # move the location details into the right place - if( $noid =~ /@/ ) { - $nloc = $noid; - $noid = undef; - } - # If no OID (community@host) was specified use the hardcoded default - if( not $noid ) { - $noid = 'sysUptime'; - } - # If no location (community@host) was specified use values from the - # unique target referred to in the monitored data calculation - if( not $nloc ){ - if ($u >= 0) { - my $comm = $target->[ $u ]{ Community }; - my $host = $target->[ $u ]{ Host }; - my $opt = $target->[ $u ]{ SnmpOpt }; - $nloc = "$comm\@$host$opt"; - } else { - die "$NOW: ERROR: You must specify the location part of the RouterUptime oid for non unique targets! ($rou)\n"; - } - } - - $uploc = $nloc; - # Get the device uptime if $noid(OID) and $nloc (community@host) have been specified - # one way or the other - debug('base', "Fetching sysUptime and sysName from: $noid:$nloc"); - ( $uptime, $name ) = snmpget( $uploc, $rcfg->{ snmpoptions }{ $rou }, $noid, 'sysName'); - } - - # Get the device name from the alternate location (OID or - # OID:community@host) that may have been specified with the RouterName - # target keyword - if( defined $rcfg->{ routername }{ $rou } ) { - my( $noid, $nloc ) = split( /:/, $rcfg->{ routername }{ $rou }, 2 ); - # If no location (community@host) was specified use values from the - # unique target referred to in the monitored data calculation - if( $u >= 0 and not $nloc ) { - my $comm = $target->[ $u ]{ Community }; - my $host = $target->[ $u ]{ Host }; - my $opt = $target->[ $u ]{ SnmpOpt }; - $nloc = "$comm\@$host$opt"; - } - # Get the location from the RouterUptime keyword if that is defined - # and $nloc has not otherwise been specified - $nloc = $uploc if $uploc and not $nloc; - # Get the device name if $nloc (community@host) has been specified - # one way or the other - debug('base', "Fetching sysName from: $noid:$nloc"); - ( $name ) = snmpget( $nloc, $$rcfg{snmpoptions}{ $rou }, $noid ) if $nloc; - } - - return ( $last{ _IN_ }, $last{ _OUT_ }, $uptime, $name, $time ); -} - -sub rateupcheck ($) { - my $router = shift; - if ($?) { - my $value = $?; - my $signal = $? & 127; #ignore the most significant bit - #as it is always one when it is a returning - #child says dave ... - if (($MRTG_lib::OS ne 'UNIX') || ($signal != 127)) { - my $exitval = $? >> 8; - warn "$NOW: WARNING: rateup died from Signal $signal\n". - " with Exit Value $exitval when doing router '$router'\n". - " Signal was $signal, Returncode was $exitval\n" - } - } -} - -sub clonedirectory { - my($router,$cfg, $rcfg) = @_; - require File::Copy; - import File::Copy; - - return unless ( $$rcfg{'clonedirectory'}{$router} ); - - my ($clonedstdir, $clonedsttarget ,$srcname, $dstname); - - ($clonedstdir, $clonedsttarget ) = split (/,|\s+/, $$rcfg{'clonedirectory'}{$router}) if ( $$rcfg{'clonedirectory'}{$router} =~ /,|\S\s+\S/ ); - - if ( defined $clonedsttarget ) { - $clonedsttarget =~ s/\s+//; - $clonedsttarget = lc($clonedsttarget); - } else { - $clonedstdir = $$rcfg{'clonedirectory'}{$router}; - } - if ( $$rcfg{'directory'}{$router} ne $clonedstdir) { - $clonedstdir =~ s/\s+$//; - $clonedstdir .= "/" unless ($clonedstdir =~ /\/$/); - my $fullpathsrcdir = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}"; - my $fullpathdstdir = "$$cfg{'logdir'}$clonedstdir"; - - die "$NOW: ERROR: Destination dir: $fullpathdstdir not found for cloning process\n" unless ( -e $fullpathdstdir ); - die "$NOW: ERROR: Destination dir: $fullpathdstdir is not a directory destination for cloning process\n" unless ( -d $fullpathdstdir ); - die "$NOW: ERROR: Destination dir: $fullpathdstdir is not writeable for cloning process\n" unless ( -w $fullpathdstdir ); - - if ( defined $clonedsttarget ) { - debug('base', "Clone directory $fullpathsrcdir to $fullpathdstdir " . - "renaming target $router to $clonedsttarget"); - } else { - debug('base', "Clone directory $fullpathsrcdir to $fullpathdstdir"); - } - - foreach my $srcfile (<$fullpathsrcdir$router\[.-\]*>) { - debug('base', "copying $srcfile $fullpathdstdir"); - copy("$srcfile","$fullpathdstdir") or warn "$NOW: WARNING: Cloning $srcfile to $fullpathdstdir unsuccessful; $!\n"; - if ($srcfile =~ /\.html/i) { - debug('base', "altering $fullpathdstdir/$router.$$rcfg{'extension'}{$router}"); - # - my $dirrel = "../" x ($$rcfg{'clonedirectory'}{$router} =~ tr|/|/|); - # - debug('base', "dirrel $dirrel $clonedstdir"); - open(HTML,"$fullpathdstdir/$router.$$rcfg{'extension'}{$router}"); - my @CLONEHTML = <HTML>; - close(HTML); - foreach ( @CLONEHTML ) { - if ( defined $clonedsttarget and /$router/ ) { - debug('base', "altering $router to $clonedsttarget in html file"); - s/$router/$clonedsttarget/i; - } - if ( /SRC=/i and /$$rcfg{'directory'}{$router}/ ) { - debug('base', "altering from $_"); - s|(\.\./)+|$dirrel|; - s|$$rcfg{'directory'}{$router}|$clonedstdir|; - debug('base', "altering to $_"); - } - } - open(HTML,">$fullpathdstdir/$router.$$rcfg{'extension'}{$router}"); - print HTML $_ for ( @CLONEHTML ); - close(HTML); - } - if ( defined $clonedsttarget ) { - $srcfile =~ /.+\/(.+)?$/; - $srcname = $1; - $dstname = $srcname; - $dstname =~ s/$router/$clonedsttarget/; - debug('base', "Clone renaming $srcname to $dstname at $fullpathdstdir"); - rename("$fullpathdstdir/$srcname","$fullpathdstdir/$dstname") or - warn "$NOW: WARNING: Renaming $fullpathdstdir/$srcname to $fullpathdstdir/$dstname unsuccessful; $!\n"; - } - } - } else { - warn "$NOW: WARNING: Cloning to the same place suspended. ; $!\n"; - } -} - -sub writegraphics { - my($router, $cfg, $rcfg, $inlast, $outlast, $time,$LOC, $opts) = @_; - - my($absmax,$maxv, $maxvi, $maxvo, $i, $period, $res); - my(@exec, @mxvls, @metas); - my(%maxin, %maxout, %maxpercent, %avin, %avout, %avmxin, %avmxout, %avpercent, %cuin, %cuout, %cupercent); - my($rrdinfo); - - @metas = (); - $maxvi = $$rcfg{'maxbytes1'}{$router}; - $maxvo = $$rcfg{'maxbytes2'}{$router}; - if ($maxvi > $maxvo) { - $maxv = $maxvi; - } else { - $maxv = $maxvo; - } - $absmax = $$rcfg{'absmax'}{$router}; - $absmax = $maxv unless defined $absmax; - if ($absmax < $maxv) { - die "$NOW: ERROR: AbsMax: $absmax is smaller than MaxBytes: $maxv\n"; - } - - - # select whether the datasource gives relative or absolute return values. - my $up_abs="u"; - $up_abs='m' if defined $$rcfg{'options'}{'perminute'}{$router}; - $up_abs='h' if defined $$rcfg{'options'}{'perhour'}{$router}; - $up_abs='d' if defined $$rcfg{'options'}{'derive'}{$router}; - $up_abs='a' if defined $$rcfg{'options'}{'absolute'}{$router}; - $up_abs='g' if defined $$rcfg{'options'}{'gauge'}{$router}; - - my $dotrrd = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.rrd"; - my $dotlog = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.log"; - my $reallog = $$cfg{logformat} eq 'rrdtool' ? $dotrrd : $dotlog; - - if (defined $$cfg{maxage} and -e $reallog and time()-$$cfg{maxage} > (stat($reallog))[9]){ - warn "$NOW: ERROR: skipping update of $router. As $reallog is older than MaxAge ($$cfg{maxage} s)\n"; - return undef; - } - - if ($$cfg{logformat} eq 'rrdtool') { - debug('base',"start RRDtool section"); - # make sure we got some sane default here - my %dstype = qw/u COUNTER a ABSOLUTE g GAUGE h COUNTER m COUNTER d DERIVE/; - $up_abs = $dstype{$up_abs}; - # update the database. - - # set minimum/maximum values. use 'U' if we cannot get good values - # the lower bound is hardcoded to 0 - my $absi = $maxvi; - my $abso = $maxvo; - $absi = $abso = $$rcfg{'absmax'}{$router} - if defined $$rcfg{'absmax'}{$router}; - debug('base',"maxi:$absi, maxo:$abso"); - $absi = 'U' if $absi == 0; - $abso = 'U' if $abso == 0; - # check to see if we already have an RRD file or have to create it - # maybe we can convert an .log file to the new rrd format - if( $RRDs::VERSION >= 1.4 and $$cfg{rrdcached} and $$cfg{rrdcached} !~ /^unix:/ ) { - # rrdcached in network mode. No log conversion possible. - # In this mode, we cannot use absolute paths. So, we strip logdir from the name. - $dotrrd = "$$rcfg{'directory'}{$router}$router.rrd"; - if( $RRDs::VERSION < 1.49 ) { - # This version of RRD doesnt support info, create and tune - debug('base',"Unable to verify RRD file with this version of rrdcached"); - if( !$main::verified_rrd{$dotrrd} ) { - warn "WARN: Unable to verify $dotrrd with this version of RRDTool\n"; - $main::verified_rrd{$dotrrd} = 1; - } - } elsif( !$main::verified_rrd{$dotrrd} ) { - # Test to see if it exists - debug('base',"Attempting to verify RRD file via rrdcached"); - $rrdinfo = RRDs::info($dotrrd,'--daemon',$$cfg{rrdcached},'--noflush'); - if(!$rrdinfo) { # doesnt exist, or cannot be accessed - my $e = RRDs::error(); - warn "$NOW: Cannot access $dotrrd; will attempt to (re)create it: $e\n" if $e; - - # don't fail if interval is not set - my $interval = $$cfg{interval}; - my $minhb = int($$cfg{interval} * 60)*2; - $minhb = 600 if ($minhb <600); - my $rows = $$rcfg{'rrdrowcount'}{$router} || int( 4000 / $interval); - my $rows30m = $$rcfg{'rrdrowcount30m'}{$router} || 800; - my $rows2h = $$rcfg{'rrdrowcount2h'}{$router} || 800; - my $rows1d = $$rcfg{'rrdrowcount1d'}{$router} || 800; - my @args = ($dotrrd, '-b', $time-10, '-s', int($interval * 60), - "DS:ds0:$up_abs:$minhb:0:$absi", - "DS:ds1:$up_abs:$minhb:0:$abso", - "RRA:AVERAGE:0.5:1:$rows", - ( $interval < 30 ? ("RRA:AVERAGE:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:AVERAGE:0.5:".int(120/$interval).":".$rows2h, - "RRA:AVERAGE:0.5:".int(1440/$interval).":".$rows1d, - "RRA:MAX:0.5:1:$rows", - ( $interval < 30 ? ("RRA:MAX:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:MAX:0.5:".int(120/$interval).":".$rows2h, - "RRA:MAX:0.5:".int(1440/$interval).":".$rows1d); - # do we have holt winters rras defined here ? - if (defined $$rcfg{'rrdhwrras'} and defined $$rcfg{'rrdhwrras'}{$router}){ - push @args, split(/\s+/, $$rcfg{'rrdhwrras'}{$router}); - } - push @args,"--daemon", $$cfg{rrdcached}; - - debug('base',"create $dotrrd via rrdcached"); - debug('log', "RRDs::create(".join(',',@args).")"); - RRDs::create(@args); - $e = RRDs::error(); - die "$NOW: ERROR: Cannot create RRD ".join(',',@args)."- $e\n" if $e; - } else { - # Does the RRD file need to be tuned? - if( - ($rrdinfo->{"ds[ds0].max"} != $absi) - ||($rrdinfo->{"ds[ds1].max"} != $abso) - ||($rrdinfo->{"ds[ds0].type"} ne $up_abs) - ||($rrdinfo->{"ds[ds1].type"} ne $up_abs) - ) { - debug('base',"RRD file needs to be tuned"); - warn "$NOW: RRDFile $dotrrd needs to be tuned but cannot do this remotely.\n"; - } - } - $main::verified_rrd{$dotrrd} = 1; - } else { - debug('base',"No need to verify this file again"); - } - } elsif (-e $dotlog and not -e $dotrrd) { - debug('base',"converting $dotlog to RRD format"); - if(defined $RRDs::VERSION and $RRDs::VERSION < 1.000271){ - die "$NOW: ERROR: RRDtool version 1.0.27 or later required to perform log2rrd conversion\n"; - } - log2rrd($router,$cfg,$rcfg); - } elsif (! -e $dotrrd) { - #nope it seems we have to create a new one - debug('base',"create $dotrrd"); - # create the rrd if it doesn't exist - # don't fail if interval is not set - my $interval = $$cfg{interval}; - my $minhb = int($$cfg{interval} * 60)*2; - $minhb = 600 if ($minhb <600); - my $rows = $$rcfg{'rrdrowcount'}{$router} || int( 4000 / $interval); - my $rows30m = $$rcfg{'rrdrowcount30m'}{$router} || 800; - my $rows2h = $$rcfg{'rrdrowcount2h'}{$router} || 800; - my $rows1d = $$rcfg{'rrdrowcount1d'}{$router} || 800; - my @args = ($dotrrd, '-b', $time-10, '-s', int($interval * 60), - "DS:ds0:$up_abs:$minhb:0:$absi", - "DS:ds1:$up_abs:$minhb:0:$abso", - "RRA:AVERAGE:0.5:1:$rows", - ( $interval < 30 ? ("RRA:AVERAGE:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:AVERAGE:0.5:".int(120/$interval).":".$rows2h, - "RRA:AVERAGE:0.5:".int(1440/$interval).":".$rows1d, - "RRA:MAX:0.5:1:$rows", - ( $interval < 30 ? ("RRA:MAX:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:MAX:0.5:".int(120/$interval).":".$rows2h, - "RRA:MAX:0.5:".int(1440/$interval).":".$rows1d); - # do we have holt winters rras defined here ? - if (defined $$rcfg{'rrdhwrras'} and defined $$rcfg{'rrdhwrras'}{$router}){ - push @args, split(/\s+/, $$rcfg{'rrdhwrras'}{$router}); - } - - debug('log', "RRDs::create(".join(',',@args).")"); - RRDs::create(@args); - my $e = RRDs::error(); - die "$NOW: ERROR: Cannot create RRD ".join(',',@args)."- $e\n" if $e; - } elsif ( -M $dotrrd > 0 ) { - # update the minimum/maximum according to maxbytes/absmax - # and (re)set the data-source-type to reflect cfg changes - # cost: 1 read/write cycle, but update will reuse the buffered data - # in daemon mode this will only happen in the first round - my @args = ($dotrrd, '-a', "ds0:$absi", '-a', "ds1:$abso", - '-d', "ds0:$up_abs", '-d', "ds1:$up_abs"); - debug('log', "RRDs::tune(@args)"); - my $start = gettimeofday(); - RRDs::tune(@args); - debug('prof',sprintf("RRDs::tune $dotrrd - %.3fs",gettimeofday()-$start)); - my $e = RRDs::error(); - warn "$NOW: ERROR: Cannot tune logfile: $e\n" if $e; - } - # update the rrd - $inlast = 'U' unless defined $inlast and $inlast =~ /\S/ and $inlast ne '##UNDEF##'; - $outlast = 'U' unless defined $outlast and $outlast =~ /\S/ and $outlast ne '##UNDEF##'; - debug('log', "RRDs::update($dotrrd, '$time:$inlast:$outlast')"); - my $start = gettimeofday(); - my $rrddata = 0; - if ( $RRDs::VERSION >= 1.4 and $$cfg{rrdcached} ){ - RRDs::update($dotrrd, '--daemon', $$cfg{rrdcached}, "$time:$inlast:$outlast"); - debug('prof',sprintf("RRDs::update $dotrrd (rrdcached) - %.3fs",gettimeofday()-$start)); - $rrddata = \{ dummy => "" }; - } elsif ( $RRDs::VERSION >= 1.2 ){ - $rrddata=RRDs::updatev($dotrrd, "$time:$inlast:$outlast"); - debug('prof',sprintf("RRDs::updatev $dotrrd - %.3fs",gettimeofday()-$start)); - } else { - RRDs::update($dotrrd, "$time:$inlast:$outlast"); - debug('prof',sprintf("RRDs::update $dotrrd - %.3fs",gettimeofday()-$start)); - } - my $e = RRDs::error(); - warn "$NOW: ERROR: Cannot update $dotrrd with '$time:$inlast:$outlast' $e\n" if ($e); - - - # Send a copy of the time series data to graphite - if(defined $$cfg{sendtographite}) { - my @a = split ",",$$cfg{sendtographite}; - if (not $graphiteObj) { - $graphiteObj = Net::Graphite->new( - host => $a[0], - port => $a[1], - trace => 0, - proto => "tcp", - timeout => 1, - fire_and_forget => 1, - return_connect_error => 0 - ); - if($graphiteObj->connect) { - debug('log',"successfully opened graphite socket ($graphiteObj)\n"); - } else { - debug('log',"graphite connect error: $!\n"); - } - } - - # make a copy of the router name for use in the graphite namespace - my $graphiterouter = $router; - # periods are delimiters in the graphite namespace so change them to underscores - $graphiterouter =~ s/_/\./g; - # commas are not allowed - $graphiterouter =~ s/,//g; #remove commas - #set up MRTG's 'in' var naming for graphite - my $graphitein; - if ($$rcfg{'legendi'}{$router}) { - $graphitein = $$rcfg{'legendi'}{$router}; - # if the 'in' var contains an underscore, change to a period so graphite will delimit on it - $graphitein =~ s/_/\./g; - } else { - # if legendi is not specified, just use 'in' - $graphitein = "in"; - } - - #set up MRTG's 'out' var naming for graphite - my $graphiteout; - if ($$rcfg{'legendo'}{$router}) { - $graphiteout = $$rcfg{'legendo'}{$router}; - # if the 'out' var contains an underscore, change to a period so graphite will delimit on it - $graphiteout =~ s/_/\./g; - } else { - # if legendo is not specified, just use 'out' - $graphiteout = "out"; - } - - my $graphitensprefix="m2g"; - - # verify that the 'in' value is numeric (ints, floats, +, -) , and send to graphite - if($inlast =~ /^[+-]?([0-9]*[.])?[0-9]+$/) { - # send the 'in' var data to graphite, and log it appropriately - debug('log', "graphite->send($graphitensprefix.$graphiterouter.$graphitein,$inlast,$time)\n"); - $graphiteObj->send( - path => "$graphitensprefix.$graphiterouter.$graphitein", - value => "$inlast", - time => $time, - ); - } else { - debug('log', "WARNING - skipped graphite->send($graphitensprefix.$graphiterouter.$graphitein,$inlast,$time) because value ($inlast) is non-numeric\n"); - } - - # verify that the 'out' value is numeric (ints, floats, +, -) , and send to graphite - if($outlast =~ /^[+-]?([0-9]*[.])?[0-9]+$/) { - # send the 'out' var data to graphite, and log it appropriately - debug('log', "graphite->send($graphitensprefix.$graphiterouter.$graphiteout,$outlast,$time)\n"); - $graphiteObj->send( - path => "$graphitensprefix.$graphiterouter.$graphiteout", - value => "$outlast", - time => $time, - ); - } else { - debug('log', "WARNING - skipped graphite->send($graphitensprefix.$graphiterouter.$graphiteout,$outlast,$time) because value ($outlast) is non-numeric\n"); - } - } - # done sending to graphite for this iteration - - - - if ( $RRDs::VERSION < 1.2 ){ - # get the rrdtool-processed values back from rrdtool - # for the threshold checks (we cannot use the fetched data) - $start = gettimeofday(); - my $info = RRDs::info($dotrrd); - debug('prof',sprintf("RRDs::info $dotrrd - %.3fs",gettimeofday()-$start)); - my $lasttime = $info->{last_update} - $info->{last_update} % $info->{step}; - debug('log', "RRDs::info($dotrrd)"); - $e = RRDs::error(); - warn "$NOW: ERROR: Cannot 'info' $dotrrd: $e\n" if ($e); - $start = gettimeofday(); - my $fetch = (RRDs::fetch($dotrrd,'AVERAGE','-s',$lasttime-1,'-e',$lasttime))[3]; - debug('prof',sprintf("RRDs::fetch $dotrrd - %.3fs",gettimeofday()-$start)); - $e = RRDs::error(); - warn "$NOW: ERROR: Cannot 'fetch' $dotrrd: $e\n" if ($e); - debug('log', "RRDs::fetch($dotrrd,'AVERAGE','-s',$lasttime,'-e',$lasttime)"); - $cuin{d}{$router} = $fetch->[0][0]; - $cuout{d}{$router} = $fetch->[0][1]; - } elsif ( $RRDs::VERSION >= 1.4 and $$cfg{rrdcached} ){ - # Cannot check thresholds - } elsif($rrddata) { - my $utime = $time - ($time % int($cfg->{interval}*60)); - $cuin{d}{$router} = $rrddata->{"[$utime]RRA[AVERAGE][1]DS[ds0]"}; - $cuout{d}{$router} = $rrddata->{"[$utime]RRA[AVERAGE][1]DS[ds1]"}; - $cuin{d_hwfail}{$router} = $rrddata->{"[$utime]RRA[FAILURES][1]DS[ds0]"}; - $cuout{d_hwfail}{$router} = $rrddata->{"[$utime]RRA[FAILURES][1]DS[ds1]"}; - } - my $in = defined $cuin{d}{$router} ? $cuin{d}{$router} : "???" ; - my $out = defined $cuout{d}{$router} ? $cuout{d}{$router} : "???" ; - debug('log', " got: $in/$out"); - # the html pages and the graphics are created at "call time" so that's it! - # (the returned hashes are empty, it's just to minimize the changes to mrtg) - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - return (\%maxin, \%maxout, \%maxpercent, \%avin, \%avout, \%avpercent, \%cuin, \%cuout, \%cupercent, \%avmxin, \%avmxout); - } - return (\%maxin, \%maxout, \%avin, \%avout, \%cuin, \%cuout, \%avmxin, \%avmxout ); - - } - ########## rrdtool users have left here ############### - - ((($MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2') and (-e "${FindBin::Bin}${MRTG_lib::SL}rateup.exe")) or - (($MRTG_lib::OS eq 'NW') and (-e "SYS:/Mrtg/bin/rateup.nlm")) or - (-x "${FindBin::Bin}${MRTG_lib::SL}rateup")) or - die "$NOW: ERROR: Can't Execute '${FindBin::Bin}${MRTG_lib::SL}rateup'\n"; - - # rateup does not know about undef so we make inlast and outlast ready for rateup - #warn "$NOW: ERROR: inlast is undefined. Skipping $router\n" unless defined $inlast; - #warn "$NOW: ERROR: outlast is undefined. Skipping $router\n" unless defined $outlast; - #return undef unless defined $inlast and defined $outlast; - - - # set values to -1 to tell rateup about unknown values - $inlast = -1 unless defined $inlast; - $outlast = -1 unless defined $outlast; - - # untaint in and out - if ($inlast =~ /^([-0-9.]+)$/) { - $inlast = $1; - } - if ($outlast =~ /^([-0-9.]+)$/) { - $outlast = $1; - } - - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", - "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router", - $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z', - "$up_abs"."p", $inlast, $outlast, $absmax, - "C", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router}, - $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router}, - $$rcfg{'rgb5'}{$router}); - } else { - - @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", - "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router", - $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z', - "$up_abs", $inlast, $outlast, $absmax, - "c", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router}, - $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router}); - } - - # If this list grows anymore would it be more efficient to have an - # array to look up the command line option to send to rateup rather - # than have a long list to check? - push (@exec, '-t') if defined $$rcfg{'options'}{'transparent'}{$router}; - push (@exec, '-0') if defined $$rcfg{'options'}{'withzeroes'}{$router}; - push (@exec, '-b') if defined $$rcfg{'options'}{'noborder'}{$router}; - push (@exec, '-a') if defined $$rcfg{'options'}{'noarrow'}{$router}; - push (@exec, '-i') if defined $$rcfg{'options'}{'noi'}{$router}; - push (@exec, '-o') if defined $$rcfg{'options'}{'noo'}{$router}; - push (@exec, '-l') if defined $$rcfg{'options'}{'logscale'}{$router}; - push (@exec, '-x') if defined $$rcfg{'options'}{'expscale'}{$router}; - push (@exec, '-m') if defined $$rcfg{'options'}{'secondmean'}{$router}; - push (@exec, '-p') if defined $$rcfg{'options'}{'printrouter'}{$router}; - - my $maxx = $$rcfg{'xsize'}{$router}; - my $maxy = $$rcfg{'ysize'}{$router}; - my $xscale = $$rcfg{'xscale'}{$router}; - my $yscale = $$rcfg{'yscale'}{$router}; - my $growright = 0+($$rcfg{'options'}{'growright'}{$router} or 0); - my $bits = 0+($$rcfg{'options'}{'bits'}{$router} or 0); - my $integer = 0+($$rcfg{'options'}{'integer'}{$router} or 0); - my $step = 5*60; - my $rop; - my $ytics = $$rcfg{'ytics'}{$router}; - my $yticsf= $$rcfg{'yticsfactor'}{$router}; - my $timestrfmt = $$rcfg{'timestrfmt'}{$router}; - my $timestrpos = ${MRTG_lib::timestrpospattern}{uc $$rcfg{'timestrpos'}{$router}}; - - if (not defined $$rcfg{'ylegend'}{$router}){ - if ($bits){ - $$rcfg{'ylegend'}{$router} = &$LOC("Bits per minute") - if defined $$rcfg{'options'}{'perminute'}{$router}; - $$rcfg{'ylegend'}{$router} = &$LOC("Bits per hour") - if defined $$rcfg{'options'}{'perhour'}{$router}; - } else { - $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per minute") - if defined $$rcfg{'options'}{'perminute'}{$router}; - $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per hour") - if defined $$rcfg{'options'}{'perhour'}{$router}; - } - } - - if ($$rcfg{'ylegend'}{$router}) { - push (@exec, "l", "[$$rcfg{'ylegend'}{$router}]"); - } - my $sign = ($$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /d/) ? 1 : -1; - - if ($$rcfg{'pngtitle'}{$router}) { - push (@exec, "T", "[$$rcfg{'pngtitle'}{$router}]"); - } - - if ($$rcfg{'timezone'}{$router}) { - push (@exec, "Z", "$$rcfg{'timezone'}{$router}"); - } - - if ($$rcfg{'kilo'}{$router}) { - push (@exec, "k", $$rcfg{'kilo'}{$router}); - } - if ($$rcfg{'kmg'}{$router}) { - push (@exec, "K", $$rcfg{'kmg'}{$router}); - } - if ($$rcfg{'weekformat'}{$router}) { - push (@exec, "W", $$rcfg{'weekformat'}{$router}); - } - my $SAGE = (time - $main::STARTTIME) / 3600 / 24; # current script age - if (not defined $$opts{'log-only'}){ - if (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /d/) { - # VMS: should work for both now - push (@exec, "i", "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, ,$xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - @mxvls = ("d"); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}", - $$cfg{'interval'}); - } - - - if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") or - ((-M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") + $SAGE >= 0.5/24)) and - (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~/w/) - ) { - $step=30*60; - $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /w/) ? 1 : -1; - push (@mxvls , "w"); - $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /w/) ? "p" : "i"; - push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}", 30); - } - - - if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") or - (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") + $SAGE >= 2/24)) and - (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /m/)) { - $step=2*60*60; - $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /m/) ? 1 : -1; - push (@mxvls , "m"); - $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /m/) ? "p" : "i"; - push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}", 120); - } - - if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") or - (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") + $SAGE >= 1)) and - (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~/y/)) { - $step=24*60*60; - $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /y/) ? 1 : -1; - push (@mxvls , "y"); - $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /y/) ? "p" : "i"; - push (@exec, $rop, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}", 1440); - } - } - - # VMS: this might work now ... or does VMS NOT know about pipes? - # NT doesn't have fork() so an open(xxx,"-|") won't work - # OS2 fork() have bug with socket handles. In RunAsDaemon mode it fail - # after first loop (with "socket operation on non socket" message. - - if ($MRTG_lib::OS eq 'VMS' or $MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2'){ - map { s/"/\\"/; $_ = '"'.$_.'"' if /\s/ } @exec; - open (RATEUP, join (" ", @exec)."|") or - do { - warn "$NOW: WARNING: rateup (".(join " ", @exec ). - ") did not work: $!\n"; - return; - } - } elsif ($MRTG_lib::OS eq 'NW'){ - map { s/"/\\"/; $_ = '"'.$_.'"' if /\s/ } @exec; - - # Stuff around because of Perl problems. - - open (NWPARMS, ">"."$$cfg{'imagedir'}$router.dat") or - do { - warn "$NOW: WARNING: Rateup parameters [$$cfg{'imagedir'}$router.dat] [open] failed.\n"; - - return; - }; - print NWPARMS join (" ", @exec); - close NWPARMS; - - # Now run Rateup with path to Parameters. - - open (RATEUP, "SYS:/Mrtg/bin/rateup -f $$cfg{'imagedir'}$router.dat"."|") or - do { - warn "$NOW: WARNING: SYS:/Mrtg/bin/rateup -f $$cfg{'imagedir'}$router.dat did NOT work.\n"; - - return; - } - } else { - $! = undef; - open (RATEUP,"-|") or - do { exec @exec or - warn "$NOW: WARNING: rateup (".(join " ", @exec ). - ") did not work: $!\n"; - }; - - } - - debug('log', join(" ", @exec)); - - - if (open (HTML,"<$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}")) { - for ($i=0 ; $i<200 ; $i++) { - last if eof(HTML); - $_= <HTML>; - if (/<!-- maxin ([dwmy]) (\d*)/) { - $maxin{$1}{$router}=$2 || 0; - } - - if (/<!-- maxout ([dwmy]) (\d*)/) { - $maxout{$1}{$router}=$2 || 0; - } - - if (/<!-- maxpercent ([dwmy]) (\d*)/) { - $maxpercent{$1}{$router}=$2 || 0; - } - - if (/<!-- avin ([dwmy]) (\d*)/) { - $avin{$1}{$router}=$2 || 0; - } - - if (/<!-- avout ([dwmy]) (\d*)/) { - $avout{$1}{$router}=$2 || 0; - } - - if (/<!-- avpercent ([dwmy]) (\d*)/) { - $avpercent{$1}{$router}=$2 || 0; - } - - if (/<!-- cuin ([dwmy]) (\d*)/) { - $cuin{$1}{$router}=$2 || 0; - } - - if (/<!-- cuout ([dwmy]) (\d+)/) { - $cuout{$1}{$router}=$2 || 0; - } - - if (/<!-- cupercent ([dwmy]) (\d+)/) { - $cupercent{$1}{$router}=$2 || 0; - } - if (/<!-- avmxin ([dwmy]) (\d*)/) { - $avmxin{$1}{$router}=$2 || 0; - } - - if (/<!-- avmxout ([dwmy]) (\d*)/) { - $avmxout{$1}{$router}=$2 || 0; - } - } - close HTML; - } - - foreach $period (@mxvls) { - $res = <RATEUP>; - if (not defined $res and eof(RATEUP)){ - warn "$NOW: ERROR: Skipping webupdates because rateup did not return anything sensible\n"; - close RATEUP; - rateupcheck $router; - return; - }; - chomp $res; - $maxin{$period}{$router}=sprintf("%.0f",$res || 0); - chomp($res = <RATEUP>); - $maxout{$period}{$router}=sprintf("%.0f",$res || 0); - - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - chomp($res = <RATEUP>); - $maxpercent{$period}{$router}=sprintf("%.0f",$res || 0); - } - - chomp($res = <RATEUP>); - $avin{$period}{$router}=sprintf("%.0f",$res || 0); - chomp($res = <RATEUP>); - $avout{$period}{$router}=sprintf("%.0f",$res || 0); - - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - chomp($res = <RATEUP>); - $avpercent{$period}{$router}=sprintf("%.0f",$res || 0); - } - - chomp($res = <RATEUP>); - $cuin{$period}{$router}=sprintf("%.0f",$res || 0); - chomp($res = <RATEUP>); - $cuout{$period}{$router}=sprintf("%.0f",$res || 0); - - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - chomp($res = <RATEUP>); - $cupercent{$period}{$router}=sprintf("%.0f",$res || 0); - } - - chomp($res = <RATEUP>); - debug('avmx',"avmxin $res"); - $avmxin{$period}{$router}=sprintf("%.0f",$res || 0); - chomp($res = <RATEUP>); - debug('avmx',"avmxout $res"); - $avmxout{$period}{$router}=sprintf("%.0f",$res || 0); - - } - close(RATEUP); - rateupcheck $router; - if ( defined $$cfg{'writeexpires'} and $$cfg{'writeexpires'} =~ /^y/i ) { - my($fil,$exp); - while ( $fil = shift(@metas) ) { - $exp = &expistr(shift(@metas)); - open(META, ">$fil.meta"); - print META "Expires: $exp\n"; - close(META); - } - } - - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - return (\%maxin, \%maxout, \%maxpercent, \%avin, \%avout, \%avpercent, \%cuin, \%cuout, \%cupercent, \%avmxin, \%avmxout); - } else { - return (\%maxin, \%maxout, \%avin, \%avout, \%cuin, \%cuout, \%avmxin, \%avmxout); - } -} - -#format 10*$kilo to 10 kB/s -sub fmi { - my($number, $maxbytes, $router, @foo) = @_; - return "?????" unless defined $number; - my($rcfg,$LOC)=@foo; - my @short= (); - my $mul = 1; - if ($$rcfg{'kmg'}{$router}) { - my($i); - if (defined $$rcfg{'shortlegend'}{$router}) { - foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) { - $short[$#short+1] = "$i"."$$rcfg{'shortlegend'}{$router}"; - } - } - elsif ($$rcfg{'options'}{'bits'}{$router}) { - foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) { - if ($$rcfg{'options'}{'perminute'}{$router}) { - $short[$#short+1] = "$i".&$LOC("b/min"); - } elsif ($$rcfg{'options'}{'perhour'}{$router}) { - $short[$#short+1] = "$i".&$LOC("b/h"); - } else { - $short[$#short+1] = "$i".&$LOC("b/s"); - } - } - $mul= 8; - } else { - foreach $i (split(/,/, $$rcfg{'kmg'}{$router})) { - if ($$rcfg{'options'}{'perminute'}{$router}) { - $short[$#short+1] = "$i".&$LOC ("B/min"); - } elsif ($$rcfg{'options'}{'perhour'}{$router}) { - $short[$#short+1] = "$i".&$LOC("B/h"); - } else { - $short[$#short+1] = "$i".&$LOC("B/s"); - } - } - $mul= 1; - } - } else { - if (defined $$rcfg{'options'}{'bits'}{$router}) { - if ($$rcfg{'options'}{'perminute'}{$router}) { - @short = (&$LOC("b/min"),&$LOC("kb/min"),&$LOC("Mb/min"),&$LOC("Gb/min")); - } elsif (defined $$rcfg{'options'}{'perhour'}{$router}) { - @short = (&$LOC("b/h"),&$LOC("kb/h"),&$LOC("Mb/h"),&$LOC("Gb/h")); - } else { - @short = (&$LOC("b/s"),&$LOC("kb/s"),&$LOC("Mb/s"),&$LOC("Gb/s")); - } - $mul= 8; - } else { - if ($$rcfg{'options'}{'perminute'}{$router}) { - @short = (&$LOC("B/min"),&$LOC("kB/min"),&$LOC("MB/min"),&$LOC("GB/min")); - } elsif ($$rcfg{'options'}{'perhour'}{$router}) { - @short = (&$LOC("B/h"),&$LOC("kB/h"),&$LOC("MB/h"),&$LOC("GB/h")); - } else { - @short = (&$LOC("B/s"),&$LOC("kB/s"),&$LOC("MB/s"),&$LOC("GB/s")); - } - $mul= 1; - } - if ($$rcfg{'shortlegend'}{$router}) { - @short = ("$$rcfg{'shortlegend'}{$router}", - "k$$rcfg{'shortlegend'}{$router}", - "M$$rcfg{'shortlegend'}{$router}", - "G$$rcfg{'shortlegend'}{$router}"); - } - } - my $digits=length("".$number*$mul); - my $divm=0; - # - # while ($digits-$divm*3 > 4) { $divm++; } - # my $divnum = $number*$mul/10**($divm*3); - my $divnum=$number*$mul*$$rcfg{'factor'}{$router}; - # while ($divnum/$$rcfg{'kilo'}{$router} >= 10*$$rcfg{'kilo'}{$router} and $divnum<$#short) { - while (($divnum >= 10*$$rcfg{'kilo'}{$router} or $short[$divm] =~ /^-/) and - $divm<$#short) { - $divm++; - $divnum /= $$rcfg{'kilo'}{$router}; - } - my $perc; - if ($number == 0 || $maxbytes == 0) { - $perc = 0; - } else { - $perc = 100/$maxbytes*$number; - } - if (defined $$rcfg{'options'}{'integer'}{$router}) { - if ($$rcfg{'options'}{'nopercent'}{$router}) { - return sprintf("%.0f %s",$divnum,$short[$divm]); - } else { - return sprintf("%.0f %s (%2.1f%%)",$divnum,$short[$divm],$perc); - } - } else { - if (defined $$rcfg{'options'}{'nopercent'}{$router}) { - return sprintf("%.1f %s",$divnum,$short[$divm]); # Added: FvW - } else { - return sprintf("%.1f %s (%2.1f%%)",$divnum,$short[$divm],$perc); - } - return sprintf("%.1f %s (%2.1f%%)",$divnum,$short[$divm],$perc); - } -} - - -sub writehtml { - my($router, $cfg, $rcfg, $maxin, $maxout, $maxpercent, - $avin, $avout, $avmxin, $avmxout, $avpercent, - $cuin, $cuout, $cupercent, $uptime, $name, $LOC) = @_; - - my($VERSION,$Today,$peri); - - my($persec); - - if (defined $$rcfg{'options'}{'bits'}{$router}) { - $persec = &$LOC("Bits"); - } else { - $persec = &$LOC("Bytes"); - } - - # Work out the Colour legend - my($leg1, $leg2, $leg3, $leg4, $leg5); - if ($$rcfg{'legend1'}{$router}) { - $leg1 = $$rcfg{'legend1'}{$router}; - } else { - if ($$rcfg{'options'}{'perminute'}{$router}) { - $leg1=&$LOC("Incoming Traffic in $persec per Minute"); - } elsif ($$rcfg{'options'}{'perhour'}{$router}) { - $leg1=&$LOC("Incoming Traffic in $persec per Hour"); - } else { - $leg1=&$LOC("Incoming Traffic in $persec per Second"); - } - } - if ($$rcfg{'legend2'}{$router}) { - $leg2 = $$rcfg{'legend2'}{$router}; - } else { - if ($$rcfg{'options'}{'perminute'}{$router}) { - $leg2=&$LOC("Outgoing Traffic in $persec per Minute"); - } elsif ($$rcfg{'options'}{'perhour'}{$router}) { - $leg2=&$LOC("Outgoing Traffic in $persec per Hour"); - } else { - $leg2=&$LOC("Outgoing Traffic in $persec per Second"); - } - } - if ($$rcfg{'legend3'}{$router}) { - $leg3 = $$rcfg{'legend3'}{$router}; - } else { - $leg3 = &$LOC("Maximal 5 Minute Incoming Traffic"); - } - if ($$rcfg{'legend4'}{$router}) { - $leg4 = $$rcfg{'legend4'}{$router}; - } else { - $leg4 = &$LOC("Maximal 5 Minute Outgoing Traffic"); - } - if ($$rcfg{'legend5'}{$router}) { - $leg5 = $$rcfg{'legend5'}{$router}; - } else { - $leg5 = "(($leg1)/($leg2))*100"; - } - # Translate the color names - $$rcfg{'col1'}{$router}=&$LOC($$rcfg{'col1'}{$router}); - $$rcfg{'col2'}{$router}=&$LOC($$rcfg{'col2'}{$router}); - $$rcfg{'col3'}{$router}=&$LOC($$rcfg{'col3'}{$router}); - $$rcfg{'col4'}{$router}=&$LOC($$rcfg{'col4'}{$router}); - $$rcfg{'col5'}{$router}=&$LOC($$rcfg{'col5'}{$router}); - - my $dirrel = "../" x ($$rcfg{'directory_web'}{$router} =~ tr|/|/|); - - $Today=&$LOC(datestr(time)); - $VERSION = "2.17.7"; - open (HTML,">$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}") || - do { warn ("$NOW: WARNING: Writing $router.$$rcfg{'extension'}{$router}: $!"); - next }; - # this unforutnately confuses old IE greatly ... so we have to comment - # it out for now ... :-( - # print HTML '<?xml version="1.0" encoding="' . &$LOC('UTF-8') . '"?>' . "\n"; - print HTML '<!DOCTYPE html>' . "\n"; - print HTML '<html lang="en">' . "\n"; - my $interval =$$cfg{'interval'}; - my $expiration = &expistr($interval); - my $refresh = defined $$cfg{'refresh'} ? $$cfg{'refresh'} : 300; - my $namestring = &$LOC("the device"); - print HTML '<!-- MRTG ' . "$VERSION" . ' -->' . "\n"; - print HTML '<!-- Begin Head -->' . "\n"; - print HTML "\t" . '<head>' . "\n"; - print HTML "\t\t" . '<meta charset="' . &$LOC('UTF-8') . '" />' . "\n"; - print HTML "\t\t" . '<meta http-equiv="refresh" content="' . "$refresh" . '" />' . "\n"; -# Not functional in HTML file in modern web browsers - use HTTP header instead -# <meta http-equiv="pragma" content="no-cache" /> -# <meta http-equiv="cache-control" content="no-cache" /> -# <meta http-equiv="expires" content="$expiration" /> -# <meta http-equiv="generator" content="MRTG $VERSION" /> -# <meta http-equiv="date" content="$expiration" /> - print HTML "\t\t" . '<title>' . "$$rcfg{'title'}{$router}" . '' . "\n"; - foreach $peri (qw(d w m y)) { - print HTML <<"TEXT"; - - -TEXT - if ($$rcfg{'options'}{'dorelpercent'}{$router} and defined $$maxpercent{$peri}{$router}) { - print HTML <<"TEXT"; - -TEXT - } - print HTML <<"TEXT"; - - -TEXT - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - print HTML <<"TEXT"; - -TEXT - } - print HTML "\n" - if defined $$cuin{$peri}{$router}; - print HTML "\n" - if defined $$cuout{$peri}{$router}; - - if ($$rcfg{'options'}{'dorelpercent'}{$router} and $$cupercent{$peri}{$router} ) { - print HTML <<"TEXT"; - -TEXT - } - print HTML <<"TEXT" if $$avmxin{$peri}{$router} and $$avmxout{$peri}{$router}; - - -TEXT - - } - - $namestring = "'$name'" if $name; - - defined $$rcfg{backgc}{$router} or $$rcfg{backgc}{$router} = "#fff"; - - $$rcfg{'rgb1'}{$router} = "" unless defined $$rcfg{'rgb1'}{$router}; - $$rcfg{'rgb2'}{$router} = "" unless defined $$rcfg{'rgb2'}{$router}; - $$rcfg{'rgb3'}{$router} = "" unless defined $$rcfg{'rgb3'}{$router}; - $$rcfg{'rgb4'}{$router} = "" unless defined $$rcfg{'rgb4'}{$router}; - $$rcfg{'rgb5'}{$router} = "" unless defined $$rcfg{'rgb5'}{$router}; - $$rcfg{'rgb6'}{$router} = "" unless defined $$rcfg{'rgb6'}{$router}; - - print HTML " "; - - # allow for \n in addhead - defined $$rcfg{addhead}{$router} or $$rcfg{addhead}{$router} = ""; - defined $$rcfg{pagetop}{$router} or $$rcfg{pagetop}{$router} = ""; - - if (defined $$rcfg{bodytag}{$router}) { - if ($$rcfg{bodytag}{$router} !~ / -"; - print HTML " -$$rcfg{bodytag}{$router} - $$rcfg{'pagetop'}{$router} - "; - print HTML "

"; - if (defined $$rcfg{'timezone'}{$router}){ - print HTML - &$LOC("The statistics were last updated $Today $$rcfg{'timezone'}{$router}"); - } else { - print HTML - &$LOC("The statistics were last updated $Today"); - } - if ($uptime and ! $$rcfg{options}{noinfo}{$router}) { - print HTML - ",
\n". - &$LOC("at which time $namestring had been up for $uptime.") - } - print HTML "

"; - my %sample= ('d' => "`Daily' Graph (".$interval.' Minute', - 'w' => "`Weekly' Graph (30 Minute", - 'm' => "`Monthly' Graph (2 Hour", - 'y' => "`Yearly' Graph (1 Day"); - - my %full = ('d' => 'day', - 'w' => 'week', - 'm' => 'month', - 'y' => 'year'); - - my $InCo; - if (!(defined $$rcfg{'options'}{'noi'}{$router})) { - if (exists $$rcfg{'legendi'}{$router}) { - if ($$rcfg{'legendi'}{$router} ne "") { - $InCo=$$rcfg{'legendi'}{$router}; - } - } else { - $InCo=&$LOC("In"); - } - } - - my $OutCo; - if (!(defined $$rcfg{'options'}{'noo'}{$router})) { - if (exists $$rcfg{'legendo'}{$router}) { - if ($$rcfg{'legendo'}{$router} ne "") { - $OutCo=$$rcfg{'legendo'}{$router}; - } - } else { - $OutCo=&$LOC("Out"); - } - } - my $PercentCo; - if (defined $$rcfg{'legend5'}{$router}) { - if ($$rcfg{'legend5'}{$router} ne "") { - $PercentCo=$$rcfg{'legend5'}{$router}; - } - } else { - $PercentCo=&$LOC("Percentage"); - } - - foreach $peri (qw(d w m y)) { - next if defined $$rcfg{'suppress'}{$router} and $$rcfg{'suppress'}{$router} =~/$peri/; - my $gifw; - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router}+ - +100+30) *$$rcfg{'xzoom'}{$router}); - } else { - $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router} - +100) *$$rcfg{'xzoom'}{$router}); - } - my $gifh=sprintf("%.0f",($$rcfg{'ysize'}{$router}*$$rcfg{'yscale'}{$router}+35) - *$$rcfg{'yzoom'}{$router}); - - # take the image directory away from the html directory to give us relative links - - - my $imagepath = ( $cfg->{htmldir} ne $cfg->{imagedir} ) ? "$dirrel$$cfg{imagehtml}$$rcfg{directory_web}{$router}" : ""; - print HTML " - -
-

".&$LOC("$sample{$peri}").&$LOC(' Average)')."

- \"$full{$peri}\" - - - - - - - "; - my(@foo)=($rcfg,$LOC); - print HTML " - - - - - - " if $InCo; - print HTML " - - - - - - " if $OutCo; - print HTML " - - - - - - " if ($$rcfg{'options'}{'dorelpercent'}{$router} and $PercentCo); -print HTML " - - - " if ($$rcfg{'options'}{'avgpeak'}{$router} and $InCo and $OutCo); - print HTML " -
" . &$LOC("Max") . "" . &$LOC("Average") . "" . &$LOC("Current") . "
" . $InCo . "".&fmi($$maxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."".&fmi($$avin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)." ".&fmi($$cuin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."
" . $OutCo . "".&fmi($$maxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." ".&fmi($$avout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." ".&fmi($$cuout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)."
" . $PercentCo . "".sprintf("%0.1f %%",($$maxpercent{$peri}{$router} || 0))." ".sprintf("%0.1f %%",($$avpercent{$peri}{$router} || 0 ))." ".sprintf("%0.1f %%",($$cupercent{$peri}{$router} || 0 ))."
- " . &$LOC("Average max 5 min values for $sample{$peri} interval):") . " - $InCo " . &fmi($$avmxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo) . "/ - $OutCo " . &fmi($$avmxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo) . " -
-
-\n"; -} - - if (!(defined $$rcfg{'options'}{'nolegend'}{$router})) { - print HTML " - -
- "; - print HTML " - - - - " if $InCo; - print HTML " - - - - " if $OutCo; - if ($$rcfg{'withpeak'}{$router}) { - print HTML " - - - - " if $InCo; - print HTML " - - - - " if $OutCo; - } - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - print HTML " - - - - "; - } - print HTML " -
$$rcfg{'col1'}{$router} ###$leg1
$$rcfg{'col2'}{$router} ###$leg2
$$rcfg{'col3'}{$router} ###$leg3
$$rcfg{'col4'}{$router} ###$leg4
$$rcfg{'col5'}{$router} ###$leg5
-
- -"; - } - - if (!(defined $$rcfg{'options'}{'nobanner'}{$router})) { - my $gifPath; - - if (defined $$cfg{icondir}) { - $gifPath = $$cfg{icondir}; - #lets make sure there is a trailing path separator - $gifPath =~ s|/*$|/|; - } else { - $gifPath = "$dirrel$$cfg{imagehtml}"; - } - - print HTML< -
- -TEXT - } - - print HTML $$rcfg{'pagefoot'}{$router} if defined $$rcfg{'pagefoot'}{$router}; - print HTML < - - -TEXT - close HTML; - - if (defined $$cfg{'writeexpires'} and $$cfg{'writeexpires'} =~ /^y/i) { - open(HTMLG, ">$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.". - "$$rcfg{'extension'}{$router}.meta") || - do { - warn "$NOW: WARNING: Writing $$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.". - "$$rcfg{'extension'}{$router}.meta: $!\n"; - next - }; - - print HTMLG "Expires: $expiration\n"; - close(HTMLG); - } -} - - -sub printusage { - print < - -mrtg-2.17.7 - Multi Router Traffic Grapher - -Copyright 1995-2006 by Tobias Oetiker -Licensed under the Gnu GPL. - -If you want to know more about this tool, you might want -to read the docs. You can find everything on the -mrtg website: - -http://oss.oetiker.ch/mrtg/ - -USAGEDESC - exit(1); -} - - -sub lockit { - my ($lockfile,$templock) = @_; - if ($MRTG_lib::OS eq 'VMS' or $MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2') { - # too sad NT and VMS can't do links we'll do the diletants lock - if (-e $lockfile and not unlink $lockfile) { - my($lockage) = time()-(stat($lockfile))[9]; - die "$NOW: ERROR: I guess another mrtg is running. A lockfile ($lockfile)\n". - " aged $lockage seconds is hanging around and I can't remove\n". - " it because another process is still using it."; - } - - open (LOCK, ">$lockfile") or - die "$NOW: ERROR: Creating lockfile $lockfile: $!\n"; - print LOCK "$$\n"; - close LOCK; - open (LOCK, "<$lockfile") or - die "$NOW: ERROR: Reading lockfile $lockfile for owner check: $!\n"; - my($read)=; - chomp($read); - die "$NOW: ERROR: Someone else just got the lockfile $lockfile\n" - unless $$ == $read; - } else { - # now, lets do it the UNIX way ... Daves work ... - open(LOCK,">$templock") or die "$NOW: ERROR: Creating templock $templock: $!"; - $main::Cleanfile = $templock; - if (!link($templock,$lockfile)) { # Lock file exists - deal with it. - my($nlink,$lockage) = (stat($lockfile))[3,9]; - $lockage = time() - $lockage; - if ($nlink < 2 or $lockage > 30*60) { #lockfile is alone and old - unlink($lockfile) - || do{ unlink $templock; - die "$NOW: ERROR: Can't unlink stale lockfile ($lockfile). Permissions?\n"}; - link($templock,$lockfile) - || do{ unlink $templock; - die "$NOW: ERROR: Can't create lockfile ($lockfile).\n". - "Permission problem or another mrtg locking succesfully?\n"}; - } else { - unlink $templock; - die "$NOW: ERROR: It looks as if you are running two copies of mrtg in parallel on\n". - " the same config file. There is a lockfile ($lockfile) and it is\n". - " is only $lockage seconds old ... Check your crontab.\n". - " (/etc/crontab and /var/spool/cron/root) \n" - if $lockage < 4; - - die "$NOW: ERROR: I guess another mrtg is running. A lockfile ($lockfile) aged\n". - "$lockage seconds is hanging around. If you are sure that no other mrtg\n". - "is running you can remove the lockfile\n"; - - } - - } - } -} - -sub threshmail ($$$$){ - my $server = shift; - my $from = shift; - my $to = shift; - my $message = shift; - debug('base',"sending threshmail from $from to $to"); - my $smtp = Net::SMTP->new([split /\s*,\s*/, $server],Timeout=>5) or - do { warn "$NOW: ERROR: could not send thresholdmail to $to"; return }; - $smtp->mail($from); - $smtp->to(split(/\s*,\s*/, $to)); - $smtp->data(); - $smtp->datasend($message); - $smtp->dataend(); - $smtp->quit; -} - -sub threshcheck { - # threshold checking by Tom Muggli - # ... fsck'd up but fixed by Juha Laine - my ($cfg,$rcfg,$cfgfile,$router,$cuin,$cuout) = @_; - my $threshfile; - my %cu = ( i=> $cuin, o=>$cuout ); - # are we going to keep state ? - if (defined $$cfg{'threshdir'}){ - ensureSL(\$$cfg{'threshdir'}); - $threshfile = $$cfg{'threshdir'}.(split /\Q$MRTG_lib::SL\E/, $cfgfile)[-1].".$router"; - } - - # setup environment for external scripts - if (defined $rcfg->{'threshdesc'}{$router}) { - $ENV{THRESH_DESC}=$rcfg->{'threshdesc'}{$router}; - } else { - delete $ENV{THRESH_DESC}; - } - if (defined $rcfg->{'hwthreshdesc'}{$router}) { - $ENV{HWTHRESH_DESC}=$rcfg->{'hwthreshdesc'}{$router}; - } else { - delete $ENV{HWTHRESH_DESC}; - } - - for my $dir (qw(i o)){ # in and out - my %thresh = ( - thresh => $cu{$dir}{d}{$router}, - - # if we are looking at an rrd with holtwinters RRAs - # we get a failures count. - hwthresh => $cu{$dir}{d_hwfail}{$router} - ); - for my $type (keys %thresh){ - for my $bound (qw(min max)){ - # it gets set inside the loop because threshval gets modified further - # down in the code - my $threshval = $thresh{$type}; - next if not defined $threshval; - - my $boundval = $rcfg->{$type.$bound.$dir}{$router}; - next unless defined $boundval; - - my $realval = ""; - my $realthresh = ""; - - if ($boundval =~ s/%$//) { # defined in % of maxbytes - # 2 decimals in % - $realval = "% ($threshval)"; - $realthresh = "% (".sprintf("%.1f",($rcfg->{"maxbytes".($dir eq 'i' ? 1 : 2)}{$router} * $boundval / 100)).")"; - $threshval = sprintf "%.1f", ($threshval / $rcfg->{"maxbytes".($dir eq 'i' ? 1 : 2)}{$router} * 100); # the new code - } - my $msghead = ""; - $msghead = "From: $cfg->{threshmailsender}\nTo: $rcfg->{${type}.'mailaddress'}{$router}" - if $rcfg->{${type}.'mailaddress'}{$router} and $cfg->{threshmailsender}; - my $pagetop = $rcfg->{pagetop}{$router} || ''; - $pagetop =~ s|

.*?

||; - $pagetop =~ s|\s*\s*(.*?)\s*(.*?)\s*\s*|$1 $2\n|g; - $pagetop =~ s|\s*<.+?>\s*\n?||g; - - my $msgbody = <{title}{$router} - - Target: $router - Type: $type -Direction: $dir - Bound: $bound -Threshold: $boundval$realthresh - Current: $threshval$realval - -$pagetop - -MESSAGE - $msgbody .= "\n$rcfg->{$type.'desc'}{$router}\n" if $rcfg->{$type.'desc'}{$router}; - - - if (($bound eq 'min' and $boundval > $threshval) or - ($bound eq 'max' and $boundval < $threshval)) { - # threshold was broken... - my $message = <{$type.'prog'.$dir}{$router}, $router, - $rcfg->{$type.$bound.$dir}{$router}, $threshval,($rcfg->{$type.'desc'}{$router} ||"No Description")); - - # Check if we use the status file or not... - if ( defined $threshfile ) { - if ( not -e $threshfile.".".$type.$bound.uc($dir) ) { - # Create a file to indicate a threshold problem for the time after the problem - open THRESHTOUCH, ">".$threshfile.".".$type.$bound.uc($dir) - or warn "$NOW: WARNING: Creating $threshfile.".$bound.uc($dir).": $!\n"; - close THRESHTOUCH; - if (defined $rcfg->{$type.'prog'.$dir}{$router}){ - debug('base',"run threshprog$dir: ".(join ",",@exec)); - system @exec; - } - threshmail $cfg->{threshmailserver},$cfg->{threshmailsender},$rcfg->{$type.'mailaddress'}{$router},$message - if $rcfg->{$type.'mailaddress'}{$router} - } - else { - debug('base',"NOT acting on BROKEN threshold since $threshfile.$type$bound$dir exists"); - } - } elsif ( not defined $cfg->{$type.'hyst'} or - ($bound eq 'min' and $boundval - $cfg->{$type.'hyst'}* $boundval < $threshval) or - ($bound eq 'max' and $boundval + $cfg->{$type.'hyst'}* $boundval > $threshval) - ) { - # no threshold dir so run on every 'break' - if (defined $rcfg->{$type.'prog'.$dir}{$router}){ - debug('base',"run ${type}prog$dir: ".(join ",",@exec)); - system @exec; - } - threshmail $cfg->{threshmailserver},$cfg->{threshmailsender},$rcfg->{$type.'mailaddress'}{$router},$message - if $rcfg->{$type.'mailaddress'}{$router}; - } - } else { - # no threshold broken ... - my @exec = ( $rcfg->{$type.'progok'.$dir}{$router}, $router, - $rcfg->{$type.$bound.$dir}{$router}, $threshval); - my $message = <{$type.$bound.$dir}{$router} vs $threshval) - -Threshold UN-BROKEN -------------------- -$msgbody -MESSAGE - - # Check if we use the status file or not... - if ( defined $threshfile ) { - if ( -e $threshfile.".".$type.$bound.uc($dir) ){ - unlink "$threshfile.".$type.$bound.uc($dir); - if (defined $rcfg->{$type.'progok'.$dir}{$router}){ - debug('base',"run ${type}progok$dir: ".(join ",",@exec)); - system @exec; - } - threshmail $cfg->{threshmailserver},$cfg->{threshmailsender},$rcfg->{$type.'mailaddress'}{$router},$message - if $rcfg->{$type.'mailaddress'}{$router}; - } - } - } - } # for my $bound ... - } # for my $type - } # for my $dir -} - -sub getexternal ($) { - my $command = shift; - my $in=undef; - my $out=undef; - my $uptime="unknown"; - my $name="unknown"; - - open (EXTERNAL , $command."|") - or warn "$NOW: WARNING: Running '$command': $!\n"; - - warn "$NOW: WARNING: Could not get any data from external command ". - "'".$command. - "'\nMaybe the external command did not even start. ($!)\n\n" if eof EXTERNAL; - - chomp( $in=) unless eof EXTERNAL; - chomp( $out=) unless eof EXTERNAL; - chomp( $uptime=) unless eof EXTERNAL; - chomp( $name=) unless eof EXTERNAL; - - close EXTERNAL; - - # strip returned date - $uptime =~ s/^\s*(.*?)\s*/$1/; - $name =~ s/^\s*(.*?)\s*/$1/; - - # do we have numbers in the external programs answer ? - if ( not defined $in ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'in' but nothing'\n\n"; - } elsif ( $in eq 'UNKNOWN' ) { - $in = undef; - } elsif ( $in !~ /([-+]?\d+(.\d+)?)/ ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'in' but got '$in'\n\n"; - $in = undef; - } else { - $in = $1; - } - - if ( not defined $out ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'out' but nothing'\n\n"; - } elsif ( $out eq 'UNKNOWN' ) { - $out = undef; - } elsif ( $out !~ /([-+]?\d+(.\d+)?)/ ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'out' but got '$out'\n\n"; - $out = undef; - } else { - $out = $1; - } - debug('snpo',"External result:".($in||"undef")." out:".($out||"undef")." uptime:".($uptime||"undef")." name:".($name||"undef")); - return ($in,$out,time,$uptime,$name); -} - -sub getsnmparg ($$$$){ - my $confcache = shift; - my $target = shift; - my $cfg = shift; - my $populated = shift; - my $retry = 0; - - my $hostname = $$target{Host}; - my $hostkey = "$$target{Community}\@$$target{Host}$$target{SnmpOpt}"; - - if ($$target{ipv4only}) { - if (not ( $hostname =~ /^\d+\.\d+\.\d+\.\d+$/ or gethostbyname $hostname) ){ - warn "$NOW: WARNING: Skipping host $hostname as it does not resolve to an IPv4 address\n"; - return 'DEADHOST'; - } - } else { - if($hostname =~ /^\[(.*)\]$/) { - # Numeric IPv6 address. Check that it's valid - $hostname = substr($hostname, 1); - chop $hostname; - if(! inet_pton(AF_INET6(), $hostname)) { - warn "$NOW: WARNING: Skipping host $hostname: invalid IPv6 address\n"; - return 'DEADHOST'; - } - } else { - # Hostname. Look it up - my @res; - my ($too,$port,$otheropts) = split(':', $$target{SnmpOpt}, 3); - $port = 161 unless defined $port; - @res = getaddrinfo($hostname, $port, Socket::AF_UNSPEC(), Socket::SOCK_DGRAM()); - if (scalar (@res) < 5) { - warn "$NOW: WARNING: Skipping host $hostname as it does not resolve to an IPv4 or IPv6 address\n"; - return 'DEADHOST'; - } - } - } - RETRY: - my @ifnum = (); - my @OID = (); - # Find apropriate Interface to poll from - for my $i (0..1) { - if ($$target{IfSel}[$i] eq 'If') { - $ifnum[$i] = ".".$$target{Key}[$i]; - debug('snpo',"simple If: $ifnum[$i]"); - } elsif($$target{IfSel}[$i] eq 'None') { - $ifnum[$i] = ""; - } else { - $$target{Key}[$i] =~ s/\s+$//; # no trainling whitespace in keys ... - - if (not defined readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i])) { - debug('snpo',"($i) Populate ConfCache for $$target{Host}$$target{SnmpOpt}"); - populateconfcache($confcache,"$$target{Community}\@$$target{Host}$$target{SnmpOpt}",$$target{ipv4only},1,$$target{snmpoptions}); - $$populated{$hostname} = 1; # set cache population to true for this cycle and host - } - if (not defined readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i])) { - warn "$NOW: WARNING: Could not match host:'$$target{Community}\@$$target{Host}$$target{SnmpOpt}' ref:'$$target{IfSel}[$i]' key:'$$target{Key}[$i]'\n"; - return 'NOMATCH'; - } else { - $ifnum[$i] = ".".readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i]); - debug('snpo',"($i) Confcache Match $$target{Key}[$i] -> $ifnum[$i]"); - } - } - if ($ifnum[$i] !~ /^$|^\.\d+$/) { - warn "$NOW: WARNING: Can not determine". - " ifNumber for $$target{Community}\@$$target{Host}$$target{SnmpOpt} \tref: '$$target{IfSel}[$i]' \tkey: '$$target{Key}[$i]'\n"; - return 'NOMATCH'; - } - } - for my $i (0..1) { - # add ifget methodes call for a cross check; - for ($$target{IfSel}[$i]) { - /^Eth$/ && do { - push @OID, "ifPhysAddress".$ifnum[$i]; last - }; - /^Ip$/ && do { - push @OID, "ipAdEntIfIndex".".".$$target{Key}[$i];last - }; - /^Descr$/ && do { - push @OID, "ifDescr".$ifnum[$i]; last - }; - /^Type$/ && do { - push @OID, "ifType".$ifnum[$i]; last - }; - /^Name$/ && do { - push @OID, "ifName".$ifnum[$i]; last - }; - } - push @OID ,$$target{OID}[$i].$ifnum[$i]; - } - # we also want to know uptime and system name unless we are - if ( not defined $$cfg{nomib2} and $$cfg{logformat} ne 'rrdtool' ) { - if ( $OID[0] !~ /^cache.+$/ and - $OID[0] !~ /^\Q1.3.6.1.4.1.3495.1\E/ ) { - push @OID, qw(sysUptime sysName); - } else { - push @OID, qw(cacheUptime cacheSoftware cacheVersionId) - } - } - - # pull that data - debug('snpo',"SNMPGet from $$target{Community}\@$$target{Host}$$target{SnmpOpt} -- ".(join ",", @OID)); - my @ret; - - # make sure we have no error messages hanging round. - - $SNMP_Session::errmsg = undef; - $Net_SNMP_util::ErrorMessage = undef; - - my $targtemp = $$target{Community}.'@'.$$target{Host}.$$target{SnmpOpt}; - $targtemp = v4onlyifnecessary($targtemp, $$target{ipv4only}); - - my @snmpoids = grep !/^(Pseudo|WaLK|GeTNEXT|CnTWaLK)|IndexPOS/, @OID; - - if (defined $$cfg{singlerequest}){ -#LH local $BER::pretty_print_timeticks = 0; - foreach my $oid (@snmpoids){ - push @ret, snmpget($targtemp,$$target{snmpoptions},$oid); - } - } else { - @ret = snmpget($targtemp,$$target{snmpoptions},@snmpoids); - } - my @newret; - for (@OID) { - /^PseudoZero$/ && do { push @newret, 0; next; }; - /^PseudoOne$/ && do { push @newret, 1; next; }; - s/^WaLK(\d*)// && do { my $idx = $1 || 0; my $oid=$_;push @newret, (split /:/, (snmpwalk($targtemp,$$target{snmpoptions},$oid))[$idx],2)[1]; - debug('snpo',"snmpwalk '$oid' -> ".($newret[-1]||'UNDEF'));next}; - s/^GeTNEXT// && do { my $oid=$_;push @newret, (split /:/, snmpgetnext($targtemp,$$target{snmpoptions},$oid),2)[1]; - debug('snpo',"snmpgetnext '$oid' -> ".($newret[-1]||'UNDEF'));next}; - s/^CnTWaLK// && do { my $oid=$_;my @insts= (snmpwalk($targtemp,$$target{snmpoptions},$_)); - undef @insts if( $insts[1] || '') =~/no/i; push @newret, scalar @insts; - debug('snpo',"snmpCountwalk '$oid' -> ".($newret[-1]||'UNDEF'));next}; - /IndexPOS.*\.(\d*)/ && do { my $idx=$1; s/IndexPOS/$idx/; s/\.\d*$//; push @newret, snmpget($targtemp,$$target{snmpoptions},$_); - debug('snpo', "snmpget of oid '$_' after replacement of IndexPOS"); next}; - push @newret, shift @ret; - } - @ret = @newret; - debug('snpo',"SNMPfound -- ".(join ", ", map {"'".($_||"undef")."'"} @ret)); - $ret[-2] = $ret[-2].' '.$ret[-1] if $OID[-1] and $OID[-1] eq 'cacheVersionId'; - my $time = time; - my @final; - # lets do some reality check - for my $i (0..1) { - # some ifget methodes call for a cross check; - for ($$target{IfSel}[$i]) { - /^Eth$/ && do { - my $bin = shift @ret || 0xff; - my $eth = unpack 'H*', $bin; - my @eth; - while ($eth =~ s/^..//){ - push @eth, $&; - } - my $phys=join '-', @eth; - if ($phys ne $$target{Key}[$i]) { - debug('snpo', "($i) eth if crosscheck got $phys expected $$target{Key}[$i]"); - if (not $retry) { - $retry=1; - # remove broken entry - storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef); - debug('repo',"($i) goto RETRY force if cache repopulation"); - goto RETRY; - } else { - warn "$NOW: WARNING: could not match&get". - " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for Eth $$target{Key}[$i]\n"; - return 'NOMATCH'; - } - }; - debug ('snpo',"($i) Eth crosscheck OK"); - }; - /^Ip$/ && do { - my $if = shift @ret || 'none'; - if ($ifnum[$i] ne '.'.$if) { - debug('repo', "($i) IP if crosscheck got .$if expected $ifnum[$i]"); - if (not $retry) { - $retry=1; - # remove broken entry - storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef); - debug('repo',"($i) goto RETRY force if cache repopulation"); - goto RETRY; - } else { - warn "$NOW: WARNING: could not match&get". - " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for IP $$target{Key}[$i]\n"; - return 'NOMATCH'; - } - } - debug ('snpo',"($i) IP crosscheck OK"); - }; - /^(Descr|Name|Type)$/ && do { - my $descr = shift @ret || 'Empty'; - $descr =~ s/[\0- ]+$//; # remove excess spaces and stuff - if ($descr ne $$target{Key}[$i]) { - debug('repo', "($i) $_ if crosscheck got $descr expected $$target{Key}[$i]"); - if (not $retry) { - $retry=1; - # remove broken entry - storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef); - debug('repo',"($i) goto RETRY force if cache repopulation"); - goto RETRY; - } else { - warn "$NOW: WARNING: could not match&get". - " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for $_ '$$target{Key}[$i]'\n"; - return 'NOMATCH'; - } - } - debug ('snpo',"($i) $_ crosscheck OK"); - }; - } - # no sense continuing here ... if there is no data ... - if (defined $SNMP_Session::errmsg and $SNMP_Session::errmsg =~ /no response received/){ - $SNMP_Session::errmsg = undef; - warn "$NOW: WARNING: skipping because at least the query for $OID[0] on $$target{Host} did not succeed\n"; - return 'DEADHOST'; - } - if (defined $Net_SNMP_util::ErrorMessage and $Net_SNMP_util::ErrorMessage =~ /No response from remote/){ - $Net_SNMP_util::ErrorMessage = undef; - warn "$NOW: WARNING: skipping because at least the query for $OID[0] on $$target{Host} did not succeed\n"; - return 'DEADHOST'; - } - if ($$target{OID}[$i] =~ /if(Admin|Oper)Hack/) { - push @final, ((shift @ret) == 1) ? 1:0; - } else { - push @final, shift @ret; - } - } - - my @res = ( @final,$time, @ret); - - # Convert in and out values to integers with a user-defined subroutine - # specified by the Conversion target key - if( $target->{ Conversion } ) { - foreach my $ri( 0..1 ) { - next unless defined $res[ $ri ]; - local $SIG{__DIE__}; - my $exp = "&MRTGConversion::$target->{ Conversion }( \$res[\$ri] )"; - $res[ $ri ] = eval $exp; - warn "$NOW: WARNING: evaluation of \"$exp\" failed\n$@\n" if $@; - } - } - - # have some cleanup first, it seems that some agents - # are adding newlines to what they return - map{ $_ =~ s/\n|\r//g if defined $_ } @res; - map{ $_ =~ s/^\s+//g if defined $_ } @res; - map{ $_ =~ s/\s+$//g if defined $_ } @res; - - # in and out should be numbers only - for my $ri (0..1){ - # for folks using rrdtool I am allowing numbers - # with decimals here - if ( defined $res[$ri] and $res[$ri] !~ /^[-+]?\d+(.\d+)?$/ ) { - warn "$NOW: WARNING: Expected a number but got '$res[$ri]'\n"; - $res[$ri] = undef; - - } - } - return @res; - } - - -# read target function ... -sub readtargets ($$$) { - my ($confcache,$target,$cfg) = @_; - my $forks = $$cfg{forks}; - my $trgnum = $#{$target}+1; - if (defined $forks and $forks > 1 and $trgnum > 1){ - $forks = $trgnum if $forks > $trgnum; - my $split = int($trgnum / $forks) + 1; - my @hand; - # get them forks to work ... - for (my $i = 0; $i < $forks;$i++) { - local *D; - my $sleep_count=0; - my $pid; - do { - $pid = open(D, "-|"); - unless (defined $pid) { - warn "$NOW: WARNING cannot fork: $!\n"; - die "$NOW: ERROR bailing out after 6 failed forkattempts" - if $sleep_count++ > 6; - sleep 10; - } - } until defined $pid; - if ($pid) { # parent - $hand[$i] = *D; # funky file handle magic ... - debug ('fork',"Parent $$ after fork of child $i"); - } else { # child - debug ('fork',"Child $i ($$) after fork"); - my $res = ""; - my %deadhost; - my %populated; - for (my $ii = $i * $split; - $ii < ($i+1) * $split and $ii < $trgnum; - $ii++){ - my $targ = $$target[$ii]; - my @res; - if ($$targ{Methode} eq 'EXEC') { - @res = getexternal($$targ{Command}); - } else { # parent - if (not $deadhost{$$targ{Community}.$$targ{Host}}) { - @res = getsnmparg($confcache,$targ,$cfg,\%populated); - if ( $res[0] and $res[0] eq 'DEADHOST') { - # guess we got a blank here - @res = ( undef,undef,time,undef,undef); - $deadhost{$$targ{Community}.$$targ{Host}} = 1; - warn "$NOW: WARNING: no data for $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}\@$$targ{Host}. Skipping further queries for Host $$targ{Host} in this round.\n" - } elsif ($res[0] and $res[0] eq 'NOMATCH'){ - @res = (undef,undef,time,undef,undef); - } - } else { - @res = ( undef,undef,time,undef,undef); - } - } - - for (my $iii=0;$iii<5;$iii++){ - if (defined $res[$iii]){ - $res .= "$res[$iii]\n"; - } else { - $res .= "##UNDEF##\n"; - } - } - } - debug ('fork',"Child $i ($$) waiting to deliver"); - print $res; # we only talk after the work has been done to - # otherwhise we might get blocked - # return updated hosts from confcache - writeconfcache($confcache,'&STDOUT') - if defined $$confcache{___updated}; - exit 0; - } - - } - # happy reaping ... - my $vin =''; # vector of pipe file-descriptors from children - for (my $i = 0; $i < $forks;$i++) { - vec($vin, fileno($hand[$i]), 1) = 1; - } - my $left = $forks; - while ($left) { - my $rout = $vin; # read vector - my $eout = $vin; # exception vector - my $nfound = select($rout, undef, $eout, undef); # no timeout - if (1 > $nfound) { - die sprintf("$NOW: ERROR: select returned %d: $!\n", $nfound); - } - for (my $i = 0; $i < $forks; $i++) { - next unless defined $hand[$i] and defined fileno($hand[$i]); -# this does not seem to work reliably -# if (vec($eout, fileno($hand[$i]), 1)) { -# die "$NOW: ERROR: fork $i has died ahead of time?\n"; -# } - next unless vec($rout, fileno($hand[$i]), 1); - - vec($vin, fileno($hand[$i]), 1) = 0; # remove this child fd - - debug ('fork',"Parent reading child $i"); - my $h = $hand[$i]; - for (my $ii = $i * $split; - $ii < ($i+1) * $split and $ii < $trgnum; - $ii++){ - my $targ = $$target[$ii]; - my @res; - for (0..4){ - my $line = <$h>; # must be a simple scalar here else it wont work - die "$NOW: ERROR: fork $i has died ahead of time ...\n" if not defined $line; - chomp $line; - # debug ('fork',"reading for $ii $line"); - $line = undef if $line eq "##UNDEF##"; - push @res,$line; - }; - - ($$targ{_IN_}, - $$targ{_OUT_}, - $$targ{_TIME_}, - $$targ{_UPTIME_}, - $$targ{_NAME_}) = @res; - if ($] >= 5.0061){ - $$targ{_IN_} = Math::BigFloat->new($$targ{_IN_}) if $$targ{_IN_}; - $$targ{_OUT_} = Math::BigFloat->new($$targ{_OUT_}) if $$targ{_OUT_}; - } - } - # feed confcache entries - my $lasthost =""; - while (<$h>){ - chomp; - my ($host,$method,$key,$value) = split (/\t/, $_); - if ($host ne $lasthost){ - debug ('fork',"start clearing confcache on first entry for target $host"); - clearfromcache($confcache,$host); - debug ('fork',"finished clearing confcache"); - } - $lasthost = $host; - storeincache($confcache,$host,$method,$key,$value); - } - close $h; - --$left; - } - } - - } else { - my %deadhost; - my %populated; - foreach my $targ (@$target) { - if ($$targ{Methode} eq 'EXEC') { - debug('snpo', "run external $$targ{Command}"); - ($$targ{_IN_}, - $$targ{_OUT_}, - $$targ{_TIME_}, - $$targ{_UPTIME_}, - $$targ{_NAME_}) = getexternal($$targ{Command}); - } elsif ($$targ{Methode} eq 'SNMP' and not $deadhost{$$targ{Host}}) { - debug('snpo', "run snmpget from $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}\@$$targ{Host}"); - ($$targ{_IN_}, - $$targ{_OUT_}, - $$targ{_TIME_}, - $$targ{_UPTIME_}, - $$targ{_NAME_}) = getsnmparg($confcache,$targ,$cfg,\%populated); - if ( $$targ{_IN_} and $$targ{_IN_} eq 'DEADHOST') { - $$targ{_IN_} = undef; - $$targ{_TIME_} =time; - # guess we got a blank here - $deadhost{$$targ{Host}} = 1; - warn "$NOW: WARNING: no data for $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}\@$$targ{Host}. Skipping further queries for Host $$targ{Host} in this round.\n" - } - if ( $$targ{_IN_} and $$targ{_IN_} eq 'NOMATCH') { - $$targ{_IN_} = undef; - $$targ{_TIME_} =time; - } - - } else { - $$targ{_IN_} = undef; - $$targ{_OUT_} = undef; - $$targ{_TIME_} = time; - $$targ{_UPTIME_} = undef; - $$targ{_NAME_} = undef; - } - if ($] >= 5.008 ){ - $$targ{_IN_} = new Math::BigFloat "$$targ{_IN_}" if $$targ{_IN_}; - $$targ{_OUT_} = new Math::BigFloat "$$targ{_OUT_}" if $$targ{_OUT_}; - } - - } - } - -} - -sub imggen ($) { - my $dir = shift; - if ( ! -r "$dir${main::SL}mrtg-l.png" and open W, ">$dir${main::SL}mrtg-l.png" ){ - binmode W; - print W unpack ('u', <<'UUENC'); -MB5!.1PT*&@H -24A$4@ #\ 9! , !TA.O' &%!,5$5]9GTT -M79A?8HB(9WFR;6G_=DWM=%35<5R_M[A2 6)+1T0'%F&(ZP 1%)1$%4 -M>-JMDKUOPD ,Q:E4]CH?S0PMG8-.$6L1A*YMJ,DQ%5?[_ON>$Y (2$R>= -MY;-^OF=;GNCM\SFY#_#GW$IU0WMP/#O5HSGNW8"9R)/92$NQ\Z:GUDG/@.@! -M)CP#4H\ >LT>)NB!A0]8,"]&0.(#WY92V<\$=FM4WHV? J -M=$58=%-I9VYA='5R90!D,C(W8S$dir${main::SL}mrtg-m.png" ){ - binmode W; - print W unpack ('u', <<'UUENC'); -MB5!.1PT*&@H -24A$4@ !D 9! , VQYA0 &%!,5$5]9GTT -M79A588QN9(*7:73_=DWL=%31-IC"$4"!0P$>"E&2B:I,%Z((!"(I$)X88H@GJ :A)<$Y@@*I8)YAA">("N( -M%PYD"#L+"IJ#Y4!FN(86N4',# )J"4TO*R\O!_$"@::'@HTEQ /K _&$0+P -M(.T:6@A2@62?H#B*6TRQN#,C9\ "IT15AT4VEG;F%T -M=7)E #1E,S8X-S$P,38Q-S)A96%B.3,Y8SEA,F5D-31B86(U@DWZ,@ ET -M15AT1&5L87D ,C4P(RC.$P YT15AT4&%G90 R-7@R-2LP*S"#D2 ? -) $E%3D2N0F"" -UUENC -close W; - } - if ( ! -r "$dir${main::SL}mrtg-r.png" and open W, ">$dir${main::SL}mrtg-r.png" ){ - binmode W; - print W unpack ('u', <<'UUENC'); -MB5!.1PT*&@H -24A$4@ 80 9! , ##'$3) &%!,5$4T79@\ -M8YQ5=JAPC;>:K\S\_?[5WNFZR-PB%CO! 6)+1T0'%F&(ZP !=5)1$%4 -M>-KM6$ESVE@0;BW 58: K_)"YDH(.%=YL/"511)7O""N3AS!WY_^NM^3Q)+, -M4C55DZJ1*2&]U]O7Z\,4T"]__0_A;US.O\7$$#H*PR=R[_'@#? A9X3KLW,K -MNRX_*L=MGK^#PQ)[$USW+J@'U)E,!JIGS9"+RG0^B!(NDM,ZMB'G,M_?6 -MF@^[_#7D[U)*!V_N!#+4$)>M@+Z(.J/1)PNAI4]=5K# 0_,)'Z(4UXH*A<#/ -M2SQ\3+?[=!F4Q&M?Z!8@2-?4X+O*L^Q\$PPQ;MA.R= 65RRWR/:[U&#PTVR_ -MR4*JI#3?9#E-$U)#W)5*GE&3[Y&!T%P+?QR2)U;UUOB(0@A0"-Z*.AOVC)=^ -MY=L&-T/L"$G !,KBW"V-3PU[.\8W@5N7J)W(?L'&WB5LP6_*X10OP+2L21G/ -MH7OA=#:1&M)F-1FI\^A+8B&H %;A+\U>LP8A)HN^RZOC9^BZY#=#_&10NA8" -M.5E !^R7H/2R=0G!6PH%1\%-!W@<"GU+PN4Q.Z2D6-K!2-;D]-=J"-2H?&C> -M7"B$ABCW."HV-PX@%"6$YHS<3:@.K!$K2;N"4-0A\)X'.R[?%R4$MXQ"TT9, -M7*Y:IP)!8N0L=Z0IVUJH(9!51H%B1)9A]$%,S3QB[_P0 EA;B\ W*KM/%?%Q -M%*@8'$*0K?Y@54'0*+ !XZ@&P;C'2N$W?SU&\BX"Z"YKH08ATBB,^WCHW[U5 -MCK6U$!U'H3$WA9<<1\%+2I8R"H:]@ZT=Q8%9LK1%Z!1AA< XH902XAM@X#<; -MA?;J) HL-F[!KJ*U%KC:DDD -M_NQ\.9M$RD9?TE!AX\HJ8E,+IJE^'Q6+.H1X\K#A;V\FI.B6F=)&B$+E>'9T -M51>-EPD:$VVE;#C>'CJ2:=.FJ?9>1_'20-CQ'_ESAN"5Y?Q4LT'=SFS?^.7* -MYFU%;!(IFTP>!K!Q%=8AL.)7D@8/AL;K:#3A#LW#*>0H> <0(-"]YF!:*>X2 -MUG&GD;D08Z0] ,((4XWG0O;)0-@2UTPW8@V^G0NG46BO^FNB,@I.7A$?EO.W -MK+1)V<4R&H<$E]MR-K7@GD!HB;DL!4G%N>VP;?YJCPE?\''#<:4C!?S7?-IH -MXG%'2N"B.&2Y/^M(*ZE!6PM.]L..5#4997R@5W:5"SWE* JV([U5 -M*6W8H>EBM=_O>9B8CF2C0)M:.1L)@-*(!$_\^+C?S8TF.FJJO;6?& AP20*A -MB6DR[*FS':G[7)5<_,&2F1-N>:+LBJ2^+#Z1Q1?WV02(3PR2/'*IX -M=,_1!P+ %2,A]5Q'TBSR49QN_$P5\?$9Z>2 P=;DXN8B..I(+ 2/'76SBU,P -MC9=6BD28R\^O)=+1&2E2"#/C%CX7M++)J&#SFMP*GD^F,_SCC+.'42'H+;&) -M0LH]YTU8>HM#"!3_GI@4.:R%*_3\U\EC:M+I8_I]-,T&9*3H0:4U/RYG:-+9 -M):7)9D,JX$_9*=,TW>)74"X0F@ Y50B83W?\'H!$"]80*XF;"P2P>%NUJ&2_ -M?-51V7N3)7)?E!]B_$V:OMMJ^,+CZMYPMK??9;'SXBG"J;4C%PBPVX5^T]1" -M6U-M/;K_[#>U>Q4<$?_I=?&SS>NP_(5\X=Z<FT="M4^ 0W1%6'13;V9T=V%R90! *",I26UA9V5-86=I8VL@ -M-"XR+CD@.3DO,#DO,#$@8W)IC9 -M\ "IT15AT4VEG;F%T=7)E #$R,3 -# and Dave Rand -# -# For individual Contributers check the CHANGES file -# -################################################################### -# -# Distributed under the GNU General Public License -# -################################################################### -my @STARTARGS=($0,@ARGV); - -@main::DEBUG=qw(); -# DEBUG TARGETS -# cfg - watch the config file reading -# dir - directory mangeling -# base - basic program flow -# tarp - target parser -# snpo - snmp polling -# snpo2 - more snmp debug -# coca - confcache operations -# repo - track confcache repopulation -# fork - forking view -# time - some timing info -# log - logging of data via rateup or rrdtool -# eval - trace eval experssions -# prof - add timeing info some interesting bits of code - -$main::GRAPHFMT="png"; -# There older perls tend to behave peculiar with -# large integers ... -require 5.005; - -use strict; -# addon jpt -BEGIN { - # Automatic OS detection ... do NOT touch - if ( $^O =~ /^(ms)?(dos|win(32|nt)?)/i ) { - $main::OS = 'NT'; - $main::SL = '\\'; - $main::PS = ';'; - } elsif ( $^O =~ /^NetWare$/i ) { - $main::OS = 'NW'; - $main::SL = '/'; - $main::PS = ';'; - } elsif ( $^O =~ /^VMS$/i ) { - $main::OS = 'VMS'; - $main::SL = '.'; - $main::PS = ':'; - } elsif ( $^O =~ /^os2$/i ) { - $main::OS = 'OS2'; - $main::SL = '/'; - $main::PS = ';'; - } else { - $main::OS = 'UNIX'; - $main::SL = '/'; - $main::PS = ':'; - } - if ( $ENV{LANG} and $ENV{LANG} =~ /UTF.*8/i ){ - my $args = join " ", map { /\s/ ? "\"$_\"" : $_ } @ARGV; - $args ||= ""; - print <) = ($uid,$uid) ; - die "$NOW: ERROR failed to set UID to $uid\n" unless ($< == $uid and $> == $uid); - - $logfile = $opts{logging} || $logfile; - if (defined $logfile){ - setup_loghandlers $logfile; - warn "Started mrtg with config \'$cfgfile\'\n"; - } - - # lets make sure that there are not two mrtgs running in parallel. - # so we lock on the cfg file. Nothing fancy, just a lockfile - - $lockfile = $opts{"lock-file"} || $lockfile; - - if (! defined $lockfile) { - $lockfile = $cfgfile."_l"; - } - if (! defined $templock) { - $templock = $lockfile."_" . $$ ; - } - - debug('base', "Creating Lockfiles $lockfile,$templock"); - &lockit($lockfile,$templock); - - debug('base', "Reading Config File: $cfgfile"); - my $cfgfile_age = -M $cfgfile; - readcfg($cfgfile,\@routers,\%cfg,\%rcfg); - - imggen($cfg{icondir} || $cfg{imagedir} || $cfg{workdir}); - - # Enable or disable snmpv3 - if(defined $cfg{enablesnmpv3}) { - $cfg{enablesnmpv3} = lc($cfg{enablesnmpv3}); - } else { - $cfg{enablesnmpv3} = 'no'; - } - - if ($cfg{threshmailserver}) { - if (eval {local $SIG{__DIE__};require Net::SMTP;}) { - import Net::SMTP; - debug('base', "Loaded Net::SMTP module for ThreshMail."); - } else { - die "$NOW: WARNING: Can't load Net::SMTP module. This is required for ThreshMail."; - } - } - # Check we have the necessary libraries for IPv6 support - if ($cfg{enablesnmpv3} eq 'yes') { - if (eval {local $SIG{__DIE__};require Net_SNMP_util;}) { - import Net_SNMP_util; - debug('base', "SNMP V3 libraries found, SNMP V3 enabled."); - } else { - warn "$NOW: WARNING: SNMP V3 libraries not found, SNMP V3 disabled.\n"; - $cfg{enablesnmpv3} = 'no'; - require SNMP_util; - import SNMP_util; - } - } - else { # load V1/V2 libraries - require SNMP_util; - import SNMP_util; - } - - - # Enable or disable IPv6 - if(defined $cfg{enableipv6}) { - $cfg{enableipv6} = lc($cfg{enableipv6}); - } else { - $cfg{enableipv6} = 'no'; - } - - # Check we have the necessary libraries for IPv6 support - if ($cfg{enableipv6} eq 'yes') { - if ( eval {local $SIG{__DIE__};require Socket; require Socket6; require IO::Socket::INET6;}) { - import Socket; - import Socket6; - debug('base', "IPv6 libraries found, IPv6 enabled."); - } else { - warn "$NOW: WARNING: IPv6 libraries not found, IPv6 disabled.\n"; - $cfg{enableipv6} = 'no'; - } - } - - # Module invocation for sending a copy of the time series data to graphite. - if(defined $cfg{sendtographite}) { - require Net::Graphite; - } - - # from our last run we kept some info about - # the configuration of our devices around - debug('base', "Reading Interface Config cache"); - $confcachefile = $opts{"confcache-file"} || $confcachefile; - if ( !defined($confcachefile) ) { - $confcachefile = $cfgfile; - $confcachefile =~ s/\.[^.\/]+$//; - $confcachefile .= ".ok"; - } - my $confcache = readconfcache($confcachefile); - - # Check the config and create the target object - debug('base', "Checking Config File"); - my @target; - cfgcheck(\@routers, \%cfg, \%rcfg, \@target, \%opts); - - # exit here if we only check the config file - # in case of an error, cfgcheck() already exited - if (defined $opts{check}) { - debug('base', "Remove Lock Files"); - close LOCK; unlink ($templock, $lockfile); - debug('base', "Exit after successful config file check"); - exit 0; - } - - # postload rrdtool support - if ($cfg{logformat} eq 'rrdtool'){ - debug('base', "Loading RRD support"); - require 'RRDs.pm'; - } - - # set the locale - my $LOC; - if ( $cfg{'language'} and defined($lang2tran::LOCALE{"\L$cfg{'language'}\E"})) { - debug('base', "Loading Locale for ".$cfg{'language'}); - $LOC=$lang2tran::LOCALE{"\L$cfg{'language'}\E"}; - } else { - debug('base', "Loading default Locale"); - $LOC=$lang2tran::LOCALE{'default'}; - } - - # Daemon Code - my $last_time=0; - my $curent_time; - my $sleep_time; - if (defined $opts{"daemon"}) { $cfg{'runasdaemon'} = "yes"; } - &demonize_me($pidfile,$cfgfile) if defined $cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i and $MRTG_lib::OS ne 'VMS' - and not (defined $cfg{'nodetach'} and $cfg{'nodetach'} =~ /y/i); - # auto restart on die if running as demon - - $SIG{__DIE__} = sub { - warn $_[0]; - warn "*** Restarting after 10 seconds in an attempt to recover from the error above\n"; - sleep 10; - exec @STARTARGS; - } if $cfg{'runasdaemon'}; - - debug('base', "Starting main Loop"); - do { # Do this loop once for native mode and forever in daemon mode - my $router; - $NOW = timestamp; # get the time - debug 'time', "loop start ".localtime(time); - - #if we run as daemon, we sleep in between collection cycles - $sleep_time= (int($cfg{interval}*60))-(time-$last_time); - if ($sleep_time > 0 ) { #If greater than 0 the sleep that amount of time - debug('time', "Sleep time $sleep_time seconds"); - sleep ($sleep_time); - } elsif ($last_time > 0) { - warn "$NOW: WARNING: data collection did not complete within interval!\n"; - } - $last_time=time; - - # set meta expires if there is an index file - # 2000/05/03 Bill McGonigle - if (defined $cfg{'writeexpires'}) { - my $exp = &expistr($cfg{'interval'}); - my $fil; - $fil = "$cfg{'htmldir'}index.html" if -e "$cfg{'htmldir'}index.html"; - $fil = "$cfg{'htmldir'}index.htm" if -e "$cfg{'htmldir'}index.htm"; - if (defined $fil) { - open(META, ">$fil.meta"); - print META "Expires: $exp\n"; - close(META); - } - } - - - # Use SNMP to populate the target object - debug('base', "Populate Target object by polling SNMP and". - " external Datasources"); - debug 'time', "snmp read start ".localtime(time); - readtargets($confcache,\@target, \%cfg); - - $NOW = timestamp; # get the time - # collect data for each router or pseudo target (`executable`) - debug 'time', "target loop start ".localtime(time); - foreach $router (@routers) { - debug('base', "Act on Router/Target $router"); - if (defined $rcfg{'setenv'}{$router}) { - my $line = $rcfg{'setenv'}{$router}; - while ( $line =~ s/([^=]+)=\"([^\"]*)\"\s*// ) # " - unconfuse the highliter - { - $ENV{$1}=$2; - } - } - my($savetz) = $ENV{'TZ'}; - if (defined $rcfg{'timezone'}{$router}) { - $ENV{'TZ'} = $rcfg{'timezone'}{$router}; - if ( $main::OS eq 'UNIX' ){ - require 'POSIX.pm'; - POSIX::tzset(); - } - } - - my ($inlast, $outlast, $uptime, $name, $time) = - getcurrent(\@target, $router, \%rcfg, \%cfg); - - if ( defined($inlast) and defined($outlast)) { - $EXITCODE = $EXITCODE | 1; - } - else { - $EXITCODE = $EXITCODE | 2; - } - - debug('base', "Get Current values: in:".( defined $inlast ? $inlast : "undef").", out:". - ( defined $outlast? $outlast : "undef").", up:". - ( defined $uptime ? $uptime : "undef").", name:". - ( defined $name ? $name : "undef").", time:". - ( defined $time ? $time : "undef")); - - #abort, if the router is not responding. - if ($cfg{'logformat'} ne 'rrdtool') { - # undefined values are ok for rrdtool ! - #if ( not defined $inlast or not defined $outlast){ - # warn "$NOW: WARNING: Skipping Update of $router, inlast is not defined\n" - # unless defined $inlast; - # warn "$NOW: WARNING: Skipping Update of $router, outlast is not defined\n" - # unless defined $outlast; - # next; - #} - - if (defined $inlast and $inlast < 0) { - $inlast += 2**31; - # this is likely to be a broken snmp counter ... lets compensate - } - if (defined $outlast and $outlast < 0) { - $outlast += 2**31; - # this is likely to be a broken snmp counter ... lets compensate - } - } - - my ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent,$avmxin, $avmxout, - $cuin, $cuout, $cupercent); - debug('base', "Create Graphics"); - if ($rcfg{'options'}{'dorelpercent'}{$router}) { - ($maxin, $maxout, $maxpercent, $avin, $avout, $avpercent, - $cuin, $cuout, $cupercent, $avmxin, $avmxout) = - writegraphics($router, \%cfg, \%rcfg, $inlast, $outlast, $time,$LOC, \%opts); - } else { - ($maxin, $maxout ,$avin, $avout, $cuin, $cuout, $avmxin, $avmxout) = - writegraphics($router, \%cfg, \%rcfg, $inlast, $outlast, $time,$LOC, \%opts); - } - # skip this update if we did not get anything usefull out of - # writegraphics - next if not defined $maxin; - - debug('base', "Check for Thresholds"); - threshcheck(\%cfg,\%rcfg,$cfgfile,$router,$cuin,$cuout); - - if (defined $opts{'log-only'}){ - debug('base', "Disable Graph and HTML generation"); - } - - if ($cfg{logformat} eq 'rateup' and not defined $opts{'log-only'} ){ - debug('base', "Check for Write HTML Pages"); - writehtml($router, \%cfg, \%rcfg, - $maxin, $maxout, $maxpercent, $avin, $avout, $avmxin, $avmxout, $avpercent, - $cuin, $cuout, $cupercent, $uptime, $name, $LOC) - } - - # - clonedirectory($router,\%cfg, \%rcfg); - # - - #put TZ things back in shape ... - if ($savetz) { - $ENV{'TZ'} = $savetz; - } else { - delete $ENV{'TZ'}; - } - if ( $main::OS eq 'UNIX' ){ - require 'POSIX.pm'; - POSIX::tzset(); - }; - } - # Has the cfg file been modified since we started? if so, reload it. - if ( -M $cfgfile < $cfgfile_age and - $cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i ) { - # reload the configuration - $cfgfile_age = -M $cfgfile; - debug('base', "Re-reading Config File: $cfgfile"); - @routers = (); %cfg = (); %rcfg = (); - readcfg($cfgfile,\@routers,\%cfg,\%rcfg); - cfgcheck(\@routers, \%cfg, \%rcfg, \@target, \%opts); - } - debug('base', "End of main Loop"); - } while ($cfg{'runasdaemon'} and $cfg{'runasdaemon'} =~ /y/i ); #In daemon mode run forever - debug('base', "Exit main Loop"); - # OK we are done, remove the lock files ... - - debug('base', "Remove Lock Files"); - close LOCK; unlink ($templock, $lockfile); - - debug('base', "Store Interface Config Cache"); - delete $$confcache{___updated} if exists $$confcache{___updated}; # make sure everything gets written out not only the updated entries - writeconfcache($confcache,$confcachefile); - - if ( ! $cfg{'runasdaemon'} or $cfg{'runasdaemon'} !~ /y/i ) { - if ( ($EXITCODE & 1) and ($EXITCODE & 2) ) { - # At least one target was sucessful - exit 91; - } - elsif ( not ($EXITCODE & 1) and ($EXITCODE & 2) ) { - # All targets failed - exit 92; - } - } -} - -# ( $inlast, $outlast, $uptime, $name, $time ) = -# &getcurrent( $target, $rou, $rcfg, $cfg ) -# Calculate monitored data for device $rou based on information in @$target -# and referring to configuration data in %$rcfg and %$cfg. In the returned -# list, $inlast and $outlast are the input and output monitored data values, -# $uptime is the device uptime, $name is the device name, and $time is the -# current time when the calculation was performed. -sub getcurrent { - my( $target, $rou, $rcfg, $cfg ) = @_; - # Hash indexed by $mode for conveniently saving $inlast and $outlast - my %last; - # Initialize uptime, device name, and data collection time to empty strings - my $uptime = ''; - my $name = ''; - my $time = ''; - - # Calculate input and output monitored data - foreach my $mode( qw( _IN_ _OUT_ ) ) { - # Initialize monitored data, warning message, and death message - # to empty strings - my $data; - my $warning; - my $death; - { - # Code block used to calculate monitoring data - # Localize warning and death exception handlers to capture - # error message less any leading and trailing white space - local $SIG{ __WARN__ } = - sub { $_[0] =~ /^\s*(.+?)\s*$/; $warning = $1; }; - local $SIG{ __DIE__ } = - sub { $_[0] =~ /^\s*(.+?)\s*$/; $death = $1; }; - # Calculate monitoring data. $rcfg->{ target }{ $rou } contains - # a Perl expression for the calculation. - $data = eval "$rcfg->{target}{$rou}"; - } - # Test for various exceptions occurring in the calculation - if( $warning ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (warn): $warning\n"; - $data = undef; - } elsif( $death ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (kill): $death\n"; - $data = undef; - } elsif( $@ ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' (eval): $@\n"; - $data = undef; - } elsif( not defined $data ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' did not eval into defined data\n"; - $data = undef; - } elsif( $data and $data !~ /^[-+]?\d+(\.\d*)?([eE][+-]?[0-9]+)?$/ ) { - warn "$NOW: ERROR: Target[$rou][$mode] '$$rcfg{target}{$rou}' evaluated to '$data' instead of a number\n"; - $data = undef; - } elsif( length( $data ) > 190 ) { - warn "$NOW: ERROR: $mode value: '$data' is way to long ...\n"; - $data = undef; - } else { - # At this point data is considered valid. Round to an integer - # unless RRDTool is in use and this is a gauge - if (not ( $cfg->{ logformat } eq 'rrdtool' - and defined $rcfg->{ options }{ gauge }{ $rou })){ - if (ref $data and ref $data eq 'Math::BigFloat') { - $data->ffround( 0 ) - } else { - $data = sprintf "%.0f", $data; - } - } - # Remove any leading plus sign - $data =~ s/^\+//; - } - $last{ $mode } = $data; - } - - # Set $u to the unique index of the @$target array referred to in the - # monitored data calculation for the current device. $u will be set to - # -1 if that index is not unique. - my $u = $rcfg->{ uniqueTarget }{ $rou }; - - # Get the uptime, device name, and data collection time from the @$target - # array if the monitored data calculation refers only to one target. - # Otherwise it doesn't make sense to do this. - if( $u >= 0 ) { - $uptime = $target->[ $u ]{ _UPTIME_ }; - $name = $target->[ $u ]{ _NAME_ }; - $time = $target->[ $u ]{ _TIME_ }; - - if ($time =~ /^([-0-9.]+)$/) { - $time = $1; - } - - } - - # Set the time to the current time if it was not set above - $time = time unless $time; - - # Cache uptime location for reading name - my( $uploc ); - - # Get the uptime and device name from the alternate location (community@host or - # (OID:community@host or OID) that may have been specified with the RouterUptime - # target keyword - if( defined $rcfg->{ routeruptime }{ $rou } ) { - my( $noid, $nloc ) = split( /:/, $rcfg->{ routeruptime }{ $rou }, 2 ); - # If only location (community@host) was specified then - # move the location details into the right place - if( $noid =~ /@/ ) { - $nloc = $noid; - $noid = undef; - } - # If no OID (community@host) was specified use the hardcoded default - if( not $noid ) { - $noid = 'sysUptime'; - } - # If no location (community@host) was specified use values from the - # unique target referred to in the monitored data calculation - if( not $nloc ){ - if ($u >= 0) { - my $comm = $target->[ $u ]{ Community }; - my $host = $target->[ $u ]{ Host }; - my $opt = $target->[ $u ]{ SnmpOpt }; - $nloc = "$comm\@$host$opt"; - } else { - die "$NOW: ERROR: You must specify the location part of the RouterUptime oid for non unique targets! ($rou)\n"; - } - } - - $uploc = $nloc; - # Get the device uptime if $noid(OID) and $nloc (community@host) have been specified - # one way or the other - debug('base', "Fetching sysUptime and sysName from: $noid:$nloc"); - ( $uptime, $name ) = snmpget( $uploc, $rcfg->{ snmpoptions }{ $rou }, $noid, 'sysName'); - } - - # Get the device name from the alternate location (OID or - # OID:community@host) that may have been specified with the RouterName - # target keyword - if( defined $rcfg->{ routername }{ $rou } ) { - my( $noid, $nloc ) = split( /:/, $rcfg->{ routername }{ $rou }, 2 ); - # If no location (community@host) was specified use values from the - # unique target referred to in the monitored data calculation - if( $u >= 0 and not $nloc ) { - my $comm = $target->[ $u ]{ Community }; - my $host = $target->[ $u ]{ Host }; - my $opt = $target->[ $u ]{ SnmpOpt }; - $nloc = "$comm\@$host$opt"; - } - # Get the location from the RouterUptime keyword if that is defined - # and $nloc has not otherwise been specified - $nloc = $uploc if $uploc and not $nloc; - # Get the device name if $nloc (community@host) has been specified - # one way or the other - debug('base', "Fetching sysName from: $noid:$nloc"); - ( $name ) = snmpget( $nloc, $$rcfg{snmpoptions}{ $rou }, $noid ) if $nloc; - } - - return ( $last{ _IN_ }, $last{ _OUT_ }, $uptime, $name, $time ); -} - -sub rateupcheck ($) { - my $router = shift; - if ($?) { - my $value = $?; - my $signal = $? & 127; #ignore the most significant bit - #as it is always one when it is a returning - #child says dave ... - if (($MRTG_lib::OS ne 'UNIX') || ($signal != 127)) { - my $exitval = $? >> 8; - warn "$NOW: WARNING: rateup died from Signal $signal\n". - " with Exit Value $exitval when doing router '$router'\n". - " Signal was $signal, Returncode was $exitval\n" - } - } -} - -sub clonedirectory { - my($router,$cfg, $rcfg) = @_; - require File::Copy; - import File::Copy; - - return unless ( $$rcfg{'clonedirectory'}{$router} ); - - my ($clonedstdir, $clonedsttarget ,$srcname, $dstname); - - ($clonedstdir, $clonedsttarget ) = split (/,|\s+/, $$rcfg{'clonedirectory'}{$router}) if ( $$rcfg{'clonedirectory'}{$router} =~ /,|\S\s+\S/ ); - - if ( defined $clonedsttarget ) { - $clonedsttarget =~ s/\s+//; - $clonedsttarget = lc($clonedsttarget); - } else { - $clonedstdir = $$rcfg{'clonedirectory'}{$router}; - } - if ( $$rcfg{'directory'}{$router} ne $clonedstdir) { - $clonedstdir =~ s/\s+$//; - $clonedstdir .= "/" unless ($clonedstdir =~ /\/$/); - my $fullpathsrcdir = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}"; - my $fullpathdstdir = "$$cfg{'logdir'}$clonedstdir"; - - die "$NOW: ERROR: Destination dir: $fullpathdstdir not found for cloning process\n" unless ( -e $fullpathdstdir ); - die "$NOW: ERROR: Destination dir: $fullpathdstdir is not a directory destination for cloning process\n" unless ( -d $fullpathdstdir ); - die "$NOW: ERROR: Destination dir: $fullpathdstdir is not writeable for cloning process\n" unless ( -w $fullpathdstdir ); - - if ( defined $clonedsttarget ) { - debug('base', "Clone directory $fullpathsrcdir to $fullpathdstdir " . - "renaming target $router to $clonedsttarget"); - } else { - debug('base', "Clone directory $fullpathsrcdir to $fullpathdstdir"); - } - - foreach my $srcfile (<$fullpathsrcdir$router\[.-\]*>) { - debug('base', "copying $srcfile $fullpathdstdir"); - copy("$srcfile","$fullpathdstdir") or warn "$NOW: WARNING: Cloning $srcfile to $fullpathdstdir unsuccessful; $!\n"; - if ($srcfile =~ /\.html/i) { - debug('base', "altering $fullpathdstdir/$router.$$rcfg{'extension'}{$router}"); - # - my $dirrel = "../" x ($$rcfg{'clonedirectory'}{$router} =~ tr|/|/|); - # - debug('base', "dirrel $dirrel $clonedstdir"); - open(HTML,"$fullpathdstdir/$router.$$rcfg{'extension'}{$router}"); - my @CLONEHTML = ; - close(HTML); - foreach ( @CLONEHTML ) { - if ( defined $clonedsttarget and /$router/ ) { - debug('base', "altering $router to $clonedsttarget in html file"); - s/$router/$clonedsttarget/i; - } - if ( /SRC=/i and /$$rcfg{'directory'}{$router}/ ) { - debug('base', "altering from $_"); - s|(\.\./)+|$dirrel|; - s|$$rcfg{'directory'}{$router}|$clonedstdir|; - debug('base', "altering to $_"); - } - } - open(HTML,">$fullpathdstdir/$router.$$rcfg{'extension'}{$router}"); - print HTML $_ for ( @CLONEHTML ); - close(HTML); - } - if ( defined $clonedsttarget ) { - $srcfile =~ /.+\/(.+)?$/; - $srcname = $1; - $dstname = $srcname; - $dstname =~ s/$router/$clonedsttarget/; - debug('base', "Clone renaming $srcname to $dstname at $fullpathdstdir"); - rename("$fullpathdstdir/$srcname","$fullpathdstdir/$dstname") or - warn "$NOW: WARNING: Renaming $fullpathdstdir/$srcname to $fullpathdstdir/$dstname unsuccessful; $!\n"; - } - } - } else { - warn "$NOW: WARNING: Cloning to the same place suspended. ; $!\n"; - } -} - -sub writegraphics { - my($router, $cfg, $rcfg, $inlast, $outlast, $time,$LOC, $opts) = @_; - - my($absmax,$maxv, $maxvi, $maxvo, $i, $period, $res); - my(@exec, @mxvls, @metas); - my(%maxin, %maxout, %maxpercent, %avin, %avout, %avmxin, %avmxout, %avpercent, %cuin, %cuout, %cupercent); - my($rrdinfo); - - @metas = (); - $maxvi = $$rcfg{'maxbytes1'}{$router}; - $maxvo = $$rcfg{'maxbytes2'}{$router}; - if ($maxvi > $maxvo) { - $maxv = $maxvi; - } else { - $maxv = $maxvo; - } - $absmax = $$rcfg{'absmax'}{$router}; - $absmax = $maxv unless defined $absmax; - if ($absmax < $maxv) { - die "$NOW: ERROR: AbsMax: $absmax is smaller than MaxBytes: $maxv\n"; - } - - - # select whether the datasource gives relative or absolute return values. - my $up_abs="u"; - $up_abs='m' if defined $$rcfg{'options'}{'perminute'}{$router}; - $up_abs='h' if defined $$rcfg{'options'}{'perhour'}{$router}; - $up_abs='d' if defined $$rcfg{'options'}{'derive'}{$router}; - $up_abs='a' if defined $$rcfg{'options'}{'absolute'}{$router}; - $up_abs='g' if defined $$rcfg{'options'}{'gauge'}{$router}; - - my $dotrrd = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.rrd"; - my $dotlog = "$$cfg{'logdir'}$$rcfg{'directory'}{$router}$router.log"; - my $reallog = $$cfg{logformat} eq 'rrdtool' ? $dotrrd : $dotlog; - - if (defined $$cfg{maxage} and -e $reallog and time()-$$cfg{maxage} > (stat($reallog))[9]){ - warn "$NOW: ERROR: skipping update of $router. As $reallog is older than MaxAge ($$cfg{maxage} s)\n"; - return undef; - } - - if ($$cfg{logformat} eq 'rrdtool') { - debug('base',"start RRDtool section"); - # make sure we got some sane default here - my %dstype = qw/u COUNTER a ABSOLUTE g GAUGE h COUNTER m COUNTER d DERIVE/; - $up_abs = $dstype{$up_abs}; - # update the database. - - # set minimum/maximum values. use 'U' if we cannot get good values - # the lower bound is hardcoded to 0 - my $absi = $maxvi; - my $abso = $maxvo; - $absi = $abso = $$rcfg{'absmax'}{$router} - if defined $$rcfg{'absmax'}{$router}; - debug('base',"maxi:$absi, maxo:$abso"); - $absi = 'U' if $absi == 0; - $abso = 'U' if $abso == 0; - # check to see if we already have an RRD file or have to create it - # maybe we can convert an .log file to the new rrd format - if( $RRDs::VERSION >= 1.4 and $$cfg{rrdcached} and $$cfg{rrdcached} !~ /^unix:/ ) { - # rrdcached in network mode. No log conversion possible. - # In this mode, we cannot use absolute paths. So, we strip logdir from the name. - $dotrrd = "$$rcfg{'directory'}{$router}$router.rrd"; - if( $RRDs::VERSION < 1.49 ) { - # This version of RRD doesnt support info, create and tune - debug('base',"Unable to verify RRD file with this version of rrdcached"); - if( !$main::verified_rrd{$dotrrd} ) { - warn "WARN: Unable to verify $dotrrd with this version of RRDTool\n"; - $main::verified_rrd{$dotrrd} = 1; - } - } elsif( !$main::verified_rrd{$dotrrd} ) { - # Test to see if it exists - debug('base',"Attempting to verify RRD file via rrdcached"); - $rrdinfo = RRDs::info($dotrrd,'--daemon',$$cfg{rrdcached},'--noflush'); - if(!$rrdinfo) { # doesnt exist, or cannot be accessed - my $e = RRDs::error(); - warn "$NOW: Cannot access $dotrrd; will attempt to (re)create it: $e\n" if $e; - - # don't fail if interval is not set - my $interval = $$cfg{interval}; - my $minhb = int($$cfg{interval} * 60)*2; - $minhb = 600 if ($minhb <600); - my $rows = $$rcfg{'rrdrowcount'}{$router} || int( 4000 / $interval); - my $rows30m = $$rcfg{'rrdrowcount30m'}{$router} || 800; - my $rows2h = $$rcfg{'rrdrowcount2h'}{$router} || 800; - my $rows1d = $$rcfg{'rrdrowcount1d'}{$router} || 800; - my @args = ($dotrrd, '-b', $time-10, '-s', int($interval * 60), - "DS:ds0:$up_abs:$minhb:0:$absi", - "DS:ds1:$up_abs:$minhb:0:$abso", - "RRA:AVERAGE:0.5:1:$rows", - ( $interval < 30 ? ("RRA:AVERAGE:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:AVERAGE:0.5:".int(120/$interval).":".$rows2h, - "RRA:AVERAGE:0.5:".int(1440/$interval).":".$rows1d, - "RRA:MAX:0.5:1:$rows", - ( $interval < 30 ? ("RRA:MAX:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:MAX:0.5:".int(120/$interval).":".$rows2h, - "RRA:MAX:0.5:".int(1440/$interval).":".$rows1d); - # do we have holt winters rras defined here ? - if (defined $$rcfg{'rrdhwrras'} and defined $$rcfg{'rrdhwrras'}{$router}){ - push @args, split(/\s+/, $$rcfg{'rrdhwrras'}{$router}); - } - push @args,"--daemon", $$cfg{rrdcached}; - - debug('base',"create $dotrrd via rrdcached"); - debug('log', "RRDs::create(".join(',',@args).")"); - RRDs::create(@args); - $e = RRDs::error(); - die "$NOW: ERROR: Cannot create RRD ".join(',',@args)."- $e\n" if $e; - } else { - # Does the RRD file need to be tuned? - if( - ($rrdinfo->{"ds[ds0].max"} != $absi) - ||($rrdinfo->{"ds[ds1].max"} != $abso) - ||($rrdinfo->{"ds[ds0].type"} ne $up_abs) - ||($rrdinfo->{"ds[ds1].type"} ne $up_abs) - ) { - debug('base',"RRD file needs to be tuned"); - warn "$NOW: RRDFile $dotrrd needs to be tuned but cannot do this remotely.\n"; - } - } - $main::verified_rrd{$dotrrd} = 1; - } else { - debug('base',"No need to verify this file again"); - } - } elsif (-e $dotlog and not -e $dotrrd) { - debug('base',"converting $dotlog to RRD format"); - if(defined $RRDs::VERSION and $RRDs::VERSION < 1.000271){ - die "$NOW: ERROR: RRDtool version 1.0.27 or later required to perform log2rrd conversion\n"; - } - log2rrd($router,$cfg,$rcfg); - } elsif (! -e $dotrrd) { - #nope it seems we have to create a new one - debug('base',"create $dotrrd"); - # create the rrd if it doesn't exist - # don't fail if interval is not set - my $interval = $$cfg{interval}; - my $minhb = int($$cfg{interval} * 60)*2; - $minhb = 600 if ($minhb <600); - my $rows = $$rcfg{'rrdrowcount'}{$router} || int( 4000 / $interval); - my $rows30m = $$rcfg{'rrdrowcount30m'}{$router} || 800; - my $rows2h = $$rcfg{'rrdrowcount2h'}{$router} || 800; - my $rows1d = $$rcfg{'rrdrowcount1d'}{$router} || 800; - my @args = ($dotrrd, '-b', $time-10, '-s', int($interval * 60), - "DS:ds0:$up_abs:$minhb:0:$absi", - "DS:ds1:$up_abs:$minhb:0:$abso", - "RRA:AVERAGE:0.5:1:$rows", - ( $interval < 30 ? ("RRA:AVERAGE:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:AVERAGE:0.5:".int(120/$interval).":".$rows2h, - "RRA:AVERAGE:0.5:".int(1440/$interval).":".$rows1d, - "RRA:MAX:0.5:1:$rows", - ( $interval < 30 ? ("RRA:MAX:0.5:".int(30/$interval).":".$rows30m):()), - "RRA:MAX:0.5:".int(120/$interval).":".$rows2h, - "RRA:MAX:0.5:".int(1440/$interval).":".$rows1d); - # do we have holt winters rras defined here ? - if (defined $$rcfg{'rrdhwrras'} and defined $$rcfg{'rrdhwrras'}{$router}){ - push @args, split(/\s+/, $$rcfg{'rrdhwrras'}{$router}); - } - - debug('log', "RRDs::create(".join(',',@args).")"); - RRDs::create(@args); - my $e = RRDs::error(); - die "$NOW: ERROR: Cannot create RRD ".join(',',@args)."- $e\n" if $e; - } elsif ( -M $dotrrd > 0 ) { - # update the minimum/maximum according to maxbytes/absmax - # and (re)set the data-source-type to reflect cfg changes - # cost: 1 read/write cycle, but update will reuse the buffered data - # in daemon mode this will only happen in the first round - my @args = ($dotrrd, '-a', "ds0:$absi", '-a', "ds1:$abso", - '-d', "ds0:$up_abs", '-d', "ds1:$up_abs"); - debug('log', "RRDs::tune(@args)"); - my $start = gettimeofday(); - RRDs::tune(@args); - debug('prof',sprintf("RRDs::tune $dotrrd - %.3fs",gettimeofday()-$start)); - my $e = RRDs::error(); - warn "$NOW: ERROR: Cannot tune logfile: $e\n" if $e; - } - # update the rrd - $inlast = 'U' unless defined $inlast and $inlast =~ /\S/ and $inlast ne '##UNDEF##'; - $outlast = 'U' unless defined $outlast and $outlast =~ /\S/ and $outlast ne '##UNDEF##'; - debug('log', "RRDs::update($dotrrd, '$time:$inlast:$outlast')"); - my $start = gettimeofday(); - my $rrddata = 0; - if ( $RRDs::VERSION >= 1.4 and $$cfg{rrdcached} ){ - RRDs::update($dotrrd, '--daemon', $$cfg{rrdcached}, "$time:$inlast:$outlast"); - debug('prof',sprintf("RRDs::update $dotrrd (rrdcached) - %.3fs",gettimeofday()-$start)); - $rrddata = \{ dummy => "" }; - } elsif ( $RRDs::VERSION >= 1.2 ){ - $rrddata=RRDs::updatev($dotrrd, "$time:$inlast:$outlast"); - debug('prof',sprintf("RRDs::updatev $dotrrd - %.3fs",gettimeofday()-$start)); - } else { - RRDs::update($dotrrd, "$time:$inlast:$outlast"); - debug('prof',sprintf("RRDs::update $dotrrd - %.3fs",gettimeofday()-$start)); - } - my $e = RRDs::error(); - warn "$NOW: ERROR: Cannot update $dotrrd with '$time:$inlast:$outlast' $e\n" if ($e); - - - # Send a copy of the time series data to graphite - if(defined $$cfg{sendtographite}) { - my @a = split ",",$$cfg{sendtographite}; - if (not $graphiteObj) { - $graphiteObj = Net::Graphite->new( - host => $a[0], - port => $a[1], - trace => 0, - proto => "tcp", - timeout => 1, - fire_and_forget => 1, - return_connect_error => 0 - ); - if($graphiteObj->connect) { - debug('log',"successfully opened graphite socket ($graphiteObj)\n"); - } else { - debug('log',"graphite connect error: $!\n"); - } - } - - # make a copy of the router name for use in the graphite namespace - my $graphiterouter = $router; - # periods are delimiters in the graphite namespace so change them to underscores - $graphiterouter =~ s/_/\./g; - # commas are not allowed - $graphiterouter =~ s/,//g; #remove commas - #set up MRTG's 'in' var naming for graphite - my $graphitein; - if ($$rcfg{'legendi'}{$router}) { - $graphitein = $$rcfg{'legendi'}{$router}; - # if the 'in' var contains an underscore, change to a period so graphite will delimit on it - $graphitein =~ s/_/\./g; - } else { - # if legendi is not specified, just use 'in' - $graphitein = "in"; - } - - #set up MRTG's 'out' var naming for graphite - my $graphiteout; - if ($$rcfg{'legendo'}{$router}) { - $graphiteout = $$rcfg{'legendo'}{$router}; - # if the 'out' var contains an underscore, change to a period so graphite will delimit on it - $graphiteout =~ s/_/\./g; - } else { - # if legendo is not specified, just use 'out' - $graphiteout = "out"; - } - - my $graphitensprefix="m2g"; - - # verify that the 'in' value is numeric (ints, floats, +, -) , and send to graphite - if($inlast =~ /^[+-]?([0-9]*[.])?[0-9]+$/) { - # send the 'in' var data to graphite, and log it appropriately - debug('log', "graphite->send($graphitensprefix.$graphiterouter.$graphitein,$inlast,$time)\n"); - $graphiteObj->send( - path => "$graphitensprefix.$graphiterouter.$graphitein", - value => "$inlast", - time => $time, - ); - } else { - debug('log', "WARNING - skipped graphite->send($graphitensprefix.$graphiterouter.$graphitein,$inlast,$time) because value ($inlast) is non-numeric\n"); - } - - # verify that the 'out' value is numeric (ints, floats, +, -) , and send to graphite - if($outlast =~ /^[+-]?([0-9]*[.])?[0-9]+$/) { - # send the 'out' var data to graphite, and log it appropriately - debug('log', "graphite->send($graphitensprefix.$graphiterouter.$graphiteout,$outlast,$time)\n"); - $graphiteObj->send( - path => "$graphitensprefix.$graphiterouter.$graphiteout", - value => "$outlast", - time => $time, - ); - } else { - debug('log', "WARNING - skipped graphite->send($graphitensprefix.$graphiterouter.$graphiteout,$outlast,$time) because value ($outlast) is non-numeric\n"); - } - } - # done sending to graphite for this iteration - - - - if ( $RRDs::VERSION < 1.2 ){ - # get the rrdtool-processed values back from rrdtool - # for the threshold checks (we cannot use the fetched data) - $start = gettimeofday(); - my $info = RRDs::info($dotrrd); - debug('prof',sprintf("RRDs::info $dotrrd - %.3fs",gettimeofday()-$start)); - my $lasttime = $info->{last_update} - $info->{last_update} % $info->{step}; - debug('log', "RRDs::info($dotrrd)"); - $e = RRDs::error(); - warn "$NOW: ERROR: Cannot 'info' $dotrrd: $e\n" if ($e); - $start = gettimeofday(); - my $fetch = (RRDs::fetch($dotrrd,'AVERAGE','-s',$lasttime-1,'-e',$lasttime))[3]; - debug('prof',sprintf("RRDs::fetch $dotrrd - %.3fs",gettimeofday()-$start)); - $e = RRDs::error(); - warn "$NOW: ERROR: Cannot 'fetch' $dotrrd: $e\n" if ($e); - debug('log', "RRDs::fetch($dotrrd,'AVERAGE','-s',$lasttime,'-e',$lasttime)"); - $cuin{d}{$router} = $fetch->[0][0]; - $cuout{d}{$router} = $fetch->[0][1]; - } elsif ( $RRDs::VERSION >= 1.4 and $$cfg{rrdcached} ){ - # Cannot check thresholds - } elsif($rrddata) { - my $utime = $time - ($time % int($cfg->{interval}*60)); - $cuin{d}{$router} = $rrddata->{"[$utime]RRA[AVERAGE][1]DS[ds0]"}; - $cuout{d}{$router} = $rrddata->{"[$utime]RRA[AVERAGE][1]DS[ds1]"}; - $cuin{d_hwfail}{$router} = $rrddata->{"[$utime]RRA[FAILURES][1]DS[ds0]"}; - $cuout{d_hwfail}{$router} = $rrddata->{"[$utime]RRA[FAILURES][1]DS[ds1]"}; - } - my $in = defined $cuin{d}{$router} ? $cuin{d}{$router} : "???" ; - my $out = defined $cuout{d}{$router} ? $cuout{d}{$router} : "???" ; - debug('log', " got: $in/$out"); - # the html pages and the graphics are created at "call time" so that's it! - # (the returned hashes are empty, it's just to minimize the changes to mrtg) - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - return (\%maxin, \%maxout, \%maxpercent, \%avin, \%avout, \%avpercent, \%cuin, \%cuout, \%cupercent, \%avmxin, \%avmxout); - } - return (\%maxin, \%maxout, \%avin, \%avout, \%cuin, \%cuout, \%avmxin, \%avmxout ); - - } - ########## rrdtool users have left here ############### - - ((($MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2') and (-e "${FindBin::Bin}${MRTG_lib::SL}rateup.exe")) or - (($MRTG_lib::OS eq 'NW') and (-e "SYS:/Mrtg/bin/rateup.nlm")) or - (-x "${FindBin::Bin}${MRTG_lib::SL}rateup")) or - die "$NOW: ERROR: Can't Execute '${FindBin::Bin}${MRTG_lib::SL}rateup'\n"; - - # rateup does not know about undef so we make inlast and outlast ready for rateup - #warn "$NOW: ERROR: inlast is undefined. Skipping $router\n" unless defined $inlast; - #warn "$NOW: ERROR: outlast is undefined. Skipping $router\n" unless defined $outlast; - #return undef unless defined $inlast and defined $outlast; - - - # set values to -1 to tell rateup about unknown values - $inlast = -1 unless defined $inlast; - $outlast = -1 unless defined $outlast; - - # untaint in and out - if ($inlast =~ /^([-0-9.]+)$/) { - $inlast = $1; - } - if ($outlast =~ /^([-0-9.]+)$/) { - $outlast = $1; - } - - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", - "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router", - $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z', - "$up_abs"."p", $inlast, $outlast, $absmax, - "C", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router}, - $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router}, - $$rcfg{'rgb5'}{$router}); - } else { - - @exec = ("${FindBin::Bin}${MRTG_lib::SL}rateup", - "$$cfg{'logdir'}$$rcfg{'directory'}{$router}","$router", - $time, $$rcfg{'options'}{'unknaszero'}{$router} ? '-z':'-Z', - "$up_abs", $inlast, $outlast, $absmax, - "c", $$rcfg{'rgb1'}{$router},$$rcfg{'rgb2'}{$router}, - $$rcfg{'rgb3'}{$router},$$rcfg{'rgb4'}{$router}); - } - - # If this list grows anymore would it be more efficient to have an - # array to look up the command line option to send to rateup rather - # than have a long list to check? - push (@exec, '-t') if defined $$rcfg{'options'}{'transparent'}{$router}; - push (@exec, '-0') if defined $$rcfg{'options'}{'withzeroes'}{$router}; - push (@exec, '-b') if defined $$rcfg{'options'}{'noborder'}{$router}; - push (@exec, '-a') if defined $$rcfg{'options'}{'noarrow'}{$router}; - push (@exec, '-i') if defined $$rcfg{'options'}{'noi'}{$router}; - push (@exec, '-o') if defined $$rcfg{'options'}{'noo'}{$router}; - push (@exec, '-l') if defined $$rcfg{'options'}{'logscale'}{$router}; - push (@exec, '-x') if defined $$rcfg{'options'}{'expscale'}{$router}; - push (@exec, '-m') if defined $$rcfg{'options'}{'secondmean'}{$router}; - push (@exec, '-p') if defined $$rcfg{'options'}{'printrouter'}{$router}; - - my $maxx = $$rcfg{'xsize'}{$router}; - my $maxy = $$rcfg{'ysize'}{$router}; - my $xscale = $$rcfg{'xscale'}{$router}; - my $yscale = $$rcfg{'yscale'}{$router}; - my $growright = 0+($$rcfg{'options'}{'growright'}{$router} or 0); - my $bits = 0+($$rcfg{'options'}{'bits'}{$router} or 0); - my $integer = 0+($$rcfg{'options'}{'integer'}{$router} or 0); - my $step = 5*60; - my $rop; - my $ytics = $$rcfg{'ytics'}{$router}; - my $yticsf= $$rcfg{'yticsfactor'}{$router}; - my $timestrfmt = $$rcfg{'timestrfmt'}{$router}; - my $timestrpos = ${MRTG_lib::timestrpospattern}{uc $$rcfg{'timestrpos'}{$router}}; - - if (not defined $$rcfg{'ylegend'}{$router}){ - if ($bits){ - $$rcfg{'ylegend'}{$router} = &$LOC("Bits per minute") - if defined $$rcfg{'options'}{'perminute'}{$router}; - $$rcfg{'ylegend'}{$router} = &$LOC("Bits per hour") - if defined $$rcfg{'options'}{'perhour'}{$router}; - } else { - $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per minute") - if defined $$rcfg{'options'}{'perminute'}{$router}; - $$rcfg{'ylegend'}{$router} = &$LOC("Bytes per hour") - if defined $$rcfg{'options'}{'perhour'}{$router}; - } - } - - if ($$rcfg{'ylegend'}{$router}) { - push (@exec, "l", "[$$rcfg{'ylegend'}{$router}]"); - } - my $sign = ($$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /d/) ? 1 : -1; - - if ($$rcfg{'pngtitle'}{$router}) { - push (@exec, "T", "[$$rcfg{'pngtitle'}{$router}]"); - } - - if ($$rcfg{'timezone'}{$router}) { - push (@exec, "Z", "$$rcfg{'timezone'}{$router}"); - } - - if ($$rcfg{'kilo'}{$router}) { - push (@exec, "k", $$rcfg{'kilo'}{$router}); - } - if ($$rcfg{'kmg'}{$router}) { - push (@exec, "K", $$rcfg{'kmg'}{$router}); - } - if ($$rcfg{'weekformat'}{$router}) { - push (@exec, "W", $$rcfg{'weekformat'}{$router}); - } - my $SAGE = (time - $main::STARTTIME) / 3600 / 24; # current script age - if (not defined $$opts{'log-only'}){ - if (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /d/) { - # VMS: should work for both now - push (@exec, "i", "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, ,$xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - @mxvls = ("d"); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-day.${main::GRAPHFMT}", - $$cfg{'interval'}); - } - - - if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") or - ((-M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}") + $SAGE >= 0.5/24)) and - (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~/w/) - ) { - $step=30*60; - $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /w/) ? 1 : -1; - push (@mxvls , "w"); - $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /w/) ? "p" : "i"; - push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-week.${main::GRAPHFMT}", 30); - } - - - if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") or - (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}") + $SAGE >= 2/24)) and - (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~ /m/)) { - $step=2*60*60; - $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /m/) ? 1 : -1; - push (@mxvls , "m"); - $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /m/) ? "p" : "i"; - push (@exec, $rop ,"$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-month.${main::GRAPHFMT}", 120); - } - - if (((not -e "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") or - (( -M "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}") + $SAGE >= 1)) and - (not defined $$rcfg{'suppress'}{$router} or $$rcfg{'suppress'}{$router} !~/y/)) { - $step=24*60*60; - $sign = (defined $$rcfg{'unscaled'}{$router} and $$rcfg{'unscaled'}{$router} =~ /y/) ? 1 : -1; - push (@mxvls , "y"); - $rop =(defined $$rcfg{'withpeak'}{$router} and $$rcfg{'withpeak'}{$router} =~ /y/) ? "p" : "i"; - push (@exec, $rop, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}", - $sign*$maxvi, $sign*$maxvo, $maxx, $maxy, $xscale, $yscale, $growright, $step, $bits, $ytics, $yticsf, $timestrfmt, $timestrpos); - push (@metas, "$$cfg{'imagedir'}$$rcfg{'directory'}{$router}${router}-year.${main::GRAPHFMT}", 1440); - } - } - - # VMS: this might work now ... or does VMS NOT know about pipes? - # NT doesn't have fork() so an open(xxx,"-|") won't work - # OS2 fork() have bug with socket handles. In RunAsDaemon mode it fail - # after first loop (with "socket operation on non socket" message. - - if ($MRTG_lib::OS eq 'VMS' or $MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2'){ - map { s/"/\\"/; $_ = '"'.$_.'"' if /\s/ } @exec; - open (RATEUP, join (" ", @exec)."|") or - do { - warn "$NOW: WARNING: rateup (".(join " ", @exec ). - ") did not work: $!\n"; - return; - } - } elsif ($MRTG_lib::OS eq 'NW'){ - map { s/"/\\"/; $_ = '"'.$_.'"' if /\s/ } @exec; - - # Stuff around because of Perl problems. - - open (NWPARMS, ">"."$$cfg{'imagedir'}$router.dat") or - do { - warn "$NOW: WARNING: Rateup parameters [$$cfg{'imagedir'}$router.dat] [open] failed.\n"; - - return; - }; - print NWPARMS join (" ", @exec); - close NWPARMS; - - # Now run Rateup with path to Parameters. - - open (RATEUP, "SYS:/Mrtg/bin/rateup -f $$cfg{'imagedir'}$router.dat"."|") or - do { - warn "$NOW: WARNING: SYS:/Mrtg/bin/rateup -f $$cfg{'imagedir'}$router.dat did NOT work.\n"; - - return; - } - } else { - $! = undef; - open (RATEUP,"-|") or - do { exec @exec or - warn "$NOW: WARNING: rateup (".(join " ", @exec ). - ") did not work: $!\n"; - }; - - } - - debug('log', join(" ", @exec)); - - - if (open (HTML,"<$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.$$rcfg{'extension'}{$router}")) { - for ($i=0 ; $i<200 ; $i++) { - last if eof(HTML); - $_= ; - if (/' . "\n"; - print HTML '' . "\n"; - print HTML "\t" . '' . "\n"; - print HTML "\t\t" . '' . "\n"; - print HTML "\t\t" . '' . "\n"; -# Not functional in HTML file in modern web browsers - use HTTP header instead -# -# -# -# -# - print HTML "\t\t" . '' . "$$rcfg{'title'}{$router}" . '' . "\n"; - foreach $peri (qw(d w m y)) { - print HTML <<"TEXT"; - - -TEXT - if ($$rcfg{'options'}{'dorelpercent'}{$router} and defined $$maxpercent{$peri}{$router}) { - print HTML <<"TEXT"; - -TEXT - } - print HTML <<"TEXT"; - - -TEXT - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - print HTML <<"TEXT"; - -TEXT - } - print HTML "\n" - if defined $$cuin{$peri}{$router}; - print HTML "\n" - if defined $$cuout{$peri}{$router}; - - if ($$rcfg{'options'}{'dorelpercent'}{$router} and $$cupercent{$peri}{$router} ) { - print HTML <<"TEXT"; - -TEXT - } - print HTML <<"TEXT" if $$avmxin{$peri}{$router} and $$avmxout{$peri}{$router}; - - -TEXT - - } - - $namestring = "'$name'" if $name; - - defined $$rcfg{backgc}{$router} or $$rcfg{backgc}{$router} = "#fff"; - - $$rcfg{'rgb1'}{$router} = "" unless defined $$rcfg{'rgb1'}{$router}; - $$rcfg{'rgb2'}{$router} = "" unless defined $$rcfg{'rgb2'}{$router}; - $$rcfg{'rgb3'}{$router} = "" unless defined $$rcfg{'rgb3'}{$router}; - $$rcfg{'rgb4'}{$router} = "" unless defined $$rcfg{'rgb4'}{$router}; - $$rcfg{'rgb5'}{$router} = "" unless defined $$rcfg{'rgb5'}{$router}; - $$rcfg{'rgb6'}{$router} = "" unless defined $$rcfg{'rgb6'}{$router}; - - print HTML " "; - - # allow for \n in addhead - defined $$rcfg{addhead}{$router} or $$rcfg{addhead}{$router} = ""; - defined $$rcfg{pagetop}{$router} or $$rcfg{pagetop}{$router} = ""; - - if (defined $$rcfg{bodytag}{$router}) { - if ($$rcfg{bodytag}{$router} !~ / -"; - print HTML " -$$rcfg{bodytag}{$router} - $$rcfg{'pagetop'}{$router} - "; - print HTML "

"; - if (defined $$rcfg{'timezone'}{$router}){ - print HTML - &$LOC("The statistics were last updated $Today $$rcfg{'timezone'}{$router}"); - } else { - print HTML - &$LOC("The statistics were last updated $Today"); - } - if ($uptime and ! $$rcfg{options}{noinfo}{$router}) { - print HTML - ",
\n". - &$LOC("at which time $namestring had been up for $uptime.") - } - print HTML "

"; - my %sample= ('d' => "`Daily' Graph (".$interval.' Minute', - 'w' => "`Weekly' Graph (30 Minute", - 'm' => "`Monthly' Graph (2 Hour", - 'y' => "`Yearly' Graph (1 Day"); - - my %full = ('d' => 'day', - 'w' => 'week', - 'm' => 'month', - 'y' => 'year'); - - my $InCo; - if (!(defined $$rcfg{'options'}{'noi'}{$router})) { - if (exists $$rcfg{'legendi'}{$router}) { - if ($$rcfg{'legendi'}{$router} ne "") { - $InCo=$$rcfg{'legendi'}{$router}; - } - } else { - $InCo=&$LOC("In"); - } - } - - my $OutCo; - if (!(defined $$rcfg{'options'}{'noo'}{$router})) { - if (exists $$rcfg{'legendo'}{$router}) { - if ($$rcfg{'legendo'}{$router} ne "") { - $OutCo=$$rcfg{'legendo'}{$router}; - } - } else { - $OutCo=&$LOC("Out"); - } - } - my $PercentCo; - if (defined $$rcfg{'legend5'}{$router}) { - if ($$rcfg{'legend5'}{$router} ne "") { - $PercentCo=$$rcfg{'legend5'}{$router}; - } - } else { - $PercentCo=&$LOC("Percentage"); - } - - foreach $peri (qw(d w m y)) { - next if defined $$rcfg{'suppress'}{$router} and $$rcfg{'suppress'}{$router} =~/$peri/; - my $gifw; - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router}+ - +100+30) *$$rcfg{'xzoom'}{$router}); - } else { - $gifw=sprintf("%.0f",($$rcfg{'xsize'}{$router}*$$rcfg{'xscale'}{$router} - +100) *$$rcfg{'xzoom'}{$router}); - } - my $gifh=sprintf("%.0f",($$rcfg{'ysize'}{$router}*$$rcfg{'yscale'}{$router}+35) - *$$rcfg{'yzoom'}{$router}); - - # take the image directory away from the html directory to give us relative links - - - my $imagepath = ( $cfg->{htmldir} ne $cfg->{imagedir} ) ? "$dirrel$$cfg{imagehtml}$$rcfg{directory_web}{$router}" : ""; - print HTML " - -
-

".&$LOC("$sample{$peri}").&$LOC(' Average)')."

- \"$full{$peri}\" - - - - - - - "; - my(@foo)=($rcfg,$LOC); - print HTML " - - - - - - " if $InCo; - print HTML " - - - - - - " if $OutCo; - print HTML " - - - - - - " if ($$rcfg{'options'}{'dorelpercent'}{$router} and $PercentCo); -print HTML " - - - " if ($$rcfg{'options'}{'avgpeak'}{$router} and $InCo and $OutCo); - print HTML " -
" . &$LOC("Max") . "" . &$LOC("Average") . "" . &$LOC("Current") . "
" . $InCo . "".&fmi($$maxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."".&fmi($$avin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)." ".&fmi($$cuin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo)."
" . $OutCo . "".&fmi($$maxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." ".&fmi($$avout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)." ".&fmi($$cuout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo)."
" . $PercentCo . "".sprintf("%0.1f %%",($$maxpercent{$peri}{$router} || 0))." ".sprintf("%0.1f %%",($$avpercent{$peri}{$router} || 0 ))." ".sprintf("%0.1f %%",($$cupercent{$peri}{$router} || 0 ))."
- " . &$LOC("Average max 5 min values for $sample{$peri} interval):") . " - $InCo " . &fmi($$avmxin{$peri}{$router}, $$rcfg{'maxbytes1'}{$router}, $router, @foo) . "/ - $OutCo " . &fmi($$avmxout{$peri}{$router}, $$rcfg{'maxbytes2'}{$router}, $router, @foo) . " -
-
-\n"; -} - - if (!(defined $$rcfg{'options'}{'nolegend'}{$router})) { - print HTML " - -
- "; - print HTML " - - - - " if $InCo; - print HTML " - - - - " if $OutCo; - if ($$rcfg{'withpeak'}{$router}) { - print HTML " - - - - " if $InCo; - print HTML " - - - - " if $OutCo; - } - if ($$rcfg{'options'}{'dorelpercent'}{$router}) { - print HTML " - - - - "; - } - print HTML " -
$$rcfg{'col1'}{$router} ###$leg1
$$rcfg{'col2'}{$router} ###$leg2
$$rcfg{'col3'}{$router} ###$leg3
$$rcfg{'col4'}{$router} ###$leg4
$$rcfg{'col5'}{$router} ###$leg5
-
- -"; - } - - if (!(defined $$rcfg{'options'}{'nobanner'}{$router})) { - my $gifPath; - - if (defined $$cfg{icondir}) { - $gifPath = $$cfg{icondir}; - #lets make sure there is a trailing path separator - $gifPath =~ s|/*$|/|; - } else { - $gifPath = "$dirrel$$cfg{imagehtml}"; - } - - print HTML< - - -TEXT - } - - print HTML $$rcfg{'pagefoot'}{$router} if defined $$rcfg{'pagefoot'}{$router}; - print HTML < - - -TEXT - close HTML; - - if (defined $$cfg{'writeexpires'} and $$cfg{'writeexpires'} =~ /^y/i) { - open(HTMLG, ">$$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.". - "$$rcfg{'extension'}{$router}.meta") || - do { - warn "$NOW: WARNING: Writing $$cfg{'htmldir'}$$rcfg{'directory'}{$router}$router.". - "$$rcfg{'extension'}{$router}.meta: $!\n"; - next - }; - - print HTMLG "Expires: $expiration\n"; - close(HTMLG); - } -} - - -sub printusage { - print < - -mrtg-2.17.7 - Multi Router Traffic Grapher - -Copyright 1995-2006 by Tobias Oetiker -Licensed under the Gnu GPL. - -If you want to know more about this tool, you might want -to read the docs. You can find everything on the -mrtg website: - -http://oss.oetiker.ch/mrtg/ - -USAGEDESC - exit(1); -} - - -sub lockit { - my ($lockfile,$templock) = @_; - if ($MRTG_lib::OS eq 'VMS' or $MRTG_lib::OS eq 'NT' or $MRTG_lib::OS eq 'OS2') { - # too sad NT and VMS can't do links we'll do the diletants lock - if (-e $lockfile and not unlink $lockfile) { - my($lockage) = time()-(stat($lockfile))[9]; - die "$NOW: ERROR: I guess another mrtg is running. A lockfile ($lockfile)\n". - " aged $lockage seconds is hanging around and I can't remove\n". - " it because another process is still using it."; - } - - open (LOCK, ">$lockfile") or - die "$NOW: ERROR: Creating lockfile $lockfile: $!\n"; - print LOCK "$$\n"; - close LOCK; - open (LOCK, "<$lockfile") or - die "$NOW: ERROR: Reading lockfile $lockfile for owner check: $!\n"; - my($read)=; - chomp($read); - die "$NOW: ERROR: Someone else just got the lockfile $lockfile\n" - unless $$ == $read; - } else { - # now, lets do it the UNIX way ... Daves work ... - open(LOCK,">$templock") or die "$NOW: ERROR: Creating templock $templock: $!"; - $main::Cleanfile = $templock; - if (!link($templock,$lockfile)) { # Lock file exists - deal with it. - my($nlink,$lockage) = (stat($lockfile))[3,9]; - $lockage = time() - $lockage; - if ($nlink < 2 or $lockage > 30*60) { #lockfile is alone and old - unlink($lockfile) - || do{ unlink $templock; - die "$NOW: ERROR: Can't unlink stale lockfile ($lockfile). Permissions?\n"}; - link($templock,$lockfile) - || do{ unlink $templock; - die "$NOW: ERROR: Can't create lockfile ($lockfile).\n". - "Permission problem or another mrtg locking succesfully?\n"}; - } else { - unlink $templock; - die "$NOW: ERROR: It looks as if you are running two copies of mrtg in parallel on\n". - " the same config file. There is a lockfile ($lockfile) and it is\n". - " is only $lockage seconds old ... Check your crontab.\n". - " (/etc/crontab and /var/spool/cron/root) \n" - if $lockage < 4; - - die "$NOW: ERROR: I guess another mrtg is running. A lockfile ($lockfile) aged\n". - "$lockage seconds is hanging around. If you are sure that no other mrtg\n". - "is running you can remove the lockfile\n"; - - } - - } - } -} - -sub threshmail ($$$$){ - my $server = shift; - my $from = shift; - my $to = shift; - my $message = shift; - debug('base',"sending threshmail from $from to $to"); - my $smtp = Net::SMTP->new([split /\s*,\s*/, $server],Timeout=>5) or - do { warn "$NOW: ERROR: could not send thresholdmail to $to"; return }; - $smtp->mail($from); - $smtp->to(split(/\s*,\s*/, $to)); - $smtp->data(); - $smtp->datasend($message); - $smtp->dataend(); - $smtp->quit; -} - -sub threshcheck { - # threshold checking by Tom Muggli - # ... fsck'd up but fixed by Juha Laine - my ($cfg,$rcfg,$cfgfile,$router,$cuin,$cuout) = @_; - my $threshfile; - my %cu = ( i=> $cuin, o=>$cuout ); - # are we going to keep state ? - if (defined $$cfg{'threshdir'}){ - ensureSL(\$$cfg{'threshdir'}); - $threshfile = $$cfg{'threshdir'}.(split /\Q$MRTG_lib::SL\E/, $cfgfile)[-1].".$router"; - } - - # setup environment for external scripts - if (defined $rcfg->{'threshdesc'}{$router}) { - $ENV{THRESH_DESC}=$rcfg->{'threshdesc'}{$router}; - } else { - delete $ENV{THRESH_DESC}; - } - if (defined $rcfg->{'hwthreshdesc'}{$router}) { - $ENV{HWTHRESH_DESC}=$rcfg->{'hwthreshdesc'}{$router}; - } else { - delete $ENV{HWTHRESH_DESC}; - } - - for my $dir (qw(i o)){ # in and out - my %thresh = ( - thresh => $cu{$dir}{d}{$router}, - - # if we are looking at an rrd with holtwinters RRAs - # we get a failures count. - hwthresh => $cu{$dir}{d_hwfail}{$router} - ); - for my $type (keys %thresh){ - for my $bound (qw(min max)){ - # it gets set inside the loop because threshval gets modified further - # down in the code - my $threshval = $thresh{$type}; - next if not defined $threshval; - - my $boundval = $rcfg->{$type.$bound.$dir}{$router}; - next unless defined $boundval; - - my $realval = ""; - my $realthresh = ""; - - if ($boundval =~ s/%$//) { # defined in % of maxbytes - # 2 decimals in % - $realval = "% ($threshval)"; - $realthresh = "% (".sprintf("%.1f",($rcfg->{"maxbytes".($dir eq 'i' ? 1 : 2)}{$router} * $boundval / 100)).")"; - $threshval = sprintf "%.1f", ($threshval / $rcfg->{"maxbytes".($dir eq 'i' ? 1 : 2)}{$router} * 100); # the new code - } - my $msghead = ""; - $msghead = "From: $cfg->{threshmailsender}\nTo: $rcfg->{${type}.'mailaddress'}{$router}" - if $rcfg->{${type}.'mailaddress'}{$router} and $cfg->{threshmailsender}; - my $pagetop = $rcfg->{pagetop}{$router} || ''; - $pagetop =~ s|

.*?

||; - $pagetop =~ s|\s*\s*(.*?)\s*(.*?)\s*\s*|$1 $2\n|g; - $pagetop =~ s|\s*<.+?>\s*\n?||g; - - my $msgbody = <{title}{$router} - - Target: $router - Type: $type -Direction: $dir - Bound: $bound -Threshold: $boundval$realthresh - Current: $threshval$realval - -$pagetop - -MESSAGE - $msgbody .= "\n$rcfg->{$type.'desc'}{$router}\n" if $rcfg->{$type.'desc'}{$router}; - - - if (($bound eq 'min' and $boundval > $threshval) or - ($bound eq 'max' and $boundval < $threshval)) { - # threshold was broken... - my $message = <{$type.'prog'.$dir}{$router}, $router, - $rcfg->{$type.$bound.$dir}{$router}, $threshval,($rcfg->{$type.'desc'}{$router} ||"No Description")); - - # Check if we use the status file or not... - if ( defined $threshfile ) { - if ( not -e $threshfile.".".$type.$bound.uc($dir) ) { - # Create a file to indicate a threshold problem for the time after the problem - open THRESHTOUCH, ">".$threshfile.".".$type.$bound.uc($dir) - or warn "$NOW: WARNING: Creating $threshfile.".$bound.uc($dir).": $!\n"; - close THRESHTOUCH; - if (defined $rcfg->{$type.'prog'.$dir}{$router}){ - debug('base',"run threshprog$dir: ".(join ",",@exec)); - system @exec; - } - threshmail $cfg->{threshmailserver},$cfg->{threshmailsender},$rcfg->{$type.'mailaddress'}{$router},$message - if $rcfg->{$type.'mailaddress'}{$router} - } - else { - debug('base',"NOT acting on BROKEN threshold since $threshfile.$type$bound$dir exists"); - } - } elsif ( not defined $cfg->{$type.'hyst'} or - ($bound eq 'min' and $boundval - $cfg->{$type.'hyst'}* $boundval < $threshval) or - ($bound eq 'max' and $boundval + $cfg->{$type.'hyst'}* $boundval > $threshval) - ) { - # no threshold dir so run on every 'break' - if (defined $rcfg->{$type.'prog'.$dir}{$router}){ - debug('base',"run ${type}prog$dir: ".(join ",",@exec)); - system @exec; - } - threshmail $cfg->{threshmailserver},$cfg->{threshmailsender},$rcfg->{$type.'mailaddress'}{$router},$message - if $rcfg->{$type.'mailaddress'}{$router}; - } - } else { - # no threshold broken ... - my @exec = ( $rcfg->{$type.'progok'.$dir}{$router}, $router, - $rcfg->{$type.$bound.$dir}{$router}, $threshval); - my $message = <{$type.$bound.$dir}{$router} vs $threshval) - -Threshold UN-BROKEN -------------------- -$msgbody -MESSAGE - - # Check if we use the status file or not... - if ( defined $threshfile ) { - if ( -e $threshfile.".".$type.$bound.uc($dir) ){ - unlink "$threshfile.".$type.$bound.uc($dir); - if (defined $rcfg->{$type.'progok'.$dir}{$router}){ - debug('base',"run ${type}progok$dir: ".(join ",",@exec)); - system @exec; - } - threshmail $cfg->{threshmailserver},$cfg->{threshmailsender},$rcfg->{$type.'mailaddress'}{$router},$message - if $rcfg->{$type.'mailaddress'}{$router}; - } - } - } - } # for my $bound ... - } # for my $type - } # for my $dir -} - -sub getexternal ($) { - my $command = shift; - my $in=undef; - my $out=undef; - my $uptime="unknown"; - my $name="unknown"; - - open (EXTERNAL , $command."|") - or warn "$NOW: WARNING: Running '$command': $!\n"; - - warn "$NOW: WARNING: Could not get any data from external command ". - "'".$command. - "'\nMaybe the external command did not even start. ($!)\n\n" if eof EXTERNAL; - - chomp( $in=) unless eof EXTERNAL; - chomp( $out=) unless eof EXTERNAL; - chomp( $uptime=) unless eof EXTERNAL; - chomp( $name=) unless eof EXTERNAL; - - close EXTERNAL; - - # strip returned date - $uptime =~ s/^\s*(.*?)\s*/$1/; - $name =~ s/^\s*(.*?)\s*/$1/; - - # do we have numbers in the external programs answer ? - if ( not defined $in ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'in' but nothing'\n\n"; - } elsif ( $in eq 'UNKNOWN' ) { - $in = undef; - } elsif ( $in !~ /([-+]?\d+(.\d+)?)/ ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'in' but got '$in'\n\n"; - $in = undef; - } else { - $in = $1; - } - - if ( not defined $out ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'out' but nothing'\n\n"; - } elsif ( $out eq 'UNKNOWN' ) { - $out = undef; - } elsif ( $out !~ /([-+]?\d+(.\d+)?)/ ) { - warn "$NOW: WARNING: Problem with External get '$command':\n". - " Expected a Number for 'out' but got '$out'\n\n"; - $out = undef; - } else { - $out = $1; - } - debug('snpo',"External result:".($in||"undef")." out:".($out||"undef")." uptime:".($uptime||"undef")." name:".($name||"undef")); - return ($in,$out,time,$uptime,$name); -} - -sub getsnmparg ($$$$){ - my $confcache = shift; - my $target = shift; - my $cfg = shift; - my $populated = shift; - my $retry = 0; - - my $hostname = $$target{Host}; - my $hostkey = "$$target{Community}\@$$target{Host}$$target{SnmpOpt}"; - - if ($$target{ipv4only}) { - if (not ( $hostname =~ /^\d+\.\d+\.\d+\.\d+$/ or gethostbyname $hostname) ){ - warn "$NOW: WARNING: Skipping host $hostname as it does not resolve to an IPv4 address\n"; - return 'DEADHOST'; - } - } else { - if($hostname =~ /^\[(.*)\]$/) { - # Numeric IPv6 address. Check that it's valid - $hostname = substr($hostname, 1); - chop $hostname; - if(! inet_pton(AF_INET6(), $hostname)) { - warn "$NOW: WARNING: Skipping host $hostname: invalid IPv6 address\n"; - return 'DEADHOST'; - } - } else { - # Hostname. Look it up - my @res; - my ($too,$port,$otheropts) = split(':', $$target{SnmpOpt}, 3); - $port = 161 unless defined $port; - @res = getaddrinfo($hostname, $port, Socket::AF_UNSPEC(), Socket::SOCK_DGRAM()); - if (scalar (@res) < 5) { - warn "$NOW: WARNING: Skipping host $hostname as it does not resolve to an IPv4 or IPv6 address\n"; - return 'DEADHOST'; - } - } - } - RETRY: - my @ifnum = (); - my @OID = (); - # Find apropriate Interface to poll from - for my $i (0..1) { - if ($$target{IfSel}[$i] eq 'If') { - $ifnum[$i] = ".".$$target{Key}[$i]; - debug('snpo',"simple If: $ifnum[$i]"); - } elsif($$target{IfSel}[$i] eq 'None') { - $ifnum[$i] = ""; - } else { - $$target{Key}[$i] =~ s/\s+$//; # no trainling whitespace in keys ... - - if (not defined readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i])) { - debug('snpo',"($i) Populate ConfCache for $$target{Host}$$target{SnmpOpt}"); - populateconfcache($confcache,"$$target{Community}\@$$target{Host}$$target{SnmpOpt}",$$target{ipv4only},1,$$target{snmpoptions}); - $$populated{$hostname} = 1; # set cache population to true for this cycle and host - } - if (not defined readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i])) { - warn "$NOW: WARNING: Could not match host:'$$target{Community}\@$$target{Host}$$target{SnmpOpt}' ref:'$$target{IfSel}[$i]' key:'$$target{Key}[$i]'\n"; - return 'NOMATCH'; - } else { - $ifnum[$i] = ".".readfromcache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i]); - debug('snpo',"($i) Confcache Match $$target{Key}[$i] -> $ifnum[$i]"); - } - } - if ($ifnum[$i] !~ /^$|^\.\d+$/) { - warn "$NOW: WARNING: Can not determine". - " ifNumber for $$target{Community}\@$$target{Host}$$target{SnmpOpt} \tref: '$$target{IfSel}[$i]' \tkey: '$$target{Key}[$i]'\n"; - return 'NOMATCH'; - } - } - for my $i (0..1) { - # add ifget methodes call for a cross check; - for ($$target{IfSel}[$i]) { - /^Eth$/ && do { - push @OID, "ifPhysAddress".$ifnum[$i]; last - }; - /^Ip$/ && do { - push @OID, "ipAdEntIfIndex".".".$$target{Key}[$i];last - }; - /^Descr$/ && do { - push @OID, "ifDescr".$ifnum[$i]; last - }; - /^Type$/ && do { - push @OID, "ifType".$ifnum[$i]; last - }; - /^Name$/ && do { - push @OID, "ifName".$ifnum[$i]; last - }; - } - push @OID ,$$target{OID}[$i].$ifnum[$i]; - } - # we also want to know uptime and system name unless we are - if ( not defined $$cfg{nomib2} and $$cfg{logformat} ne 'rrdtool' ) { - if ( $OID[0] !~ /^cache.+$/ and - $OID[0] !~ /^\Q1.3.6.1.4.1.3495.1\E/ ) { - push @OID, qw(sysUptime sysName); - } else { - push @OID, qw(cacheUptime cacheSoftware cacheVersionId) - } - } - - # pull that data - debug('snpo',"SNMPGet from $$target{Community}\@$$target{Host}$$target{SnmpOpt} -- ".(join ",", @OID)); - my @ret; - - # make sure we have no error messages hanging round. - - $SNMP_Session::errmsg = undef; - $Net_SNMP_util::ErrorMessage = undef; - - my $targtemp = $$target{Community}.'@'.$$target{Host}.$$target{SnmpOpt}; - $targtemp = v4onlyifnecessary($targtemp, $$target{ipv4only}); - - my @snmpoids = grep !/^(Pseudo|WaLK|GeTNEXT|CnTWaLK)|IndexPOS/, @OID; - - if (defined $$cfg{singlerequest}){ -#LH local $BER::pretty_print_timeticks = 0; - foreach my $oid (@snmpoids){ - push @ret, snmpget($targtemp,$$target{snmpoptions},$oid); - } - } else { - @ret = snmpget($targtemp,$$target{snmpoptions},@snmpoids); - } - my @newret; - for (@OID) { - /^PseudoZero$/ && do { push @newret, 0; next; }; - /^PseudoOne$/ && do { push @newret, 1; next; }; - s/^WaLK(\d*)// && do { my $idx = $1 || 0; my $oid=$_;push @newret, (split /:/, (snmpwalk($targtemp,$$target{snmpoptions},$oid))[$idx],2)[1]; - debug('snpo',"snmpwalk '$oid' -> ".($newret[-1]||'UNDEF'));next}; - s/^GeTNEXT// && do { my $oid=$_;push @newret, (split /:/, snmpgetnext($targtemp,$$target{snmpoptions},$oid),2)[1]; - debug('snpo',"snmpgetnext '$oid' -> ".($newret[-1]||'UNDEF'));next}; - s/^CnTWaLK// && do { my $oid=$_;my @insts= (snmpwalk($targtemp,$$target{snmpoptions},$_)); - undef @insts if( $insts[1] || '') =~/no/i; push @newret, scalar @insts; - debug('snpo',"snmpCountwalk '$oid' -> ".($newret[-1]||'UNDEF'));next}; - /IndexPOS.*\.(\d*)/ && do { my $idx=$1; s/IndexPOS/$idx/; s/\.\d*$//; push @newret, snmpget($targtemp,$$target{snmpoptions},$_); - debug('snpo', "snmpget of oid '$_' after replacement of IndexPOS"); next}; - push @newret, shift @ret; - } - @ret = @newret; - debug('snpo',"SNMPfound -- ".(join ", ", map {"'".($_||"undef")."'"} @ret)); - $ret[-2] = $ret[-2].' '.$ret[-1] if $OID[-1] and $OID[-1] eq 'cacheVersionId'; - my $time = time; - my @final; - # lets do some reality check - for my $i (0..1) { - # some ifget methodes call for a cross check; - for ($$target{IfSel}[$i]) { - /^Eth$/ && do { - my $bin = shift @ret || 0xff; - my $eth = unpack 'H*', $bin; - my @eth; - while ($eth =~ s/^..//){ - push @eth, $&; - } - my $phys=join '-', @eth; - if ($phys ne $$target{Key}[$i]) { - debug('snpo', "($i) eth if crosscheck got $phys expected $$target{Key}[$i]"); - if (not $retry) { - $retry=1; - # remove broken entry - storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef); - debug('repo',"($i) goto RETRY force if cache repopulation"); - goto RETRY; - } else { - warn "$NOW: WARNING: could not match&get". - " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for Eth $$target{Key}[$i]\n"; - return 'NOMATCH'; - } - }; - debug ('snpo',"($i) Eth crosscheck OK"); - }; - /^Ip$/ && do { - my $if = shift @ret || 'none'; - if ($ifnum[$i] ne '.'.$if) { - debug('repo', "($i) IP if crosscheck got .$if expected $ifnum[$i]"); - if (not $retry) { - $retry=1; - # remove broken entry - storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef); - debug('repo',"($i) goto RETRY force if cache repopulation"); - goto RETRY; - } else { - warn "$NOW: WARNING: could not match&get". - " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for IP $$target{Key}[$i]\n"; - return 'NOMATCH'; - } - } - debug ('snpo',"($i) IP crosscheck OK"); - }; - /^(Descr|Name|Type)$/ && do { - my $descr = shift @ret || 'Empty'; - $descr =~ s/[\0- ]+$//; # remove excess spaces and stuff - if ($descr ne $$target{Key}[$i]) { - debug('repo', "($i) $_ if crosscheck got $descr expected $$target{Key}[$i]"); - if (not $retry) { - $retry=1; - # remove broken entry - storeincache($confcache,$hostkey,$$target{IfSel}[$i],$$target{Key}[$i],undef); - debug('repo',"($i) goto RETRY force if cache repopulation"); - goto RETRY; - } else { - warn "$NOW: WARNING: could not match&get". - " $$target{Host}$$target{SnmpOpt}/$$target{OID}[$i] for $_ '$$target{Key}[$i]'\n"; - return 'NOMATCH'; - } - } - debug ('snpo',"($i) $_ crosscheck OK"); - }; - } - # no sense continuing here ... if there is no data ... - if (defined $SNMP_Session::errmsg and $SNMP_Session::errmsg =~ /no response received/){ - $SNMP_Session::errmsg = undef; - warn "$NOW: WARNING: skipping because at least the query for $OID[0] on $$target{Host} did not succeed\n"; - return 'DEADHOST'; - } - if (defined $Net_SNMP_util::ErrorMessage and $Net_SNMP_util::ErrorMessage =~ /No response from remote/){ - $Net_SNMP_util::ErrorMessage = undef; - warn "$NOW: WARNING: skipping because at least the query for $OID[0] on $$target{Host} did not succeed\n"; - return 'DEADHOST'; - } - if ($$target{OID}[$i] =~ /if(Admin|Oper)Hack/) { - push @final, ((shift @ret) == 1) ? 1:0; - } else { - push @final, shift @ret; - } - } - - my @res = ( @final,$time, @ret); - - # Convert in and out values to integers with a user-defined subroutine - # specified by the Conversion target key - if( $target->{ Conversion } ) { - foreach my $ri( 0..1 ) { - next unless defined $res[ $ri ]; - local $SIG{__DIE__}; - my $exp = "&MRTGConversion::$target->{ Conversion }( \$res[\$ri] )"; - $res[ $ri ] = eval $exp; - warn "$NOW: WARNING: evaluation of \"$exp\" failed\n$@\n" if $@; - } - } - - # have some cleanup first, it seems that some agents - # are adding newlines to what they return - map{ $_ =~ s/\n|\r//g if defined $_ } @res; - map{ $_ =~ s/^\s+//g if defined $_ } @res; - map{ $_ =~ s/\s+$//g if defined $_ } @res; - - # in and out should be numbers only - for my $ri (0..1){ - # for folks using rrdtool I am allowing numbers - # with decimals here - if ( defined $res[$ri] and $res[$ri] !~ /^[-+]?\d+(.\d+)?$/ ) { - warn "$NOW: WARNING: Expected a number but got '$res[$ri]'\n"; - $res[$ri] = undef; - - } - } - return @res; - } - - -# read target function ... -sub readtargets ($$$) { - my ($confcache,$target,$cfg) = @_; - my $forks = $$cfg{forks}; - my $trgnum = $#{$target}+1; - if (defined $forks and $forks > 1 and $trgnum > 1){ - $forks = $trgnum if $forks > $trgnum; - my $split = int($trgnum / $forks) + 1; - my @hand; - # get them forks to work ... - for (my $i = 0; $i < $forks;$i++) { - local *D; - my $sleep_count=0; - my $pid; - do { - $pid = open(D, "-|"); - unless (defined $pid) { - warn "$NOW: WARNING cannot fork: $!\n"; - die "$NOW: ERROR bailing out after 6 failed forkattempts" - if $sleep_count++ > 6; - sleep 10; - } - } until defined $pid; - if ($pid) { # parent - $hand[$i] = *D; # funky file handle magic ... - debug ('fork',"Parent $$ after fork of child $i"); - } else { # child - debug ('fork',"Child $i ($$) after fork"); - my $res = ""; - my %deadhost; - my %populated; - for (my $ii = $i * $split; - $ii < ($i+1) * $split and $ii < $trgnum; - $ii++){ - my $targ = $$target[$ii]; - my @res; - if ($$targ{Methode} eq 'EXEC') { - @res = getexternal($$targ{Command}); - } else { # parent - if (not $deadhost{$$targ{Community}.$$targ{Host}}) { - @res = getsnmparg($confcache,$targ,$cfg,\%populated); - if ( $res[0] and $res[0] eq 'DEADHOST') { - # guess we got a blank here - @res = ( undef,undef,time,undef,undef); - $deadhost{$$targ{Community}.$$targ{Host}} = 1; - warn "$NOW: WARNING: no data for $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}\@$$targ{Host}. Skipping further queries for Host $$targ{Host} in this round.\n" - } elsif ($res[0] and $res[0] eq 'NOMATCH'){ - @res = (undef,undef,time,undef,undef); - } - } else { - @res = ( undef,undef,time,undef,undef); - } - } - - for (my $iii=0;$iii<5;$iii++){ - if (defined $res[$iii]){ - $res .= "$res[$iii]\n"; - } else { - $res .= "##UNDEF##\n"; - } - } - } - debug ('fork',"Child $i ($$) waiting to deliver"); - print $res; # we only talk after the work has been done to - # otherwhise we might get blocked - # return updated hosts from confcache - writeconfcache($confcache,'&STDOUT') - if defined $$confcache{___updated}; - exit 0; - } - - } - # happy reaping ... - my $vin =''; # vector of pipe file-descriptors from children - for (my $i = 0; $i < $forks;$i++) { - vec($vin, fileno($hand[$i]), 1) = 1; - } - my $left = $forks; - while ($left) { - my $rout = $vin; # read vector - my $eout = $vin; # exception vector - my $nfound = select($rout, undef, $eout, undef); # no timeout - if (1 > $nfound) { - die sprintf("$NOW: ERROR: select returned %d: $!\n", $nfound); - } - for (my $i = 0; $i < $forks; $i++) { - next unless defined $hand[$i] and defined fileno($hand[$i]); -# this does not seem to work reliably -# if (vec($eout, fileno($hand[$i]), 1)) { -# die "$NOW: ERROR: fork $i has died ahead of time?\n"; -# } - next unless vec($rout, fileno($hand[$i]), 1); - - vec($vin, fileno($hand[$i]), 1) = 0; # remove this child fd - - debug ('fork',"Parent reading child $i"); - my $h = $hand[$i]; - for (my $ii = $i * $split; - $ii < ($i+1) * $split and $ii < $trgnum; - $ii++){ - my $targ = $$target[$ii]; - my @res; - for (0..4){ - my $line = <$h>; # must be a simple scalar here else it wont work - die "$NOW: ERROR: fork $i has died ahead of time ...\n" if not defined $line; - chomp $line; - # debug ('fork',"reading for $ii $line"); - $line = undef if $line eq "##UNDEF##"; - push @res,$line; - }; - - ($$targ{_IN_}, - $$targ{_OUT_}, - $$targ{_TIME_}, - $$targ{_UPTIME_}, - $$targ{_NAME_}) = @res; - if ($] >= 5.0061){ - $$targ{_IN_} = Math::BigFloat->new($$targ{_IN_}) if $$targ{_IN_}; - $$targ{_OUT_} = Math::BigFloat->new($$targ{_OUT_}) if $$targ{_OUT_}; - } - } - # feed confcache entries - my $lasthost =""; - while (<$h>){ - chomp; - my ($host,$method,$key,$value) = split (/\t/, $_); - if ($host ne $lasthost){ - debug ('fork',"start clearing confcache on first entry for target $host"); - clearfromcache($confcache,$host); - debug ('fork',"finished clearing confcache"); - } - $lasthost = $host; - storeincache($confcache,$host,$method,$key,$value); - } - close $h; - --$left; - } - } - - } else { - my %deadhost; - my %populated; - foreach my $targ (@$target) { - if ($$targ{Methode} eq 'EXEC') { - debug('snpo', "run external $$targ{Command}"); - ($$targ{_IN_}, - $$targ{_OUT_}, - $$targ{_TIME_}, - $$targ{_UPTIME_}, - $$targ{_NAME_}) = getexternal($$targ{Command}); - } elsif ($$targ{Methode} eq 'SNMP' and not $deadhost{$$targ{Host}}) { - debug('snpo', "run snmpget from $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}\@$$targ{Host}"); - ($$targ{_IN_}, - $$targ{_OUT_}, - $$targ{_TIME_}, - $$targ{_UPTIME_}, - $$targ{_NAME_}) = getsnmparg($confcache,$targ,$cfg,\%populated); - if ( $$targ{_IN_} and $$targ{_IN_} eq 'DEADHOST') { - $$targ{_IN_} = undef; - $$targ{_TIME_} =time; - # guess we got a blank here - $deadhost{$$targ{Host}} = 1; - warn "$NOW: WARNING: no data for $$targ{OID}[0]&$$targ{OID}[1]:$$targ{Community}\@$$targ{Host}. Skipping further queries for Host $$targ{Host} in this round.\n" - } - if ( $$targ{_IN_} and $$targ{_IN_} eq 'NOMATCH') { - $$targ{_IN_} = undef; - $$targ{_TIME_} =time; - } - - } else { - $$targ{_IN_} = undef; - $$targ{_OUT_} = undef; - $$targ{_TIME_} = time; - $$targ{_UPTIME_} = undef; - $$targ{_NAME_} = undef; - } - if ($] >= 5.008 ){ - $$targ{_IN_} = new Math::BigFloat "$$targ{_IN_}" if $$targ{_IN_}; - $$targ{_OUT_} = new Math::BigFloat "$$targ{_OUT_}" if $$targ{_OUT_}; - } - - } - } - -} - -sub imggen ($) { - my $dir = shift; - if ( ! -r "$dir${main::SL}mrtg-l.png" and open W, ">$dir${main::SL}mrtg-l.png" ){ - binmode W; - print W unpack ('u', <<'UUENC'); -MB5!.1PT*&@H -24A$4@ #\ 9! , !TA.O' &%!,5$5]9GTT -M79A?8HB(9WFR;6G_=DWM=%35<5R_M[A2 6)+1T0'%F&(ZP 1%)1$%4 -M>-JMDKUOPD ,Q:E4]CH?S0PMG8-.$6L1A*YMJ,DQ%5?[_ON>$Y (2$R>= -MY;-^OF=;GNCM\SFY#_#GW$IU0WMP/#O5HSGNW8"9R)/92$NQ\Z:GUDG/@.@! -M)CP#4H\ >LT>)NB!A0]8,"]&0.(#WY92V<\$=FM4WHV? J -M=$58=%-I9VYA='5R90!D,C(W8S$dir${main::SL}mrtg-m.png" ){ - binmode W; - print W unpack ('u', <<'UUENC'); -MB5!.1PT*&@H -24A$4@ !D 9! , VQYA0 &%!,5$5]9GTT -M79A588QN9(*7:73_=DWL=%31-IC"$4"!0P$>"E&2B:I,%Z((!"(I$)X88H@GJ :A)<$Y@@*I8)YAA">("N( -M%PYD"#L+"IJ#Y4!FN(86N4',# )J"4TO*R\O!_$"@::'@HTEQ /K _&$0+P -M(.T:6@A2@62?H#B*6TRQN#,C9\ "IT15AT4VEG;F%T -M=7)E #1E,S8X-S$P,38Q-S)A96%B.3,Y8SEA,F5D-31B86(U@DWZ,@ ET -M15AT1&5L87D ,C4P(RC.$P YT15AT4&%G90 R-7@R-2LP*S"#D2 ? -) $E%3D2N0F"" -UUENC -close W; - } - if ( ! -r "$dir${main::SL}mrtg-r.png" and open W, ">$dir${main::SL}mrtg-r.png" ){ - binmode W; - print W unpack ('u', <<'UUENC'); -MB5!.1PT*&@H -24A$4@ 80 9! , ##'$3) &%!,5$4T79@\ -M8YQ5=JAPC;>:K\S\_?[5WNFZR-PB%CO! 6)+1T0'%F&(ZP !=5)1$%4 -M>-KM6$ESVE@0;BW 58: K_)"YDH(.%=YL/"511)7O""N3AS!WY_^NM^3Q)+, -M4C55DZJ1*2&]U]O7Z\,4T"]__0_A;US.O\7$$#H*PR=R[_'@#? A9X3KLW,K -MNRX_*L=MGK^#PQ)[$USW+J@'U)E,!JIGS9"+RG0^B!(NDM,ZMB'G,M_?6 -MF@^[_#7D[U)*!V_N!#+4$)>M@+Z(.J/1)PNAI4]=5K# 0_,)'Z(4UXH*A<#/ -M2SQ\3+?[=!F4Q&M?Z!8@2-?4X+O*L^Q\$PPQ;MA.R= 65RRWR/:[U&#PTVR_ -MR4*JI#3?9#E-$U)#W)5*GE&3[Y&!T%P+?QR2)U;UUOB(0@A0"-Z*.AOVC)=^ -MY=L&-T/L"$G !,KBW"V-3PU[.\8W@5N7J)W(?L'&WB5LP6_*X10OP+2L21G/ -MH7OA=#:1&M)F-1FI\^A+8B&H %;A+\U>LP8A)HN^RZOC9^BZY#=#_&10NA8" -M.5E !^R7H/2R=0G!6PH%1\%-!W@<"GU+PN4Q.Z2D6-K!2-;D]-=J"-2H?&C> -M7"B$ABCW."HV-PX@%"6$YHS<3:@.K!$K2;N"4-0A\)X'.R[?%R4$MXQ"TT9, -M7*Y:IP)!8N0L=Z0IVUJH(9!51H%B1)9A]$%,S3QB[_P0 EA;B\ W*KM/%?%Q -M%*@8'$*0K?Y@54'0*+ !XZ@&P;C'2N$W?SU&\BX"Z"YKH08ATBB,^WCHW[U5 -MCK6U$!U'H3$WA9<<1\%+2I8R"H:]@ZT=Q8%9LK1%Z!1AA< XH902XAM@X#<; -MA?;J) HL-F[!KJ*U%KC:DDD -M_NQ\.9M$RD9?TE!AX\HJ8E,+IJE^'Q6+.H1X\K#A;V\FI.B6F=)&B$+E>'9T -M51>-EPD:$VVE;#C>'CJ2:=.FJ?9>1_'20-CQ'_ESAN"5Y?Q4LT'=SFS?^.7* -MYFU%;!(IFTP>!K!Q%=8AL.)7D@8/AL;K:#3A#LW#*>0H> <0(-"]YF!:*>X2 -MUG&GD;D08Z0] ,((4XWG0O;)0-@2UTPW8@V^G0NG46BO^FNB,@I.7A$?EO.W -MK+1)V<4R&H<$E]MR-K7@GD!HB;DL!4G%N>VP;?YJCPE?\''#<:4C!?S7?-IH -MXG%'2N"B.&2Y/^M(*ZE!6PM.]L..5#4997R@5W:5"SWE* JV([U5 -M*6W8H>EBM=_O>9B8CF2C0)M:.1L)@-*(!$_\^+C?S8TF.FJJO;6?& AP20*A -MB6DR[*FS':G[7)5<_,&2F1-N>:+LBJ2^+#Z1Q1?WV02(3PR2/'*IX -M=,_1!P+ %2,A]5Q'TBSR49QN_$P5\?$9Z>2 P=;DXN8B..I(+ 2/'76SBU,P -MC9=6BD28R\^O)=+1&2E2"#/C%CX7M++)J&#SFMP*GD^F,_SCC+.'42'H+;&) -M0LH]YTU8>HM#"!3_GI@4.:R%*_3\U\EC:M+I8_I]-,T&9*3H0:4U/RYG:-+9 -M):7)9D,JX$_9*=,TW>)74"X0F@ Y50B83W?\'H!$"]80*XF;"P2P>%NUJ&2_ -M?-51V7N3)7)?E!]B_$V:OMMJ^,+CZMYPMK??9;'SXBG"J;4C%PBPVX5^T]1" -M6U-M/;K_[#>U>Q4<$?_I=?&SS>NP_(5\X=Z<FT="M4^ 0W1%6'13;V9T=V%R90! *",I26UA9V5-86=I8VL@ -M-"XR+CD@.3DO,#DO,#$@8W)IC9 -M\ "IT15AT4VEG;F%T=7)E #$R,3VERSION("1.26");}) { - Socket6->import(qw(inet_pton getaddrinfo inet_ntop)); + Socket6->import(qw(pack_sockaddr_in6 inet_pton getaddrinfo)); $ipv6_addr_len = length(pack_sockaddr_in6(161, inet_pton(AF_INET6(), "::1"))); $SNMP_Session::ipv6available = 1; } @@ -601,7 +601,7 @@ use Carp; BEGIN { if($SNMP_Session::ipv6available) { import IO::Socket::INET6; - Socket6->import(qw(inet_pton getaddrinfo inet_ntop)); + Socket6->import(qw(pack_sockaddr_in6 inet_pton getaddrinfo)); } } diff --git a/lib/mrtg2/SNMP_Session.pm.socket6 b/lib/mrtg2/SNMP_Session.pm.socket6 deleted file mode 100644 index 84b3af3..0000000 --- a/lib/mrtg2/SNMP_Session.pm.socket6 +++ /dev/null @@ -1,1118 +0,0 @@ -### -*- mode: Perl -*- -###################################################################### -### SNMP Request/Response Handling -###################################################################### -### Copyright (c) 1995-2008, Simon Leinen. -### -### This program is free software; you can redistribute it under the -### "Artistic License 2.0" included in this distribution -### (file "Artistic"). -###################################################################### -### The abstract class SNMP_Session defines objects that can be used -### to communicate with SNMP entities. It has methods to send -### requests to and receive responses from an agent. -### -### Two instantiable subclasses are defined: -### SNMPv1_Session implements SNMPv1 (RFC 1157) functionality -### SNMPv2c_Session implements community-based SNMPv2. -###################################################################### -### Created by: Simon Leinen -### -### Contributions and fixes by: -### -### Matthew Trunnell -### Tobias Oetiker -### Heine Peters -### Daniel L. Needles -### Mike Mitchell -### Clinton Wong -### Alan Nichols -### Mike McCauley -### Andrew W. Elble -### Brett T Warden : pretty UInteger32 -### Michael Deegan -### Sergio Macedo -### Jakob Ilves (/IlvJa) : PDU capture -### Valerio Bontempi : IPv6 support -### Lorenzo Colitti : IPv6 support -### Philippe Simonet : Export avoid... -### Luc Pauwels : use_16bit_request_ids -### Andrew Cornford-Matheson : inform -### Gerry Dalton : strict subs bug -### Mike Fischer : pass MSG_DONTWAIT to recv() -###################################################################### - -package SNMP_Session; - -require 5.002; - -use strict; -use Exporter; -use vars qw(@ISA $VERSION @EXPORT $errmsg - $suppress_warnings - $default_avoid_negative_request_ids - $default_use_16bit_request_ids); -use Socket; -use BER '1.05'; -use Carp; - -sub map_table ($$$ ); -sub map_table_4 ($$$$); -sub map_table_start_end ($$$$$$); -sub index_compare ($$); -sub oid_diff ($$); - -$VERSION = '1.12'; - -@ISA = qw(Exporter); - -@EXPORT = qw(errmsg suppress_warnings index_compare oid_diff recycle_socket ipv6available); - -my $default_debug = 0; - -### Default initial timeout (in seconds) waiting for a response PDU -### after a request is sent. Note that when a request is retried, the -### timeout is increased by BACKOFF (see below). -### -my $default_timeout = 2.0; - -### Default number of attempts to get a reply for an SNMP request. If -### no response is received after TIMEOUT seconds, the request is -### resent and a new response awaited with a longer timeout (see the -### documentation on BACKOFF below). The "retries" value should be at -### least 1, because the first attempt counts, too (the name "retries" -### is confusing, sorry for that). -### -my $default_retries = 5; - -### Default backoff factor for SNMP_Session objects. This factor is -### used to increase the TIMEOUT every time an SNMP request is -### retried. -### -my $default_backoff = 1.0; - -### Default value for maxRepetitions. This specifies how many table -### rows are requested in getBulk requests. Used when walking tables -### using getBulk (only available in SNMPv2(c) and later). If this is -### too small, then a table walk will need unnecessarily many -### request/response exchanges. If it is too big, the agent may -### compute many variables after the end of the table. It is -### recommended to set this explicitly for each table walk by using -### map_table_4(). -### -my $default_max_repetitions = 12; - -### Default value for "avoid_negative_request_ids". -### -### Set this to non-zero if you have agents that have trouble with -### negative request IDs, and don't forget to complain to your agent -### vendor. According to the spec (RFC 1905), the request-id is an -### Integer32, i.e. its range is from -(2^31) to (2^31)-1. However, -### some agents erroneously encode the response ID as an unsigned, -### which prevents this code from matching such responses to requests. -### -$SNMP_Session::default_avoid_negative_request_ids = 0; - -### Default value for "use_16bit_request_ids". -### -### Set this to non-zero if you have agents that use 16bit request IDs, -### and don't forget to complain to your agent vendor. -### -$SNMP_Session::default_use_16bit_request_ids = 0; - -### Whether all SNMP_Session objects should share a single UDP socket. -### -$SNMP_Session::recycle_socket = 0; - -### IPv6 initialization code: check that IPv6 libraries are available, -### and if so load them. - -### We store the length of an IPv6 socket address structure in the class -### so we can determine if a socket address is IPv4 or IPv6 just by checking -### its length. The proper way to do this would be to use sockaddr_family(), -### but this function is only available in recent versions of Socket.pm. -my $ipv6_addr_len; - -### Flags to be passed to recv() when non-blocking behavior is -### desired. On most POSIX-like systems this will be set to -### MSG_DONTWAIT, on other systems we leave it at zero. -### -my $dont_wait_flags; - -BEGIN { - $ipv6_addr_len = undef; - $SNMP_Session::ipv6available = 0; - $dont_wait_flags = 0; - - if (eval {local $SIG{__DIE__};require Socket6;} && - eval {local $SIG{__DIE__};require IO::Socket::INET6; IO::Socket::INET6->VERSION("1.26");}) { - Socket6->import(qw(pack_sockaddr_in6 inet_pton getaddrinfo)); - $ipv6_addr_len = length(pack_sockaddr_in6(161, inet_pton(AF_INET6(), "::1"))); - $SNMP_Session::ipv6available = 1; - } - eval 'local $SIG{__DIE__};local $SIG{__WARN__};$dont_wait_flags = MSG_DONTWAIT();'; -} - -my $the_socket; - -$SNMP_Session::errmsg = ''; -$SNMP_Session::suppress_warnings = 0; - -sub get_request { 0 | context_flag () }; -sub getnext_request { 1 | context_flag () }; -sub get_response { 2 | context_flag () }; -sub set_request { 3 | context_flag () }; -sub trap_request { 4 | context_flag () }; -sub getbulk_request { 5 | context_flag () }; -sub inform_request { 6 | context_flag () }; -sub trap2_request { 7 | context_flag () }; - -sub standard_udp_port { 161 }; - -sub open -{ - return SNMPv1_Session::open (@_); -} - -sub timeout { $_[0]->{timeout} } -sub retries { $_[0]->{retries} } -sub backoff { $_[0]->{backoff} } -sub set_timeout { - my ($session, $timeout) = @_; - croak ("timeout ($timeout) must be a positive number") unless $timeout > 0.0; - $session->{'timeout'} = $timeout; -} -sub set_retries { - my ($session, $retries) = @_; - croak ("retries ($retries) must be a non-negative integer") - unless $retries == int ($retries) && $retries >= 0; - $session->{'retries'} = $retries; -} -sub set_backoff { - my ($session, $backoff) = @_; - croak ("backoff ($backoff) must be a number >= 1.0") - unless $backoff == int ($backoff) && $backoff >= 1.0; - $session->{'backoff'} = $backoff; -} - -sub encode_request_3 ($$$@) { - my($this, $reqtype, $encoded_oids_or_pairs, $i1, $i2) = @_; - my($request); - local($_); - - $this->{request_id} = ($this->{request_id} == 0x7fffffff) - ? -0x80000000 : $this->{request_id}+1; - $this->{request_id} += 0x80000000 - if ($this->{avoid_negative_request_ids} && $this->{request_id} < 0); - $this->{request_id} &= 0x0000ffff - if ($this->{use_16bit_request_ids}); - foreach $_ (@{$encoded_oids_or_pairs}) { - if (ref ($_) eq 'ARRAY') { - $_ = &encode_sequence ($_->[0], $_->[1]) - || return $this->ber_error ("encoding pair"); - } else { - $_ = &encode_sequence ($_, encode_null()) - || return $this->ber_error ("encoding value/null pair"); - } - } - $request = encode_tagged_sequence - ($reqtype, - encode_int ($this->{request_id}), - defined $i1 ? encode_int ($i1) : encode_int_0 (), - defined $i2 ? encode_int ($i2) : encode_int_0 (), - encode_sequence (@{$encoded_oids_or_pairs})) - || return $this->ber_error ("encoding request PDU"); - return $this->wrap_request ($request); -} - -sub encode_get_request { - my($this, @oids) = @_; - return encode_request_3 ($this, get_request, \@oids); -} - -sub encode_getnext_request { - my($this, @oids) = @_; - return encode_request_3 ($this, getnext_request, \@oids); -} - -sub encode_getbulk_request { - my($this, $non_repeaters, $max_repetitions, @oids) = @_; - return encode_request_3 ($this, getbulk_request, \@oids, - $non_repeaters, $max_repetitions); -} - -sub encode_set_request { - my($this, @encoded_pairs) = @_; - return encode_request_3 ($this, set_request, \@encoded_pairs); -} - -sub encode_trap_request ($$$$$$@) { - my($this, $ent, $agent, $gen, $spec, $dt, @pairs) = @_; - my($request); - local($_); - - foreach $_ (@pairs) { - if (ref ($_) eq 'ARRAY') { - $_ = &encode_sequence ($_->[0], $_->[1]) - || return $this->ber_error ("encoding pair"); - } else { - $_ = &encode_sequence ($_, encode_null()) - || return $this->ber_error ("encoding value/null pair"); - } - } - $request = encode_tagged_sequence - (trap_request, $ent, $agent, $gen, $spec, $dt, encode_sequence (@pairs)) - || return $this->ber_error ("encoding trap PDU"); - return $this->wrap_request ($request); -} - -sub encode_v2_trap_request ($@) { - my($this, @pairs) = @_; - - return encode_request_3($this, trap2_request, \@pairs); -} - -sub decode_get_response { - my($this, $response) = @_; - my @rest; - @{$this->{'unwrapped'}}; -} - -sub decode_trap_request ($$) { - my ($this, $trap) = @_; - my ($snmp_version, $community, $ent, $agent, $gen, $spec, $dt, - $request_id, $error_status, $error_index, - $bindings); - ($snmp_version, $community, - $ent, $agent, - $gen, $spec, $dt, - $bindings) - = decode_by_template ($trap, "%{%i%s%*{%O%A%i%i%u%{%@", - trap_request); - if (!defined $snmp_version) { - ($snmp_version, $community, - $request_id, $error_status, $error_index, - $bindings) - = decode_by_template ($trap, "%{%i%s%*{%i%i%i%{%@", - trap2_request); - if (!defined $snmp_version) { - ($snmp_version, $community,$request_id, $error_status, $error_index, $bindings) - = decode_by_template ($trap, "%{%i%s%*{%i%i%i%{%@", inform_request); - } - return $this->error_return ("v2 trap/inform request contained errorStatus/errorIndex " - .$error_status."/".$error_index) - if defined $error_status && defined $error_index - && ($error_status != 0 || $error_index != 0); - } - if (!defined $snmp_version) { - return $this->error_return ("BER error decoding trap:\n ".$BER::errmsg); - } - return ($community, $ent, $agent, $gen, $spec, $dt, $bindings); -} - -sub wait_for_response { - my($this) = shift; - my($timeout) = shift || 10.0; - my($rin,$win,$ein) = ('','',''); - my($rout,$wout,$eout); - vec($rin,$this->sockfileno,1) = 1; - select($rout=$rin,$wout=$win,$eout=$ein,$timeout); -} - -sub get_request_response ($@) { - my($this, @oids) = @_; - return $this->request_response_5 ($this->encode_get_request (@oids), - get_response, \@oids, 1); -} - -sub set_request_response ($@) { - my($this, @pairs) = @_; - return $this->request_response_5 ($this->encode_set_request (@pairs), - get_response, \@pairs, 1); -} - -sub getnext_request_response ($@) { - my($this,@oids) = @_; - return $this->request_response_5 ($this->encode_getnext_request (@oids), - get_response, \@oids, 1); -} - -sub getbulk_request_response ($$$@) { - my($this,$non_repeaters,$max_repetitions,@oids) = @_; - return $this->request_response_5 - ($this->encode_getbulk_request ($non_repeaters,$max_repetitions,@oids), - get_response, \@oids, 1); -} - -sub trap_request_send ($$$$$$@) { - my($this, $ent, $agent, $gen, $spec, $dt, @pairs) = @_; - my($req); - - $req = $this->encode_trap_request ($ent, $agent, $gen, $spec, $dt, @pairs); - ## Encoding may have returned an error. - return undef unless defined $req; - $this->send_query($req) - || return $this->error ("send_trap: $!"); - return 1; -} - -sub v2_trap_request_send ($$$@) { - my($this, $trap_oid, $dt, @pairs) = @_; - my @sysUptime_OID = ( 1,3,6,1,2,1,1,3 ); - my @snmpTrapOID_OID = ( 1,3,6,1,6,3,1,1,4,1 ); - my($req); - - unshift @pairs, [encode_oid (@snmpTrapOID_OID,0), - encode_oid (@{$trap_oid})]; - unshift @pairs, [encode_oid (@sysUptime_OID,0), - encode_timeticks ($dt)]; - $req = $this->encode_v2_trap_request (@pairs); - ## Encoding may have returned an error. - return undef unless defined $req; - $this->send_query($req) - || return $this->error ("send_trap: $!"); - return 1; -} - -sub request_response_5 ($$$$$) { - my ($this, $req, $response_tag, $oids, $errorp) = @_; - my $retries = $this->retries; - my $timeout = $this->timeout; - my ($nfound, $timeleft); - - ## Encoding may have returned an error. - return undef unless defined $req; - - $timeleft = $timeout; - while ($retries > 0) { - $this->send_query ($req) - || return $this->error ("send_query: $!"); - # IlvJa - # Add request pdu to capture_buffer - push @{$this->{'capture_buffer'}}, $req - if (defined $this->{'capture_buffer'} - and ref $this->{'capture_buffer'} eq 'ARRAY'); - # - wait_for_response: - ($nfound, $timeleft) = $this->wait_for_response($timeleft); - if ($nfound > 0) { - my($response_length); - - $response_length - = $this->receive_response_3 ($response_tag, $oids, $errorp, 1); - if ($response_length) { - # IlvJa - # Add response pdu to capture_buffer - push (@{$this->{'capture_buffer'}}, - substr($this->{'pdu_buffer'}, 0, $response_length) - ) - if (defined $this->{'capture_buffer'} - and ref $this->{'capture_buffer'} eq 'ARRAY'); - # - return $response_length; - } elsif (defined ($response_length)) { - goto wait_for_response; - # A response has been received, but for a different - # request ID or from a different IP address. - } else { - return undef; - } - } else { - ## No response received - retry - --$retries; - $timeout *= $this->backoff; - $timeleft = $timeout; - } - } - # IlvJa - # Add empty packet to capture_buffer - push @{$this->{'capture_buffer'}}, "" - if (defined $this->{'capture_buffer'} - and ref $this->{'capture_buffer'} eq 'ARRAY'); - # - $this->error ("no response received"); -} - -sub map_table ($$$) { - my ($session, $columns, $mapfn) = @_; - return $session->map_table_4 ($columns, $mapfn, - $session->default_max_repetitions ()); -} - -sub map_table_4 ($$$$) { - my ($session, $columns, $mapfn, $max_repetitions) = @_; - return $session->map_table_start_end ($columns, $mapfn, - "", undef, - $max_repetitions); -} - -sub map_table_start_end ($$$$$$) { - my ($session, $columns, $mapfn, $start, $end, $max_repetitions) = @_; - - my @encoded_oids; - my $call_counter = 0; - my $base_index = $start; - - do { - foreach (@encoded_oids = @{$columns}) { - $_=encode_oid (@{$_},split '\.',$base_index) - || return $session->ber_error ("encoding OID $base_index"); - } - if ($session->getnext_request_response (@encoded_oids)) { - my $response = $session->pdu_buffer; - my ($bindings) = $session->decode_get_response ($response); - my $smallest_index = undef; - my @collected_values = (); - - my @bases = @{$columns}; - while ($bindings ne '') { - my ($binding, $oid, $value); - my $base = shift @bases; - ($binding, $bindings) = decode_sequence ($bindings); - ($oid, $value) = decode_by_template ($binding, "%O%@"); - - my $out_index; - - $out_index = &oid_diff ($base, $oid); - my $cmp; - if (!defined $smallest_index - || ($cmp = index_compare ($out_index,$smallest_index)) == -1) { - $smallest_index = $out_index; - grep ($_=undef, @collected_values); - push @collected_values, $value; - } elsif ($cmp == 1) { - push @collected_values, undef; - } else { - push @collected_values, $value; - } - } - (++$call_counter, - &$mapfn ($smallest_index, @collected_values)) - if defined $smallest_index; - $base_index = $smallest_index; - } else { - return undef; - } - } - while (defined $base_index - && (!defined $end || index_compare ($base_index, $end) < 0)); - $call_counter; -} - -sub index_compare ($$) { - my ($i1, $i2) = @_; - $i1 = '' unless defined $i1; - $i2 = '' unless defined $i2; - if ($i1 eq '') { - return $i2 eq '' ? 0 : 1; - } elsif ($i2 eq '') { - return 1; - } elsif (!$i1) { - return $i2 eq '' ? 1 : !$i2 ? 0 : 1; - } elsif (!$i2) { - return -1; - } else { - my ($f1,$r1) = split('\.',$i1,2); - my ($f2,$r2) = split('\.',$i2,2); - - if ($f1 < $f2) { - return -1; - } elsif ($f1 > $f2) { - return 1; - } else { - return index_compare ($r1,$r2); - } - } -} - -sub oid_diff ($$) { - my($base, $full) = @_; - my $base_dotnot = join ('.',@{$base}); - my $full_dotnot = BER::pretty_oid ($full); - - return undef unless substr ($full_dotnot, 0, length $base_dotnot) - eq $base_dotnot - && substr ($full_dotnot, length $base_dotnot, 1) eq '.'; - substr ($full_dotnot, length ($base_dotnot)+1); -} - -# Pretty_address returns a human-readable representation of an IPv4 or IPv6 address. -sub pretty_address { - my($addr) = shift; - my($port, $addrunpack, $addrstr); - - # Disable strict subs to stop old versions of perl from - # complaining about AF_INET6 when Socket6 is not available - - if( (defined $ipv6_addr_len) && (length $addr == $ipv6_addr_len)) { - ($port,$addrunpack) = Socket6::unpack_sockaddr_in6 ($addr); - $addrstr = inet_ntop (AF_INET6(), $addrunpack); - } else { - ($port,$addrunpack) = unpack_sockaddr_in ($addr); - $addrstr = inet_ntoa ($addrunpack); - } - - return sprintf ("[%s].%d", $addrstr, $port); -} - -sub version { $VERSION; } - - -sub error_return ($$) { - my ($this,$message) = @_; - $SNMP_Session::errmsg = $message; - unless ($SNMP_Session::suppress_warnings) { - $message =~ s/^/ /mg; - carp ("Error:\n".$message."\n"); - } - return undef; -} - -sub error ($$) { - my ($this,$message) = @_; - my $session = $this->to_string; - $SNMP_Session::errmsg = $message."\n".$session; - unless ($SNMP_Session::suppress_warnings) { - $session =~ s/^/ /mg; - $message =~ s/^/ /mg; - carp ("SNMP Error:\n".$SNMP_Session::errmsg."\n"); - } - return undef; -} - -sub ber_error ($$) { - my ($this,$type) = @_; - my ($errmsg) = $BER::errmsg; - - $errmsg =~ s/^/ /mg; - return $this->error ("$type:\n$errmsg"); -} - -package SNMPv1_Session; - -use strict qw(vars subs); # see above -use vars qw(@ISA); -use SNMP_Session; -use Socket; -use BER; -use IO::Socket; -use Carp; - -BEGIN { - if($SNMP_Session::ipv6available) { - import IO::Socket::INET6; - Socket6->import(qw(pack_sockaddr_in6 inet_pton getaddrinfo)); - } -} - -@ISA = qw(SNMP_Session); - -sub snmp_version { 0 } - -# Supports both IPv4 and IPv6. -# Numeric IPv6 addresses must be passed between square brackets [] -sub open { - my($this, - $remote_hostname,$community,$port, - $max_pdu_len,$local_port,$max_repetitions, - $local_hostname,$ipv4only) = @_; - my($remote_addr,$socket,$sockfamily); - - $ipv4only = 1 unless defined $ipv4only; - $sockfamily = AF_INET; - - $community = 'public' unless defined $community; - $port = SNMP_Session::standard_udp_port unless defined $port; - $max_pdu_len = 8000 unless defined $max_pdu_len; - $max_repetitions = $default_max_repetitions - unless defined $max_repetitions; - - if ($ipv4only || ! $SNMP_Session::ipv6available) { - # IPv4-only code, uses only Socket and INET calls - if (defined $remote_hostname) { - $remote_addr = inet_aton ($remote_hostname) - or return $this->error_return ("can't resolve \"$remote_hostname\" to IP address"); - } - if ($SNMP_Session::recycle_socket && defined $the_socket) { - $socket = $the_socket; - } else { - $socket = IO::Socket::INET->new(Proto => 17, - Type => SOCK_DGRAM, - LocalAddr => $local_hostname, - LocalPort => $local_port) - || return $this->error_return ("creating socket: $!"); - $the_socket = $socket - if $SNMP_Session::recycle_socket; - } - $remote_addr = pack_sockaddr_in ($port, $remote_addr) - if defined $remote_addr; - } else { - # IPv6-capable code. Will use IPv6 or IPv4 depending on the address. - # Uses Socket6 and INET6 calls. - - # If it's a numeric IPv6 addresses, remove square brackets - if ($remote_hostname =~ /^\[(.*)\]$/) { - $remote_hostname = $1; - } - - my (@res, $socktype_tmp, $proto_tmp, $canonname_tmp); - @res = getaddrinfo($remote_hostname, $port, AF_UNSPEC, SOCK_DGRAM); - ($sockfamily, $socktype_tmp, $proto_tmp, $remote_addr, $canonname_tmp) = @res; - if (scalar(@res) < 5) { - return $this->error_return ("can't resolve \"$remote_hostname\" to IPv6 address"); - } - - if ($SNMP_Session::recycle_socket && defined $the_socket) { - $socket = $the_socket; - } elsif ($sockfamily == AF_INET) { - $socket = IO::Socket::INET->new(Proto => 17, - Type => SOCK_DGRAM, - LocalAddr => $local_hostname, - LocalPort => $local_port) - || return $this->error_return ("creating socket: $!"); - } else { - $socket = IO::Socket::INET6->new(Proto => 17, - Type => SOCK_DGRAM, - LocalAddr => $local_hostname, - LocalPort => $local_port) - || return $this->error_return ("creating socket: $!"); - $the_socket = $socket - if $SNMP_Session::recycle_socket; - } - } - bless { - 'sock' => $socket, - 'sockfileno' => fileno ($socket), - 'community' => $community, - 'remote_hostname' => $remote_hostname, - 'remote_addr' => $remote_addr, - 'sockfamily' => $sockfamily, - 'max_pdu_len' => $max_pdu_len, - 'pdu_buffer' => '\0' x $max_pdu_len, - 'request_id' => (int (rand 0x10000) << 16) - + int (rand 0x10000) - 0x80000000, - 'timeout' => $default_timeout, - 'retries' => $default_retries, - 'backoff' => $default_backoff, - 'debug' => $default_debug, - 'error_status' => 0, - 'error_index' => 0, - 'default_max_repetitions' => $max_repetitions, - 'use_getbulk' => 1, - 'lenient_source_address_matching' => 1, - 'lenient_source_port_matching' => 1, - 'avoid_negative_request_ids' => $SNMP_Session::default_avoid_negative_request_ids, - 'use_16bit_request_ids' => $SNMP_Session::default_use_16bit_request_ids, - 'capture_buffer' => undef, - }; -} - -sub open_trap_session (@) { - my ($this, $port) = @_; - $port = 162 unless defined $port; - return $this->open (undef, "", 161, undef, $port); -} - -sub sock { $_[0]->{sock} } -sub sockfileno { $_[0]->{sockfileno} } -sub remote_addr { $_[0]->{remote_addr} } -sub pdu_buffer { $_[0]->{pdu_buffer} } -sub max_pdu_len { $_[0]->{max_pdu_len} } -sub default_max_repetitions { - defined $_[1] - ? $_[0]->{default_max_repetitions} = $_[1] - : $_[0]->{default_max_repetitions} } -sub debug { defined $_[1] ? $_[0]->{debug} = $_[1] : $_[0]->{debug} } - -sub close { - my($this) = shift; - ## Avoid closing the socket if it may be shared with other session - ## objects. - if (! defined $the_socket || $this->sock ne $the_socket) { - close ($this->sock) || $this->error ("close: $!"); - } -} - -sub wrap_request { - my($this) = shift; - my($request) = shift; - - encode_sequence (encode_int ($this->snmp_version), - encode_string ($this->{community}), - $request) - || return $this->ber_error ("wrapping up request PDU"); -} - -my @error_status_code = qw(noError tooBig noSuchName badValue readOnly - genErr noAccess wrongType wrongLength - wrongEncoding wrongValue noCreation - inconsistentValue resourceUnavailable - commitFailed undoFailed authorizationError - notWritable inconsistentName); - -sub unwrap_response_5b { - my ($this,$response,$tag,$oids,$errorp) = @_; - my ($community,$request_id,@rest,$snmpver); - - ($snmpver,$community,$request_id, - $this->{error_status}, - $this->{error_index}, - @rest) - = decode_by_template ($response, "%{%i%s%*{%i%i%i%{%@", - $tag); - return $this->ber_error ("Error decoding response PDU") - unless defined $snmpver; - return $this->error ("Received SNMP response with unknown snmp-version field $snmpver") - unless $snmpver == $this->snmp_version; - if ($this->{error_status} != 0) { - if ($errorp) { - my ($oid, $errmsg); - $errmsg = $error_status_code[$this->{error_status}] || $this->{error_status}; - $oid = $oids->[$this->{error_index}-1] - if $this->{error_index} > 0 && $this->{error_index}-1 <= $#{$oids}; - $oid = $oid->[0] - if ref($oid) eq 'ARRAY'; - return ($community, $request_id, - $this->error ("Received SNMP response with error code\n" - ." error status: $errmsg\n" - ." index ".$this->{error_index} - .(defined $oid - ? " (OID: ".&BER::pretty_oid($oid).")" - : ""))); - } else { - if ($this->{error_index} == 1) { - @rest[$this->{error_index}-1..$this->{error_index}] = (); - } - } - } - ($community, $request_id, @rest); -} - -sub send_query ($$) { - my ($this,$query) = @_; - send ($this->sock,$query,0,$this->remote_addr); -} - -## Compare two sockaddr_in structures for equality. This is used when -## matching incoming responses with outstanding requests. Previous -## versions of the code simply did a bytewise comparison ("eq") of the -## two sockaddr_in structures, but this didn't work on some systems -## where sockaddr_in contains other elements than just the IP address -## and port number, notably FreeBSD. -## -## We allow for varying degrees of leniency when checking the source -## address. By default we now ignore it altogether, because there are -## agents that don't respond from UDP port 161, and there are agents -## that don't respond from the IP address the query had been sent to. -## -## The address family is stored in the session object. We could use -## sockaddr_family() to determine it from the sockaddr, but this function -## is only available in recent versions of Socket.pm. -sub sa_equal_p ($$$) { - my ($this, $sa1, $sa2) = @_; - my ($p1,$a1,$p2,$a2); - - # Disable strict subs to stop old versions of perl from - # complaining about AF_INET6 when Socket6 is not available - if($this->{'sockfamily'} == AF_INET) { - # IPv4 addresses - ($p1,$a1) = unpack_sockaddr_in ($sa1); - ($p2,$a2) = unpack_sockaddr_in ($sa2); - } elsif($this->{'sockfamily'} == AF_INET6()) { - # IPv6 addresses - ($p1,$a1) = Socket6::unpack_sockaddr_in6 ($sa1); - ($p2,$a2) = Socket6::unpack_sockaddr_in6 ($sa2); - } else { - return 0; - } - use strict "subs"; - - if (! $this->{'lenient_source_address_matching'}) { - return 0 if $a1 ne $a2; - } - if (! $this->{'lenient_source_port_matching'}) { - return 0 if $p1 != $p2; - } - return 1; -} - -sub receive_response_3 { - my ($this, $response_tag, $oids, $errorp, $dont_block_p) = @_; - my ($remote_addr); - my $flags = 0; - $flags = $dont_wait_flags if defined $dont_block_p and $dont_block_p; - $remote_addr = recv ($this->sock,$this->{'pdu_buffer'},$this->max_pdu_len,$flags); - return $this->error ("receiving response PDU: $!") - unless defined $remote_addr; - return $this->error ("short (".length $this->{'pdu_buffer'} - ." bytes) response PDU") - unless length $this->{'pdu_buffer'} > 2; - my $response = $this->{'pdu_buffer'}; - ## - ## Check whether the response came from the address we've sent the - ## request to. If this is not the case, we should probably ignore - ## it, as it may relate to another request. - ## - if (defined $this->{'remote_addr'}) { - if (! $this->sa_equal_p ($remote_addr, $this->{'remote_addr'})) { - if ($this->{'debug'} && !$SNMP_Session::recycle_socket) { - carp ("Response came from ".&SNMP_Session::pretty_address($remote_addr) - .", not ".&SNMP_Session::pretty_address($this->{'remote_addr'})) - unless $SNMP_Session::suppress_warnings; - } - return 0; - } - } - $this->{'last_sender_addr'} = $remote_addr; - my ($response_community, $response_id, @unwrapped) - = $this->unwrap_response_5b ($response, $response_tag, - $oids, $errorp); - if ($response_community ne $this->{community} - || $response_id ne $this->{request_id}) { - if ($this->{'debug'}) { - carp ("$response_community != $this->{community}") - unless $SNMP_Session::suppress_warnings - || $response_community eq $this->{community}; - carp ("$response_id != $this->{request_id}") - unless $SNMP_Session::suppress_warnings - || $response_id == $this->{request_id}; - } - return 0; - } - if (!defined $unwrapped[0]) { - $this->{'unwrapped'} = undef; - return undef; - } - $this->{'unwrapped'} = \@unwrapped; - return length $this->pdu_buffer; -} - -sub receive_trap { - my ($this) = @_; - my ($remote_addr, $iaddr, $port, $trap); - $remote_addr = recv ($this->sock,$this->{'pdu_buffer'},$this->max_pdu_len,0); - return undef unless $remote_addr; - - if( (defined $ipv6_addr_len) && (length $remote_addr == $ipv6_addr_len)) { - ($port,$iaddr) = Socket6::unpack_sockaddr_in6($remote_addr); - } else { - ($port,$iaddr) = unpack_sockaddr_in($remote_addr); - } - - $trap = $this->{'pdu_buffer'}; - return ($trap, $iaddr, $port); -} - -sub describe { - my($this) = shift; - print $this->to_string (),"\n"; -} - -sub to_string { - my($this) = shift; - my ($class,$prefix); - - $class = ref($this); - $prefix = ' ' x (length ($class) + 2); - ($class - .(defined $this->{remote_hostname} - ? " (remote host: \"".$this->{remote_hostname}."\"" - ." ".&SNMP_Session::pretty_address ($this->remote_addr).")" - : " (no remote host specified)") - ."\n" - .$prefix." community: \"".$this->{'community'}."\"\n" - .$prefix." request ID: ".$this->{'request_id'}."\n" - .$prefix."PDU bufsize: ".$this->{'max_pdu_len'}." bytes\n" - .$prefix." timeout: ".$this->{timeout}."s\n" - .$prefix." retries: ".$this->{retries}."\n" - .$prefix." backoff: ".$this->{backoff}.")"); -## sprintf ("SNMP_Session: %s (size %d timeout %g)", -## &SNMP_Session::pretty_address ($this->remote_addr),$this->max_pdu_len, -## $this->timeout); -} - -### SNMP Agent support -### contributed by Mike McCauley -### -sub receive_request { - my ($this) = @_; - my ($remote_addr, $iaddr, $port, $request); - - $remote_addr = recv($this->sock, $this->{'pdu_buffer'}, - $this->{'max_pdu_len'}, 0); - return undef unless $remote_addr; - - if( (defined $ipv6_addr_len) && (length $remote_addr == $ipv6_addr_len)) { - ($port,$iaddr) = Socket6::unpack_sockaddr_in6($remote_addr); - } else { - ($port,$iaddr) = unpack_sockaddr_in($remote_addr); - } - - $request = $this->{'pdu_buffer'}; - return ($request, $iaddr, $port); -} - -sub decode_request { - my ($this, $request) = @_; - my ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings); - - ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings) - = decode_by_template ($request, "%{%i%s%*{%i%i%i%@", SNMP_Session::get_request); - if (defined $snmp_version) - { - # Its a valid get_request - return(SNMP_Session::get_request, $requestid, $bindings, $community); - } - - ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings) - = decode_by_template ($request, "%{%i%s%*{%i%i%i%@", SNMP_Session::getnext_request); - if (defined $snmp_version) - { - # Its a valid getnext_request - return(SNMP_Session::getnext_request, $requestid, $bindings, $community); - } - - ($snmp_version, $community, $requestid, $errorstatus, $errorindex, $bindings) - = decode_by_template ($request, "%{%i%s%*{%i%i%i%@", SNMP_Session::set_request); - if (defined $snmp_version) - { - # Its a valid set_request - return(SNMP_Session::set_request, $requestid, $bindings, $community); - } - - # Something wrong with this packet - # Decode failed - return undef; -} - -package SNMPv2c_Session; -use strict qw(vars subs); # see above -use vars qw(@ISA); -use SNMP_Session; -use BER; -use Carp; - -@ISA = qw(SNMPv1_Session); - -sub snmp_version { 1 } - -sub open { - my $session = SNMPv1_Session::open (@_); - return undef unless defined $session; - return bless $session; -} - -## map_table_start_end using get-bulk -## -sub map_table_start_end ($$$$$$) { - my ($session, $columns, $mapfn, $start, $end, $max_repetitions) = @_; - - my @encoded_oids; - my $call_counter = 0; - my $base_index = $start; - my $ncols = @{$columns}; - my @collected_values = (); - - if (! $session->{'use_getbulk'}) { - return SNMP_Session::map_table_start_end - ($session, $columns, $mapfn, $start, $end, $max_repetitions); - } - $max_repetitions = $session->default_max_repetitions - unless defined $max_repetitions; - - for (;;) { - foreach (@encoded_oids = @{$columns}) { - $_=encode_oid (@{$_},split '\.',$base_index) - || return $session->ber_error ("encoding OID $base_index"); - } - if ($session->getbulk_request_response (0, $max_repetitions, - @encoded_oids)) { - my $response = $session->pdu_buffer; - my ($bindings) = $session->decode_get_response ($response); - my @colstack = (); - my $k = 0; - my $j; - - my $min_index = undef; - - my @bases = @{$columns}; - my $n_bindings = 0; - my $binding; - - ## Copy all bindings into the colstack. - ## The colstack is a vector of vectors. - ## It contains one vector for each "repeater" variable. - ## - while ($bindings ne '') { - ($binding, $bindings) = decode_sequence ($bindings); - my ($oid, $value) = decode_by_template ($binding, "%O%@"); - - push @{$colstack[$k]}, [$oid, $value]; - ++$k; $k = 0 if $k >= $ncols; - } - - ## Now collect rows from the column stack: - ## - ## Iterate through the column stacks to find the smallest - ## index, collecting the values for that index in - ## @collected_values. - ## - ## As long as a row can be assembled, the map function is - ## called on it and the iteration proceeds. - ## - $base_index = undef; - walk_rows_from_pdu: - for (;;) { - my $min_index = undef; - - for ($k = 0; $k < $ncols; ++$k) { - $collected_values[$k] = undef; - my $pair = $colstack[$k]->[0]; - unless (defined $pair) { - $min_index = undef; - last walk_rows_from_pdu; - } - my $this_index - = SNMP_Session::oid_diff ($columns->[$k], $pair->[0]); - if (defined $this_index) { - my $cmp - = !defined $min_index - ? -1 - : SNMP_Session::index_compare - ($this_index, $min_index); - if ($cmp == -1) { - for ($j = 0; $j < $k; ++$j) { - unshift (@{$colstack[$j]}, - [$min_index, - $collected_values[$j]]); - $collected_values[$j] = undef; - } - $min_index = $this_index; - } - if ($cmp <= 0) { - $collected_values[$k] = $pair->[1]; - shift @{$colstack[$k]}; - } - } - } - ($base_index = undef), last - if !defined $min_index; - last - if defined $end - and SNMP_Session::index_compare ($min_index, $end) >= 0; - &$mapfn ($min_index, @collected_values); - ++$call_counter; - $base_index = $min_index; - } - } else { - return undef; - } - last if !defined $base_index; - last - if defined $end - and SNMP_Session::index_compare ($base_index, $end) >= 0; - } - $call_counter; -} - -1;