#!/usr/bin/perl # ------------------------------------------------------------------------ # Copyright (C) 2007-2009 Doichin Dokov NetOne-Silistra, BG # # A NET-SNMP extension for polling BGP stats from a running OpenBGPd # # Concept taken from Oliver Hitz's quagga-snmp: # http://www.net-track.ch/opensource/quagga-snmp/ # Copyright (C) 2004-2006 Oliver Hitz # -------------------------------------------------------------------- # Contributors: # * Renato Ornelas - renato at openx.com.br - 32-bit peer ASNs support # -------------------------------------------------------------------- # 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 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. # # 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., 59 Temple Place - Suite 330, Boston, # MA 02111-1307, USA. # -------------------------------------------------------------------- # Version: 0.2 # use strict; # The base OID of this extension. Has to match the OID in snmpd.conf: my $baseoid = ".1.3.6.1.4.1.99999.1"; # The path to bgpctl: my $bgpctl = "/usr/sbin/bgpctl"; # Results from "bgpctl show" are cached for some seconds so that an # SNMP walk doesn't result in bgpctl being called hundreds of times: my $cache_secs = 60; # -------------------------------------------------------------------- my $mib; my $mibtime; # Switch on autoflush $| = 1; while (my $cmd = ) { chomp $cmd; if ($cmd eq "PING") { print "PONG\n"; } elsif ($cmd eq "get") { my $oid_in = ; my $oid = get_oid($oid_in); my $mib = create_bgp_mib(); if ($oid != 0 && defined($mib->{$oid})) { print "$baseoid.$oid\n"; print $mib->{$oid}[0]."\n"; print $mib->{$oid}[1]."\n"; } else { print "NONE\n"; } } elsif ($cmd eq "getnext") { my $oid_in = ; my $oid = get_oid($oid_in); my $found = 0; my $mib = create_bgp_mib(); my @s = sort { oidcmp($a, $b) } keys %{ $mib }; for (my $i = 0; $i < @s; $i++) { if (oidcmp($oid, $s[$i]) == -1) { print "$baseoid.".$s[$i]."\n"; print $mib->{$s[$i]}[0]."\n"; print $mib->{$s[$i]}[1]."\n"; $found = 1; last; } } if (!$found) { print "NONE\n"; } } else { # Unknown command } } exit 0; sub get_oid { my ($oid) = @_; chomp $oid; my $base = $baseoid; $base =~ s/\./\\./g; if ($oid !~ /^$base(\.|$)/) { # Requested oid doesn't match base oid return 0; } $oid =~ s/^$base\.?//; return $oid; } sub oidcmp { my ($x, $y) = @_; my @a = split /\./, $x; my @b = split /\./, $y; my $i = 0; while (1) { if ($i > $#a) { if ($i > $#b) { return 0; } else { return -1; } } elsif ($i > $#b) { return 1; } if ($a[$i] < $b[$i]) { return -1; } elsif ($a[$i] > $b[$i]) { return 1; } $i++; } } ## Populates the MIB tree sub create_bgp_mib { # We cache the results for $cache_secs seconds if (time - $mibtime < $cache_secs) { return $mib; } my %bgp = ( "1" => [ "integer", 0 ], # Number of configured peers "2" => [ "integer", 0 ], # Number of active peers "3" => [ "integer", 0 ], # Number of RDE IPv4 network entries "4" => [ "integer", 0 ], # (not used - qugga communities number) "5" => [ "integer", 0 ], # Number of total prefixes from peers # RDE stats "10" =>[ "integer", 0 ], # Number of RDE prefix entries "11" => [ "integer", 0 ], # Number of RDE BGP path attribute entries "12" => [ "integer", 0 ], # Number of RDE BGP AS-PATH attribute entries "13" => [ "integer", 0 ], # Number of RDE BGP attributes entries "14" => [ "integer", 0 ], # Number of RDE BGP attributes ); open Q, "$bgpctl -n show |"; while (my $l = ) { if ($l =~ /^(\d+\.\d+\.\d+\.\d+)\s/) { # Line starts with IP ? $bgp{"1"}[1]++; my @n = split /\s+/, $l; # .1 IP Address $bgp{"9.".$n[0].".1"} = [ "ipaddress", $n[0] ]; # .2 State, .4 Prefixes if ($n[6] =~ /\d+/) { # Prefixes are a number ? $bgp{"9.".$n[0].".2"} = [ "integer", 1 ]; $bgp{"9.".$n[0].".4"} = [ "integer", $n[6] ]; $bgp{"2"}[1]++; $bgp{"5"}[1] += $n[6]; } else { # Peer is down $bgp{"9.".$n[0].".2"} = [ "integer", 0 ]; $bgp{"9.".$n[0].".4"} = [ "integer", 0 ]; } # .3 ASN if($n[1] =~ /([0-9]+)\.([0-9]+)/) { # ASN32? $n[1] = $1*65536 + $2; } $bgp{"9.".$n[0].".3"} = [ "integer", $n[1] ]; # .5 Up/down $bgp{"9.".$n[0].".5"} = [ "timeticks", uptime($n[5]) ]; } } close Q; # Collect RDE memory stats open Q, "$bgpctl show rib memory |"; while (my $l = ) { if ($l =~ /(\d+) IPv4 network entries/) { $bgp{"3"}[1] = $1; } elsif ($l =~ /(\d+) prefix entries/) { $bgp{"10"}[1] = $1; } elsif ($l =~ /(\d+) BGP path attribute entries/) { $bgp{"11"}[1] = $1; } elsif ($l =~ /(\d+) BGP AS-PATH attribute entries/) { $bgp{"12"}[1] = $1; } elsif ($l =~ /(\d+) BGP attributes entries/) { $bgp{"13"}[1] = $1; } elsif ($l =~ /(\d+) BGP attributes using/) { $bgp{"14"}[1] = $1; } } close Q; $mib = \%bgp; $mibtime = time; return $mib; } ## Converts BGP uptime to ticks sub uptime { my ($t) = @_; if ($t =~ /^(\d+):(\d+):(\d+)$/) { return 100*($3+60*($2+$1*60)); } elsif ($t =~ /^(\d+)d(\d+)h(\d+)m$/) { return 100*60*($3+60*($2+24*$1)); } elsif ($t =~ /^(\d+)w(\d+)d(\d+)h$/) { return 100*60*60*($3+24*$2+7*24*$1); } return 0; }