[libsnmp-session-perl] 01/15: New upstream version 1.14~git20130523.186a005
Roland Rosenfeld
roland at moszumanska.debian.org
Mon Dec 4 19:28:55 UTC 2017
This is an automated email from the git hooks/post-receive script.
roland pushed a commit to branch master
in repository libsnmp-session-perl.
commit 564e12680238a3e4bc358a31126b0debcbd56e07
Author: Roland Rosenfeld <roland at debian.org>
Date: Thu Aug 24 08:29:26 2017 +0200
New upstream version 1.14~git20130523.186a005
---
ChangeLog | 2534 ++++++++++++++++++++++++++++++++++++++
META.yml | 12 -
Net_SNMP_util.pm | 2130 ++++++++++++++++++++++++++++++++
README | 231 +---
changes.html | 709 +++++++++++
faq.html | 72 ++
index.html | 307 +----
lib/BER.pm | 322 ++++-
lib/SNMP_Session.pm | 956 +++++++++++---
lib/SNMP_Table.pm | 132 ++
lib/SNMP_util.pm | 122 +-
security.html | 42 +
t/00ber.t | 52 +
test/asn1-test.pl | 32 +
test/atm-cfgmaker | 128 ++
test/atol-test.c | 37 +
test/bad-trap.pl | 40 +
test/bay-atm-test.pl | 54 +
test/bgpls | 209 ++++
test/bridge-list-fdb | 104 ++
test/cammer | 245 ++++
test/cisco-config-history | 170 +++
test/cisco-list-cards | 258 ++++
test/cisco-tftp.pl | 50 +
test/counter64-test.pl | 201 +++
test/cricket-genconf-sensor | 232 ++++
test/d.pl | 62 +
test/digital-bug | 32 +
test/entls | 269 ++++
test/fore-test.pl | 50 +
test/hex-test.pl | 61 +
test/inexist.pl | 62 +
test/ip-addr-to-if-index | 141 +++
test/lambda-monitor.pl | 169 +++
test/lambda-webmon.pl | 208 ++++
test/list-bgp4-neighbors | 53 +
test/list-bgp4-table | 84 ++
test/list-ospf-neighbors | 47 +
test/look-at-counters.pl | 105 ++
test/ls1010-list-vcls | 258 ++++
test/make-cisco-products.pl | 21 +
test/max-list-sessions | 130 ++
test/mcount.pl | 218 ++++
test/mdebug | 142 +++
test/mrouted-genconf | 114 ++
test/mrtg-ipmcast | 319 +++++
test/msdpls | 303 +++++
test/mtf.pl | 51 +
test/negative-counter.pl | 8 +
test/party-test.pl | 15 +
test/pnni-find-ilmi-neighbors.pl | 41 +
test/qosls | 553 +++++++++
test/router-stats.pl | 161 +++
test/sequence-bug.pl | 22 +
test/shipmr | 119 ++
test/snmpmap_table-test.pl | 28 +
test/snmpspeed.pl | 213 ++++
test/snmptrap.note | 4 +
test/snmptrap.pl | 83 ++
test/sorrento-nest-list | 85 ++
test/trap-listener | 90 +-
test/v6-list-prefixes | 13 +
test/vc-counters.pl | 446 +++++++
test/verio-problem.pl | 35 +
64 files changed, 13378 insertions(+), 818 deletions(-)
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..b4b3bb8
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,2534 @@
+2008-03-19 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm: Improved initialization of the flags for
+ non-blocking behavior in receive_response_3(). The __DIE__ and
+ __WARN__ signal handlers should be bound to the defaults, because
+ the caller might be binding those and get in our way. Also, we
+ only want to compute the flags once, on initialization.
+
+2007-12-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm, lib/SNMP_Session.pm, lib/BER.pm:
+ Upgraded to Artistic License 2.0.
+
+ Copyright notice updated for 2008.
+
+ * Artistic: Upgraded to Artistic License 2.0, from
+ http://svn.perl.org/viewcvs/parrot/trunk/LICENSE?view=markup&rev=19096
+
+2007-11-01 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm: New MIB parsing code from Mike Mitchell.
+
+2007-10-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION):
+ Upgraded to 1.11, to pick up change to SNMP_util.pm.
+
+ * changes.html: Document loop detection fix in SNMP_util.pm.
+
+ * faq.html, index.html: Updated my e-mail address.
+
+ * README, README.SNMP_util, lib/SNMP_Session.pm, lib/SNMP_util.pm,
+ changes.html: Changed Tobi Oetiker's mail address.
+
+ * README, index.html: Changed MRTG URL.
+
+ * lib/SNMP_util.pm: [All changes from Mike Mitchell]
+
+ Global replace || => or, && => and, to avoid precedence errors.
+
+ (snmpwalk_flg): Improved loop detection.
+
+2007-10-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION): Upgraded to 1.11, to pick up
+ change to SNMP_util.pm.
+
+ * README, README.SNMP_util, changes.html, lib/SNMP_Session.pm:
+ Changed Tobi Oetiker's mail address.
+
+ * lib/SNMP_util.pm: [All changes from Mike Mitchell]
+
+ Changed Tobi Oetiker's mail address.
+
+ Global replace || => or, && => and, to avoid precedence errors.
+
+ (snmpwalk_flg): Improved loop detection.
+
+ * README: Changed MRTG URL.
+
+2007-05-18 Simon Leinen <simon.leinen at switch.ch>
+
+ * README, changes.html, index.html: Updated copyright.
+
+ * lib/SNMP_util.pm (Check_OID):
+ Fix regexp for qualified OID case (Mike Mitchell).
+
+2007-05-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm: Added Mike Fischer as a contributor.
+
+ ($VERSION): Incremented to 1.10.
+
+ (receive_response_3): Added optional "dont_block" argument. If
+ this is present and non-zero, pass MSG_DONTWAIT to the recv()
+ call. MSG_DONTWAIT is wrapped in an eval, to avoid breaking the
+ code on systems that don't have the flag.
+
+ (request_response_5): Pass dont_block=1 to receive_response_3.
+ According to Mike Fisher, Linux sometimes blocks on recv() even
+ though a select() for readability has returned, for example when a
+ checksum fails.
+
+2007-05-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm: Added Mike Fischer as a contributor.
+
+ ($VERSION): Incremented to 1.10.
+
+ (receive_response_3): Added optional "dont_block" argument. If
+ this is present and non-zero, pass MSG_DONTWAIT to the recv()
+ call. MSG_DONTWAIT is wrapped in an eval, to avoid breaking the
+ code on systems that don't have the flag.
+
+ (request_response_5): Pass dont_block=1 to receive_response_3.
+ According to Mike Fisher, Linux sometimes blocks on recv() even
+ though a select() for readability has returned, for example when a
+ checksum fails.
+
+2007-01-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/BER.pm: Updated copyright notice.
+
+ (pretty_print): Use PDU names according to RFC3416.
+
+2006-12-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/cricket-genconf-sensor:
+ Try to install newly generated configuration, where possible.
+
+ * test/cricket-genconf-sensor: Added header comment.
+
+ * test/cricket-genconf-sensor: New script.
+
+2006-12-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/entls (router_pretty_name):
+ New subroutine, greps RANCID configuration file
+ for `hostname' command. Caches the result so that files are only
+ opened once.
+
+ (print_physical): Use new forms of per-router defaults.
+
+ * test/entls:
+ Changed so that `-t' generates a Cricket configuration file to measure
+ all transceivers that support DOM (Digital Optical Monitoring). This
+ involves some structural changes.
+
+ (print_phys_tree): Implemented in terms of the new
+ `print_phys_tree_1'.
+
+ (print_phys_tree_1): Maintain a stack of parent nodes when traversing
+ the node tree. This stack is stored in each node's `parent_stack'
+ slot, and can be used by the node class' `tostring' method.
+
+ * test/entls ($print_vendor_type, $print_ent_physical_index):
+ New variables.
+
+ (Entity::PhysicalEntry::tostring): Added optional printing of index
+ and entPhysicalVendorType, controlled by the above variables.
+
+2006-10-12 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm, lib/BER.pm: Updated copyright string.
+
+2006-08-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * faq.html:
+ Updated SNMPv3 text, mentioning that SNMPv3 is supported by MRTG 2.13
+ and up, using Net::SNMP.
+
+ * lib/SNMP_util.pm (snmpLoad_OID_Cache):
+ Strip single or double quotes around the OID and
+ value. This allows us to read SunNet Manager OID files, which are
+ also distributed by e.g. Cisco (ftp://ftp.cisco.com/pub/mibs/oid).
+ Idea by Jan van Keulen, code cleanup by Mike Mitchell.
+
+2006-07-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 1.08.
+
+ * lib/SNMP_Session.pm (BEGIN):
+ Bind the __DIE__ signal handler, so that detection of IPv6
+ capability works even when someone else has bound that handler.
+ (Patch from Tobi Oetiker.)
+
+2006-04-09 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Table.pm (snmp_row_to_object): Added.
+
+2006-04-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/trap-listener: Added prettyfication of OIDs.
+
+ Suppressed less-than-useful output such as the trap community or the
+ source port.
+
+2006-03-16 Simon Leinen <simon.leinen at switch.ch>
+
+ * README: Update copyright notice for 2006.
+
+2006-02-17 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/entls: New "entls" script.
+
+2006-01-20 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/qosls: Slight improvements in output for readability.
+
+ * README: Credit Jan van Keulen.
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 1.08.
+
+ * lib/SNMP_util.pm:
+ Some fixes from Mike Mitchell, notably in the area of OID translation.
+
+2006-01-17 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/find-admin-up-oper-down.pl:
+ Find interfaces that are admin up but oper down.
+
+2006-01-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html:
+ Fixed $session -> $trap_session in trap receipt example. Thanks to
+ rahul shah <shahrahulb at yahoo.co.in> for noticing this.
+
+2005-12-10 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm (SNMPv2c_Session::map_table_start_end):
+ Slight reindentation.
+
+ * lib/SNMP_Session.pm (SNMPv2c_Session::map_table_start_end): Call
+ `SNMP_Session::index_compare' rather than just `index_compare'.
+
+2005-11-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * faq.html: Upgraded SNMP URI syntax reference from draft to RFC 4088.
+
+2005-07-12 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: Added missing `mailto:'.
+
+ * index.html, changes.html: README.SNMP_util -> dist/
+
+2005-07-08 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm (snmpwalk_flg): Bug fix by Laurent Girod.
+
+2005-04-24 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Table.pm: Repository : :ext:diotima:/home/leinen/CVS
+ Module : SNMP_Session/lib
+ Working dir: ~/perl/SNMP_Session/lib/
+
+
+
+ In directory .:
+ Up-To-Date 1.38 BER.pm
+ Up-To-Date 1.145 SNMP_Session.pm
+ Added SNMP_Table.pm
+ Up-To-Date 1.51 SNMP_util.pm
+
+ --------------------- End ---------------------
+ -- last cmd: cvs -f status -v --
+
+2005-03-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/qosls: *** empty log message ***
+
+ * test/if-counters.pl: New options: -B to avoid use of get-bulk.
+
+ -C to avoid use of curses.
+
+ * test/bgpls: *** empty log message ***
+
+2005-01-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * faq.html: Updated.
+
+2004-12-19 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/msdpls: Added `-n' option to suppress resolving IP addresses.
+
+ (usage): Made more readable.
+
+ * test/msdpls (usage): Explain options.
+
+ * test/msdpls (usage): Added.
+
+2004-12-07 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm: Removed strange formatting.
+
+2004-11-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/sorrento-nest-list (%short_types): Added a few new types.
+
+ (@nestmasters): Beginning of update.
+
+2004-10-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm: Require BER.pm 1.05.
+
+ ($VERSION): Incremented to 1.07.
+
+ * lib/SNMP_Session.pm:
+ context_flag -> context_flag() to fix error message from "use strict
+ subs" with recent Perl versions. Acknowledged Gerry Dalton.
+
+ * changes.html:
+ Documented 1.06 (SNMPv2 inform parsing) and 1.07 (strict subs bug with
+ newer Perl versions) changes to SNMP_Session.pm.
+
+ * MANIFEST (META.yml): Added by MakeMaker.
+
+ * README: Added Gerry Dalton.
+
+2004-09-17 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/if-counters.pl:
+ Added `-s' option to look at L3 switching statistics on a Catalyst
+ 6500 and probably some other Cisco L3 switches.
+
+ (out_switching_engine): New subroutine.
+
+2004-09-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/msdpls: Added `-d' flag.
+
+ Added debugging output.
+
+2004-09-04 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm:
+ Mention Andrew Cornford-Matheson in the header comment.
+
+ * lib/SNMP_Session.pm ($VERSION): 1.05 -> 1.06.
+
+ (decode_trap_request): Understand inform requests, too.
+
+2004-09-02 Simon Leinen <simon.leinen at switch.ch>
+
+ * README, index.html: CMU SNMP -> Net-SNMP.
+
+ * index.html (decode_trap_request):
+ Mention that it supports SNMPv2 informs, too.
+
+ * README (Contributors): Added Andrew Cornford-Matheson.
+
+ (decode_trap_request): Mention that it supports SNMPv2 informs, too.
+
+ * changes.html: Upgraded to 1.05.
+
+2004-08-23 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html:
+ New distribution site http://www.switch.ch/misc/leinen/snmp/perl/dist/
+
+ Made pointers more absolute.
+
+2004-07-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/BER.pm ($VERSION): Upped to 1.05.
+
+ (BEGIN): Register various pretty printers.
+
+ (pretty_printer): Simplified because most types are handled by
+ registered pretty printers now.
+
+ * lib/SNMP_Session.pm ($VERSION): Upped to 1.05.
+
+ * changes.html: Document 1.04 change (in SNMP_util.pm).
+
+ * ChangeLog: Updated using C-x v a.
+
+ * index.html: Updated copyright years.
+
+ * lib/SNMP_util.pm, lib/SNMP_Session.pm ($VERSION): Upped to 1.04.
+
+ * lib/SNMP_util.pm (snmpget, snmpgetnext, snmpset):
+ Use wantarray() to determine whether
+ to return an array or just the first value.
+
+2004-07-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm, lib/SNMP_Session.pm ($VERSION): Upped to 1.04.
+
+ * lib/SNMP_util.pm (snmpget, snmpgetnext, snmpset):
+ Use wantarray() to determine whether
+ to return an array or just the first value.
+
+2004-06-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * README: Updated copyright years.
+
+2004-03-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm:
+ Require recent versions of BER.pm and SNMP_Session.pm.
+
+ ($VERSION): Incremented to 1.03.
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 1.03.
+
+ * changes.html (SNMP_util.pm): Documented 1.03 fix.
+
+ * lib/SNMP_util.pm (snmpwalk_flg):
+ Added missing line from Mike Mitchell's patch.
+
+2004-03-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 1.02.
+
+ * changes.html: Document BER.pm 1.01 and 1.02.
+
+ * lib/SNMP_Session.pm:
+ Require BER.pm 1.01, because previous versions lack support for a
+ variant of integer encoding.
+
+ ($VERSION): Incremented to 1.01.
+
+2004-02-17 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER-1.01-Mike-Mitchell.diff, BER-1.01-02.diff: Applied.
+
+ * lib/SNMP_util.pm (snmpwalkhash, snmpwalk_flg):
+ Implemented the new hash-reference
+ argument.
+
+ * changes.html: Documented BER.pm and SNMP_util.pm news in 1.02.
+
+ * test/ber-test.pl: Use strict.
+
+ Added function prototypes.
+
+ * lib/BER.pm (%pretty_printer):
+ New variable. This is a hash of pretty-printers
+ per BER type code. It is manipulated by register_pretty_printer() and
+ unregister_pretty_printer(), and used by pretty_print().
+
+ (register_pretty_printer, unregister_pretty_printer): New subroutines,
+ contributed by Mike Mitchell.
+
+ (pretty_print): If a pretty-printer has been registered for the type
+ code, call it.
+
+ (encode_intlike): In the Math::BigInt case, copy the integer before
+ taking its bmod(), because bmod() is destructive.
+
+ * README.SNMP_util:
+ Document additional optional `hash ref' argument to snmpwalkhash().
+
+2004-02-08 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/BER.pm (decode_intlike_s): Use decode_length().
+
+ (decode_length): Accept an optional second argument, specifying the
+ offset into the first argument at which to begin parsing. This
+ eliminates a substr() operation for every object. The callers have
+ been adapted accordingly.
+
+ * README: Added Milen Pavlov.
+
+2003-12-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION): 0.99 -> 1.00.
+
+ * changes.html: 0.100 -> 1.00.
+
+ * test/iftop: Added support for 64-bit counters.
+
+ * lib/SNMP_Session.pm ($default_use_16bit_request_ids): New variable.
+
+ (encode_request_3): Obey `use_16bit_request_ids' in request ID
+ generation.
+
+ * changes.html: Added note on `use_16bit_request_ids'.
+
+ * README: Added Luc Pauwels <Luc.Pauwels at xalasys.com> as a contributor.
+
+2003-12-04 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html:
+ Added new "Served Individual Item Link" to the "Essential SNMP" book
+ on Amazon, but commented it out because it breaks the formatting.
+
+ Removed the "NEW" icon from the existing Essential SNMP pointer.
+
+2003-11-26 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/iftop: Adding iftop
+
+ * test/README (iftop): Added URL.
+
+ * test/README, MANIFEST: Added Dave Plonka's `iftop'.
+
+2003-11-09 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html:
+ Document Mike Mitchell's snmpset support for all known types.
+
+ * lib/SNMP_util.pm (snmpset): Encode all known types.
+
+2003-11-07 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: Mentioned Christopher J. Tengi.
+
+ * README, lib/SNMP_util.pm:
+ Added Christopher J. Tengi to contributors list.
+
+ * changes.html: Document 0.99 changes.
+
+ * lib/SNMP_util.pm ($VERSION): Incremented to 0.99.
+
+ * lib/SNMP_util.pm (snmpset): Support Gauge32 values.
+
+2003-10-20 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 0.99.
+
+ * lib/SNMP_Session.pm (SNMPv1_Session::open):
+ Simplify initial request_id generation.
+
+ * lib/SNMP_Session.pm (encode_request_3):
+ Handle avoid_negative_request_ids in a saner way.
+ Thanks to Mike Mitchell.
+
+2003-09-26 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/trap-send:
+ Added forward declarations. Thanks to Bret Allibone for pointing out
+ the problem.
+
+2003-09-09 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: Documented 0.98 changes.
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 0.98.
+
+ * lib/SNMP_Session.pm: Require BER version 0.95.
+
+ Fixed a portability issue in the IPv6 code.
+
+ * lib/SNMP_util.pm: Require BER version 0.95.
+
+2003-09-08 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm: Changes from Mike Mitchell:
+
+ Depend on SNMP_Session.pm 0.97.
+
+ Fix parsing of OIDs with multiple quoted strings.
+
+ ($VERSION): Incremented to 0.98.
+
+2003-09-02 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm: Undid most of the last change.
+
+ * lib/SNMP_Session.pm: Removed ``use strict "subs"'' in a few places.
+
+ * README: Added Joerg Kummer to credits.
+
+ * lib/SNMP_util.pm ($VERSION): Increased to 0.97.
+
+ (snmpset): Added TimeTicks support, courtesy Joerg Kummer.
+
+ * changes.html: TimeTicks support in snmpset.
+
+ * changes.html: Added IPv6; $default_avoid_negative_request_ids notes.
+
+ * contrib/SNMP_util.pm, test/util-trap.pl, test/v6-list-prefixes, test/vc-counters.pl, test/verio-problem.pl:
+ Initial import of the essential parts of SNMP_Session
+
+ * contrib/SNMP_util.pm, test/util-trap.pl, test/v6-list-prefixes, test/vc-counters.pl, test/verio-problem.pl:
+ New file.
+
+ * faq.html, p2.htm, security.html, test/atol-test.c, test/bad-trap.pl, test/bay-atm-test.pl, test/bgp-check-routes, test/bulkwalkbug.pl, test/cammer, test/capturetest.pl, test/counter64-test.pl, test/d.pl, test/digital-bug, test/fore-test.pl, test/inexist.pl, test/list-bgp4-table, test/mcount.pl, test/mdebug, test/negative-counter.pl, test/sequence-bug.pl, test/set-test.pl, test/shipmr, test/snmptrap.note, test/snmptrap.pl:
+ Initial import of the essential parts of SNMP_Session
+
+ * faq.html, p2.htm, security.html, test/atol-test.c, test/bad-trap.pl, test/bay-atm-test.pl, test/bgp-check-routes, test/bulkwalkbug.pl, test/cammer, test/capturetest.pl, test/counter64-test.pl, test/d.pl, test/digital-bug, test/fore-test.pl, test/inexist.pl, test/list-bgp4-table, test/mcount.pl, test/mdebug, test/negative-counter.pl, test/sequence-bug.pl, test/set-test.pl, test/shipmr, test/snmptrap.note, test/snmptrap.pl:
+ New file.
+
+2003-06-30 Simon Leinen <simon.leinen at switch.ch>
+
+ * README: Updated Philippe Simonet's e-mail.
+
+ Added Valerio Bontempi and Lorenzo Colitti.
+
+ * lib/SNMP_Session.pm ($SNMP_Session::default_avoid_negative_request_ids):
+ Exported.
+
+2003-06-04 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm:
+ Changes from Lorenzo Colitti: Avoid eval() in some places to make code
+ more robust.
+
+2003-05-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/wwwtest: Also use CGI.pm to generate HTML.
+
+ * test/wwwtest (snmp_get, write_query_form): Use CGI.pm methods.
+
+ * test/wwwtest: Use CGI.pm for query parsing.
+
+ Open SNMPv2c_Session.
+
+ (parse_query): Deleted.
+
+ ($ipv4_only_p): New variable.
+
+ * test/test.pl: Parse option arguments: `-4', `-v (1|2)'.
+
+ * test/test.pl: Add subroutine prototypes; use strict.
+
+ Use additional arguments to SNMP_Session->open to activate IPv6 code.
+
+ * lib/SNMP_Session.pm (SNMPv1_Session::open):
+ Create an IO::Socket::INET object, rather than
+ an IO::Socket::INET6 object, whenever sockfamily is AF_INET.
+
+ (SNMPv2c_Session::open): Avoid trying to bless() an undefined object,
+ to improve error diagnostics.
+
+ * test/wwwtest: Added subroutine prototypes.
+
+ ($community_file_name): New variable.
+
+ (write_query_form): Compute <option> tags from @allowed_hosts.
+
+ * lib/SNMP_Session.pm (SNMPv1_Session::BEGIN):
+ Insist on version 1.26 when trying to import
+ IO::Socket::INET6.
+
+ (SNMPv1_Session::open): No longer pass PeerAddress and PeerPort when
+ creating the INET6 Socket object. This means that the socket will not
+ be connected. This, in turn, means that `lenient_address_matching'
+ and socket-recycling will have a chance of working.
+
+ * test/if-counters.pl: Add IPv6 support:
+
+ * Require SNMP_Session 0.96 or later.
+
+ * Accept `-4' option to set `ipv4only' in SNMP_Session->open() call.
+
+ * Updated usage message.
+
+ * test/walk-intf.pl: Added subroutine prototype for usage().
+
+2003-05-25 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm:
+ Added IPv6 changes by Valerio Bontempi and Lorenzo Colitti.
+
+ * lib/BER.pm: application_flag -> application_flag (),
+ context_flag -> context_flag ().
+
+ * lib/SNMP_Session.pm (encode_request_3):
+ encode_int_0 -> encode_int_0 ().
+
+2003-05-10 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm:
+ Integrated IPv6 patch by Valerio Bontempi and Lorenzo Colitti.
+
+2003-03-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/BER.pm: Mention Jan Kasprzak as a contributor.
+
+ * README: Added Jan Kasprzak.
+
+2003-03-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html, index.html: Copyright -> 2003.
+
+ * changes.html: Document decode_sequence() bug fix.
+
+ * lib/SNMP_Session.pm, lib/BER.pm ($VERSION): Increased to 0.95.
+
+ * lib/SNMP_util.pm: Added Jakob Ilves' return_array_refs support.
+
+ * lib/BER.pm (decode_sequence): Fix operator precedence bug.
+
+2003-02-27 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/if-counters.pl:
+ Clear the screen after traversing the interfaces. This helps when
+ interfaces are removed while the program is running.
+
+2003-02-24 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html:
+ Fixed decode_by_template() call in trap reception example.
+
+2002-10-27 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: Added pointer to test/capturetest.pl.
+
+ * MANIFEST: Added test/capturetest.pl.
+
+ * changes.html: Documented 0.94 changes to BER.pm and SNMP_Session.pm.
+
+ * README: Added Jakob Ilves to list of contributors.
+
+ * lib/BER.pm ($VERSION): Incremented to 0.94.
+
+2002-10-24 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm (request_response_5, open):
+ Support PDU capture buffer.
+
+ * lib/BER.pm (pretty_generic_sequence, decode_generic_tlv):
+ New functions for
+ debugging, contributed by Jakob Ilves <jakob.ilves at oracle.com>
+ (/IlvJa).
+
+2002-10-02 Simon Leinen <simon.leinen at switch.ch>
+
+ * README, index.html: Use "or" instead of "||" in examples.
+
+2002-09-12 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html:
+ Document $default_avoid_negative_request_ids in SNMP_Session.pm.
+
+ Document changes to SNMP_util.pm.
+
+ * test/trap-listener:
+ Added argument parsing and usage() message. The UDP port to listen on
+ can now be specified using `-p'.
+
+ Print better diagnostics for decoding errors.
+
+ * lib/SNMP_Session.pm (open):
+ Undid problematic "optimization" when computing the
+ possibly-negative request_id.
+
+ * index.html: Added pointer to IOG.
+
+2002-09-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($default_avoid_negative_request_ids):
+ New variable.
+
+ (encode_request_3): Honor `avoid_negative_request_ids'.
+
+ (open): Set `avoid_negative_request_ids', and initialize the
+ `request_id' slot accordingly.
+
+2002-05-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm (snmpwalk_flg):
+ Added missing initialization to suppress warning when
+ `-w' is in use.
+
+2002-05-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm: Updated to 0.93b1 from Mike.
+
+ * README.SNMP_util:
+ Updated to 0.93b1 from Mike - describe new snmpwalk() behavior (now
+ accepts multiple OIDs).
+
+ * lib/SNMP_util.pm (snmpwalk_flg):
+ Added missing initialization to suppress warning when
+ `-w' is in use.
+
+2002-03-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/msdpls ($css_stylesheet): Deleted.
+
+ (msdp_duplicate_report_header): Inline the CSS stylesheet.
+
+ (msdp_report_duplicate_sas): Use `pretty_ip_html'.
+
+ Format entries as a table.
+
+ (pretty_ip_html): New subroutine.
+
+2002-03-01 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/uli-fh-to-dns.pl: New file.
+
+2002-02-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/BER.pm:
+ (decode_oid, decode_by_template_2, decode_sequence, decode_string):
+ Abort and return error when the PDU is shorter than the length bytes
+ promised.
+
+2002-02-17 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/msdpls (msdp_fill_in_duplicates):
+ New subroutine that removes all
+ non-duplicate SAs from the hash and fills in the duplicates with more
+ information by doing specific walks.
+
+ * test/msdpls: Generate HTML output.
+
+2002-02-16 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/msdpls: Changed code to list duplicate SAs.
+
+2002-02-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/msdpls: New file.
+
+ * changes.html: Fixed text according to Mike Mitchell's mail.
+
+ * lib/SNMP_Session.pm (decode_trap_request):
+ Better error handling for decoding problems.
+
+ * lib/BER.pm (decode_by_template_2):
+ Better diagnostics for undefined or short PDUs.
+
+ * lib/BER.pm (decode_by_template):
+ Better diagnostics for undefined or short PDUs.
+
+2002-02-12 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION):
+ Incremented to 0.92, to synchronize with SNMP_util.pm.
+
+ * changes.html: Documented SNMP_util 0.92b2 changes.
+
+ * lib/SNMP_util.pm ($VERSION): Incremented to 0.92.
+
+ (@EXPORT): Export new symbol `snmpmaptable4'.
+
+ (snmpmaptable4): New subroutine.
+
+ Use map_table_start_end.
+
+ (snmpmaptable): Now implemented in terms of `snmpmaptable4'.
+
+ (toOID): Convert strings to indexes.
+
+ (snmpMIB_to_OID): New one-pass/two-pass MIB parsing code.
+
+ * README.SNMP_util: New version from Mike Mitchell (0.92b2):
+
+ Document how to set sessino parameters using a hash as the first OID.
+
+ Document snmpmaptable4.
+
+2002-01-30 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: Updated copyright notice.
+
+ Documented map_table_start_end fix.
+
+ * index.html, README, lib/BER.pm, lib/SNMP_Session.pm:
+ Updated copyright notice.
+
+ * test/if-counters.pl:
+ Fixed argument parsing so that target and options can be mixed.
+
+ * test/if-counters.pl (out_interface):
+ Suppress "unrouted VLAN..." interfaces.
+
+2002-01-26 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 0.91.
+
+2001-12-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm (SNMPv2_Session::map_table_start_end):
+ Added comments.
+
+ Fixed bug that caused a superfluous query to be sent when the table
+ was already fully received.
+
+2001-12-12 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/sorrento-nest-list (%short_types): Recognize "GM-GE2-2.5G-A".
+
+2001-12-11 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/sorrento-nest-list (%short_types):
+ Recognize "GM-GE2" as channel ("c") card.
+
+ * test/sorrento-nest-list:
+ Applied the new naming scheme Chris Watts (Ascom) and myself had
+ agreed upon.
+
+2001-12-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added /ref=nosim/ to Amazon book pointers.
+
+2001-11-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/lambda-webmon.pl (get_amp_status):
+ New subroutine that does all the SNMP requests.
+
+ (make_amp_html_page): Renamed from `make_html_page'.
+
+2001-11-28 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/lambda-webmon.pl (@eastbound_amps, @westbound_amps):
+ New naming convention.
+
+2001-11-24 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/lambda-webmon.pl: New file.
+
+2001-11-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/lambda-monitor.pl ($inverse_video):
+ New variable. Default is normal video now.
+
+ (show_light_trail): dbM -> dBm.
+
+ (print_html_trailer): Added color legend.
+
+ (class_for_amp_status): New subroutine that converts the amplifier
+ status bits into a class by finding the highest bit set.
+
+ * test/lambda-monitor.pl: Added HTML output.
+
+ * test/lambda-monitor.pl: New file.
+
+2001-11-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: Documented new SNMP_util.pm and if-counters.pl.
+
+ * index.html: Added pointer to "Essential SNMP".
+
+ * test/snmpspeed.pl: *** empty log message ***
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 0.90.
+
+ * test/if-counters.pl:
+ Implemented Counter64 support (activated by "-l" argument):
+
+ Use Math::BigInt.
+
+ ($counter64_p, $ifHCInOctets, ifHCOutOctets): New variables.
+
+ (rate_32): Added optional multiplier argument.
+
+ (rate_64, rate, rate_or_0): New subroutines that generalize rate_32.
+
+ (out_interface): Use rate_or_0 rather than rate_32.
+
+ Use ifHCOutOctets/ifHCOutOctets if $counter64_p is set.
+
+ Always request ifAlias for the interface description.
+
+ (usage): Document -l flag.
+
+ * lib/SNMP_util.pm: Fixed typo in comment.
+
+ * test/snmpwalkh.pl: Fixed typo in comment (Example 2 -> Example 3).
+
+ * test/snmpwalkh.pl: New version ("0.90b2") from Mike Mitchell.
+
+ * lib/SNMP_util.pm: New version ("0.90b2") from Mike Mitchell:
+
+ (snmpwalk, snmpwalkhash): Now implemented as trampolines to
+ snmpwalk_flg.
+
+ (snmpopen): Bug fix in handling of the optional "port" argument.
+
+2001-11-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm (error_return, error, ber_error):
+ Moved downward in the file, but
+ forgot why.
+
+2001-10-19 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/if-counters.pl (rate_32):
+ New subroutine that computes a rate from two Counter32
+ values and an interval.
+
+ (out_interface): Use it.
+
+2001-10-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/sorrento-nest-list (%nestmasters): Added muxBE1.
+
+2001-09-18 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/sorrento-nest-list (%nestmasters): Added muxCE1.
+
+2001-09-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/sorrento-nest-list: New file.
+
+2001-08-27 Simon Leinen <simon.leinen at switch.ch>
+
+ * README.SNMP_util:
+ Added documentation for snmpwalkhash (Mike Mitchell).
+
+ * changes.html (SNMP_util.pm):
+ Mentioned new version 0.89 with new snmpwalkhash
+ subroutine.
+
+ * MANIFEST (test/snmpwalkh.pl): Added.
+
+ * test/snmpwalkh.pl: New file.
+
+ * lib/SNMP_util.pm (snmpwalkhash): New function.
+
+2001-08-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html:
+ lenient_source_port_matching now defaults to 1 rather than 0.
+
+ * lib/SNMP_Session.pm (SNMPv1_Session::open):
+ Default lenient_source_port_matching to 1
+ rather than 0.
+
+2001-07-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/BER.pm (pretty_print):
+ Handle SNMPv2 exception codes by calling error(),
+ which returns undef and leaves a message in $errmsg.
+
+ * changes.html: error code -> exception code.
+
+ * lib/SNMP_Session.pm: Added missing/new credits.
+
+ * changes.html: Document request-id range bug fix.
+
+ * lib/SNMP_Session.pm (encode_request_3, open):
+ Make sure that the request_id is always in
+ the range -2^31 to (2^31)-1. Thanks to Sergio Macedo
+ <macedo at tmp.com.br> for noticing this bug.
+
+2001-07-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: *** empty log message ***
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 0.88.
+
+ * README: Acknowledged Michael Deegan <michael at cnspc18.murdoch.edu.au>.
+
+ * lib/SNMP_Session.pm (package SNMPv1_Session, package SNMPv2_Session):
+ Added missing `use
+ Carp;'.
+
+2001-06-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/if-counters.pl:
+ Print comment for non-Cisco routers, now that we get it from ifAlias.
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to reflect BER.pm change.
+
+ * lib/BER.pm: Slightly modified patch from Bert Driehuis:
+
+ (snmp_nosuchobject, snmp_nosuchinstance, snmp_endofmibview): New
+ (constant) subroutines.
+
+ (pretty_print): Convert SNMPv2 error codes to undef.
+
+ * changes.html: Documented new SNMPv2 error code handling.
+
+ * README (Contributors): Added Bert Driehuis.
+
+2001-05-28 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/if-counters.pl:
+ Use ifAlias rather than locIfDescr. The advantages are that ifAlias
+ is not Cisco-specific, and it is defined even on ATM subinterfaces on
+ Ciscos.
+
+2001-05-25 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/snmpmap_table-test.pl: Shortened the code for easier reading.
+
+2001-05-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: Note new behavior of BER::pretty_print.
+
+ * lib/BER.pm ($VERSION): Increased to 0.86.
+
+ * lib/BER.pm (pretty_print):
+ Return undef if original value is undefined.
+
+ * test/snmpmap_table-test.pl: New file.
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 0.86.
+
+ * changes.html: Added note on snmpmaptable.
+
+ * lib/SNMP_util.pm: Indentation changes.
+
+ (snmpmaptable): Renamed from snmpmap_table.
+
+ Solved FUNARG problem.
+
+ * README.SNMP_util (snmpmaptable): Renamed from snmpmap_table.
+
+ * README.SNMP_util, lib/SNMP_util.pm:
+ snmpmap_table: New from Mike Mitchell.
+
+2001-05-09 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm: 0.86b2 from Mike Mitchell.
+
+ * changes.html: *** empty log message ***
+
+ * lib/SNMP_util.pm (snmpopen): Avoid using $port when it's undefined.
+
+ * README.SNMP_util, lib/SNMP_util.pm: 0.86b1 from Mike Mitchell.
+
+2001-05-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm (open):
+ No longer try to convert local address using inet_aton(),
+ because IO::Socket::INET->new does that.
+
+ * lib/SNMP_Session.pm: Use Carp; warn -> carp, die -> croak.
+
+ * README: Added Alistair Mills.
+
+ Clarified documentation on pretty-printing OIDs.
+
+ * lib/SNMP_Session.pm (open, sa_equal_p):
+ Added support for `lenient_source_port_matching'.
+
+2001-05-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * changes.html: New file.
+
+ * index.html: Moved changes to `changes.html'.
+
+ Clarified pretty-printing.
+
+ * lib/SNMP_util.pm (snmpopen): Default port to 161 for non-type-1.
+
+ * lib/SNMP_util.pm: New version 0.85 from Mike Mitchell:
+
+ Accept HASH as first OID to set SNMP options.
+
+ Use Carp for error messages.
+
+2001-03-07 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/mrtg-ipmcast: Removed `cisco-' from name.
+
+ * test/mrtg-ipmcast (usage): Improved help message.
+
+ * test/mrtg-ipmcast: Support `-p' flag.
+
+ (usage): Defined.
+
+ * test/mrtg-ipmcast: Added SNMPv2 support.
+
+ * test/mrtg-ipmcast: Removed SWITCH specifics.
+
+2001-01-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm: Version 0.84 received from Mike.
+
+2000-12-19 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm:
+ New version from Mike Mitchell. The local host can now be specified
+ along with the port. See README.SNMP_util for the syntax.
+
+ * README.SNMP_util:
+ New version from Mike Mitchell. Documents SNMP version and local
+ address specifications.
+
+2000-12-18 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: *** empty log message ***
+
+ * lib/SNMP_Session.pm ($VERSION): Increased to 0.83.
+
+ (open): Allow local address to be specified by an additional optional
+ argument.
+
+ Set new slot `lenient_source_address_matching' to 1.
+
+ (request_response_5): Don't resend on reception on unmatched replies.
+
+ (sa_equal_p): This is now a method on session objects.
+
+ If the session's `lenient_source_address_matching' slot is set, don't
+ compare host addresses, just ports.
+
+2000-12-10 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Document retry fix.
+
+ * lib/SNMP_Session.pm (request_response_5):
+ Rearranged so that we will no longer send an
+ additional retry for whose response we won't wait anymore.
+
+2000-12-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * README: Added Brett T Warden to contributors list.
+
+ * index.html: Documented Brett T Warden changes.
+
+ * lib/SNMP_Session.pm ($VERSION):
+ Incremented to pick up change in BER.pm.
+
+ ($default_retries): Clarified header comment.
+
+ * lib/BER.pm ($VERSION): Incremented.
+
+ * lib/BER.pm (pretty_print): Pass UInteger32 to pretty_unsignedlike().
+
+ (encode_oid): Accept OIDs of length 2 (such as 0.0).
+
+ * lib/BER.pm (encode_oid): Fix by Rik Hoorelbeke for large subids.
+
+2000-11-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added credits for Rik Hoorelbeke.
+
+ Document changes to encode_oid() and SNMPv2c_Session::map_table.
+
+ * README: Added credits for Rik Hoorelbeke.
+
+2000-10-30 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Document sa_equal_p change.
+
+ * lib/SNMP_Session.pm (sa_equal_p): New function.
+
+ (receive_response_3): Use `!sa_equal_p' rather than `ne' to compare
+ addresses.
+
+ * test/if-counters.pl: Added forward declarations.
+
+2000-10-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/map-table.pl: Recognize `-v 2'.
+
+ * test/if-status.pl: Added forward declarations.
+
+ Added SNMPv2 support.
+
+ * test/if-status.pl: New file.
+
+2000-10-17 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Documented recent changes.
+
+ * lib/SNMP_Session.pm (request_response_5):
+ Updated comment according to recent change in
+ receive_response_3.
+
+ * lib/SNMP_Session.pm ($recycle_socket):
+ New exported variable. When this is, all new
+ SNMP_Session objects will share a single UDP socket.
+
+ (open_trap_session): Use `undef' rather than 0.0.0.0 as the remote
+ address in the SNMP_Session structure.
+
+ (receive_response_3): Changed handling of mismatched source
+ addresses. If the source address of an incoming packet doesn't match
+ the expected address (as specified by the session's `remote_addr'
+ slot), the PDU is always ignored and zero is returned. Also, if the
+ $recycle_socket variable is set, mismatched replies never cause a
+ warning even when the session's `debug' slot is set.
+
+ (to_string): Handle session objects with undefined `remote_addr'.
+
+ * lib/BER.pm (encode_uinteger32, encode_counter32, encode_counter64, encode_gauge32):
+ New exported subroutines.
+
+ * test/trap-listener: Added forward declarations.
+
+2000-10-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html, README: Removed MIB Parsing stuff.
+
+ * test/list-bgp4-neighbors:
+ Improved output; in particular the hostname is now be printed if
+ possible.
+
+ * trap-send.pl: foo
+
+ * MANIFEST (test/sun-find-process): Added.
+
+ * lib/SNMP_Session.pm (map_table_start_end):
+ Rewrote to cope with holes in tables. Escape
+ to SNMPv1 (get-next) code if the new `use_getbulk' slot in the session
+ object is NOT set.
+
+2000-10-02 simon <simon.leinen at switch.ch>
+
+ * test/walk-test.pl:
+ Don't ask for ipAdEntAddr in map_table because that can be derived
+ from the index.
+
+2000-09-24 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/max-list-sessions: Added header comment.
+
+ * test/max-list-sessions (out_session, out_call): Removed.
+
+ * test/max-list-sessions:
+ Print out just one table. The callStatusTable is now used to populate
+ entries in the session entries that had been created from
+ ssnStatusTable.
+
+2000-07-09 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm (%SNMP_util::OIDS):
+ Moved around to keep lexicographical order.
+
+2000-06-20 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/mrtg-ipmcast: New file.
+
+ * test/README: Added `sun-find-process'.
+
+ * test/sun-find-process: New file.
+
+2000-05-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_util.pm (%SNMP_util::OIDS):
+ Added OIDs from IF-MIB (RFC1573).
+
+ * lib/SNMP_util.pm: Recognize SNMP version specifier.
+
+2000-05-27 Simon Leinen <simon.leinen at switch.ch>
+
+ * MANIFEST (test/asn1-test.pl):
+ Removed because it refers to the ASN_1.pm module,
+ which I started writing three years ago but never finished, and which
+ is (correctly) not included in the distribution.
+
+2000-05-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/if-counters.pl: Added `-D' option to show drops (ifOutDiscards).
+
+ Be more modular with respect to printing/reading the different
+ optional columns.
+
+2000-04-12 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/SNMP_Session.pm: Fixed header comment.
+
+2000-04-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/list-ospf-neighbors, test/list-bgp4-neighbors: New file.
+
+2000-03-30 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/if-counters.pl: Allow port to be specified (-p).
+
+ * test/trap-listener ($port): New variable.
+
+ This should be settable by a command-line option.
+
+ * test/README: Added documentation and sorted alphabetically.
+
+ * README: Added contributors: Paul E. Erkkila, Johannes Demel.
+
+ * MANIFEST (test/cisco-cpus, test/cisco-memory): Added.
+
+ * index.html: Document Counter64 support and map_table improvement.
+
+ * lib/SNMP_Session.pm (SNMPv2c_Session::map_table_start_end):
+ Added $expected_oid_count hack
+
+2000-03-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * lib/BER.pm (encode_intlike, decode_intlike_s):
+ Added code to handle big integers
+ using Math::BigInt.
+
+ * test/ber-test.pl: Added a test for encoding of big integers.
+
+ * test/ber-test.pl: Added tests for huge integers.
+
+2000-02-24 Simon Leinen <simon.leinen at switch.ch>
+
+ * test/cisco-cpus: New file.
+
+2000-02-08 simon <simon.leinen at switch.ch>
+
+ * README.SNMP_util: Refer to Artistic license.
+
+ Adapted to main README.
+
+ * lib/SNMP_util.pm (snmpMIB_to_OID):
+ Map OBJECT-IDENTITY to OBJECT IDENTIFIER.
+
+2000-01-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html:
+ Added a pointer to the parent (SNMP) page (to attract people to my
+ Amazon pointers 8-).
+
+ * index.html: Fixed HTML error.
+
+ * test/if-counters.pl, README, lib/BER.pm, index.html, lib/SNMP_Session.pm:
+ Copyright 2000.
+
+ * lib/SNMP_Session.pm ($VERSION): Incremented to 0.76.
+
+ * test/walk-test.pl: Added SNMPv2 (getBulk) support.
+
+ * ChangeLog: *** empty log message ***
+
+ * index.html: foo
+
+ * test/if-counters.pl:
+ No longer retrieves Cisco-specific variables by default. Use the new
+ option `-c' to re-activate this.
+
+ * lib/SNMP_Session.pm (default_max_repetitions): Make settable.
+
+ (debug): New read/write method.
+
+ (map_table_start_end): Added some debugging messages.
+
+1999-12-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Use > rather than > in a few places.
+
+
+1999-12-15 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Use > rather than > in a few places.
+
+1999-10-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Clarified SNMPv2 traps.
+
+ * README: Documented new features (SNMPv2 Traps).
+
+ * index.html:
+ Changed documentation of v2_trap_request_send for new signature.
+
+ * index.html: Documented changes in version 0.75.
+
+1999-09-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added note about removed uninitialized variable warning.
+
+ * index.html: Added change log entry for SNMP_util.pm 0.72.
+
+1999-09-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * MANIFEST (Artistic): Added.
+
+ * Makefile.PL (dist): Use "gzip -9f" for compression.
+
+ * Makefile.PL: New file.
+
+ * index.html, README:
+ Added copyright and pointer to "Artistic" license.
+
+ * Artistic: New file.
+
+1999-09-02 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Documented introduction of IO::Socket.
+
+1999-07-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Documented $BER::pretty_print_timeticks.
+
+1999-06-30 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Mention changes in new SNMP_util.pm.
+
+ * README.SNMP_util: Upgraded to 0.71 from Mike Mitchell.
+
+1999-04-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * mibparse.pl: New file.
+
+1999-04-07 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added pointer to README.SNMP_util file.
+
+ * index.html:
+ Document 0.70 changes (MIB parsing addition to lib/SNMP_util.pm</tt>.
+
+ * README.SNMP_util:
+ Update from Mike Mitchell to include snmpMIB_to_OID().
+
+1999-03-10 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Document new community string parsing.
+
+1999-02-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Documented changes for SNMP_util.pm 0.57 -> 0.58.
+
+1999-02-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Document agent methods.
+
+ * README: Added Mike McCauley's credit.
+
+ * MANIFEST (test/SNMPAgent.pm): Added.
+
+1999-02-21 simon <simon.leinen at switch.ch>
+
+ * index.html: Fixed a few Weblint warnings.
+
+ * index.html: Added note about really big tables.
+
+ * index.html: Documented map_table implementation for SNMPv2c.
+
+ Documented map_table_4.
+
+ * MANIFEST (test/if-counters.pl): Added.
+
+1999-02-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Documented recent changes.
+
+ * README: Added Alan Nichols (Sun) to the list of contributors.
+
+1999-02-17 Simon Leinen <simon.leinen at switch.ch>
+
+ * mibtree.pl: New file.
+
+1999-01-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added note about bind() error message fix.
+
+1998-12-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Document SNMP_util.pm change.
+
+1998-12-11 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added Mike Mitchell's changes.
+
+ * README.SNMP_util: New version from Mike Mitchell.
+
+1998-11-16 Simon Leinen <simon.leinen at switch.ch>
+
+ * MANIFEST (test/sun-ps): Added.
+
+ * MANIFEST: New file.
+
+ * index.html: Document fixed response matching logic.
+
+1998-10-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile.orig (TESTSRCS): Added test/discover.
+
+ * index.html: Point to FTP directory rather than the archive directly.
+
+ * index.html: Mentioned Clinton Wong's changes.
+
+1998-10-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added a note on broadcast/multicast support.
+
+1998-09-08 Simon Leinen <simon.leinen at switch.ch>
+
+ * README, index.html: Fixed sample trap-reception code.
+
+1998-08-19 Simon Leinen <simon.leinen at switch.ch>
+
+ * trap-send.pl: New file.
+
+1998-08-18 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile.orig (DOCS): Added README.SNMP_util.
+
+ * README.SNMP_util: New file.
+
+1998-08-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Simple.pm: New file.
+
+1998-08-10 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added pointer to FTP location.
+
+1998-07-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Get html40 validator icon locally.
+
+1998-07-16 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Made HTML 4.0 (Transitional) compliant.
+
+1998-07-01 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile.orig (TESTSRCS): Added trap-send and trap-listener.
+
+ * index.html, README: Added documentation about receiving traps.
+
+1998-07-01 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm ($VERSION): Upped version to 0.60.
+
+ (open): Accept optional additional argument BIND_TO_PORT.
+
+ (open_trap_session): New method, calls open with BIND_TO_PORT 162
+ (or an alternate port given as an optional argument).
+
+ (decode_trap_request, receive_trap): New methods for decoding and
+ receiving SNMPv1 traps, respectively.
+
+ * BER.pm (pretty_uptime_value):
+ New function split off from pretty_uptime.
+
+ ($template_debug): New variable, controls debugging output in
+ decode_by_template_2.
+
+ (decode_by_template_2): Added debugging output.
+
+ Fixed computation of position of error in template string.
+
+ Added %A (IP address), %u (uptime).
+
+ (decode_int, decode_string): Better error reporting, include
+ erroneous tags.
+
+ * Makefile (TESTSRCS): Added trap-send and trap-listener.
+
+ * index.html, README: Added documentation about receiving traps.
+
+1998-06-24 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm (set_timeout, set_retries, set_backoff):
+ Fixed warning.
+
+ * SNMP_util.pm (snmptrap): Added upTime handling.
+
+ * index.html: Documented new methods and map_table fix.
+
+ * SNMP_Session.pm (set_timeout, set_retries, set_backoff):
+ New methods.
+
+ (map_table_start_end): Fixed comparison logic so that an index of
+ "0" no longer terminates the table.
+
+1998-06-11 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Updated RFC 1213 reference for new server.
+
+ * index.html: Updated pointer to mrtg page.
+
+1998-06-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Noted SNMP_util.pm.
+
+ Shortened section about Higher-Level APIs.
+
+ * README: Updated to be in line with the current index.html.
+
+ * Makefile (PKGSRCS): Added SNMP_util.pm.
+
+ * BER.pm: Added exported subroutine `encode_timeticks'.
+
+ Upped version to 0.58.
+
+1998-06-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm: Added exported subroutine `encode_timeticks'.
+
+ Upped version to 0.58.
+
+1998-06-02 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_util.pm: New version from Mike Mitchell:
+
+ I've changed the API so that the first argument is
+ 'community at host:port'.
+
+ * SNMP_util.pm: New file.
+
+1998-05-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm (get_request_response,
+ set_request_response,getnext_request_response): Pass 1 rather
+ than 0 as the $error argument.
+
+ (receive_response_3): Fix a bug which prevented the $errorp
+ argument from being passed correctly.
+
+1998-05-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile (TESTSRCS): Added test/if-to-routes.pl.
+
+1998-05-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm: Upped verision number to 0.57.
+
+ Added credit to Mike Diehn for encode_ip_address.
+
+ * index.html: Added sections about walking tables and sending traps.
+
+1998-04-30 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm ($VERSION): Upped to 0.58.
+
+ (trap_request_send): Return 1, not 0, on success.
+
+ * BER.pm (encode_ip_address): New exported subroutine.
+
+ (encode_intlike): New subroutine, could be used to encode
+ integer-like data such as uptime.
+
+1998-04-21 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm: Added encoding of traps, courtesy Mike Mitchell
+ <mcm at unx.sas.com>.
+
+1998-04-09 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm (request_response_5, receive_response_3,
+ unwrap_response_6): Replace request_response_4,
+ receive_response_2, and unwrap_response_5, respectively. Added
+ $errorp argument to control error handling.
+
+ (map_table): Count calls to walk function and return the total
+ number.
+
+ No longer pretty_print the table entries before passing them to
+ the walk function.
+
+ (get_request_response, set_request_response,
+ getnext_request_response, send_query): Added formal argument
+ lists.
+
+ (SNMPv1_Session::open): Initialize new slots `error_status' and
+ `error_index' to be used for user-supplied error handling.
+
+1998-04-06 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile (TESTSRCS): Renamed test/test_table.pl to
+ test/test-table.pl for consistency.
+
+ * SNMP_Session.pm (map_table, map_table_start_end, index_compare,
+ oid_diff): New subroutines.
+
+ (index_compare, oid_diff): New exported names.
+
+ * Makefile (TESTSRCS): Added new table test scripts.
+
+1998-03-25 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Document two new fixes.
+
+ * BER.pm (encode_int): New version by Mike Mitchell
+ <mcm at unx.sas.com>.
+
+ * README: Added Mike Mitchell to the list of contributors.
+
+1998-03-25 Simon Leinen <simon.leinen at switch.ch>
+
+ * README: Added Mike Mitchell to the list of contributors.
+
+1998-03-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm (pretty_uptime): Be careful to use integer arithmetic.
+
+ ($VERSION): 0.54 -> 0.55.
+
+1998-03-05 Simon Leinen <simon.leinen at switch.ch>
+
+ * README: Added Niels Bakker to the credits.
+
+1998-02-14 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm ($VERSION): -> 0.54.
+
+ * BER.pm: Changed the spacing in prototype argument lists so that
+ Perl 5.003 likes it.
+
+1998-02-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm: Removed forgotten diagnostic message.
+
+ Made spacing before parentheses consistent.
+
+1998-02-13 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm: Removed forgotten diagnostic message.
+
+ Made spacing before parentheses consistent.
+
+1998-02-11 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm: Use prototypes consistently.
+
+ Fixed two benign errors found by this.
+
+1998-01-30 Simon Leinen <simon.leinen at switch.ch>
+
+ * README (Credits): Added Dan Cox and Iouri Pakhomenko.
+
+ * index.html: Mentioned that Iouri Pakhomenko also noted the
+ error_return bug.
+
+1998-01-29 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Mention Dan Cox' fix to return_error.
+
+ * SNMP_Session.pm ($VERSION): 0.55 -> 0.56.
+
+ * SNMP_Session.pm (request_response_4):
+ Call $this->error when no response is received.
+
+ (error_return): Converted to a method, so that SNMPv1_Session
+ inherits it.
+
+ * index.html: SNMPv2 -> SNMPv3.
+
+ * index.html: Noted that leading dots are now ignored in OIDs.
+
+1998-01-07 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile (INSTALL):
+ ginstall -> install. GNU install is no longer installed in
+ our new environment. However, Solaris 2.6 install seems to be
+ compatible enough to BSD install at last.
+
+ * BER.pm ($VERSION): Incremented to 0.52.
+
+ * BER.pm (encode_oid): Ignore leading dot.
+
+1997-12-22 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm ($errmsg, $suppress_warnings):
+ New variables for silent error
+ handling.
+
+1997-12-03 Simon Leinen <simon.leinen at switch.ch>
+
+ * ChangeLog: *** empty log message ***
+
+ * index.html: Documented recent changes.
+
+ * SNMP_Session.pm: Upped version number.
+
+ * BER.pm: No longer use integer.
+
+ No longer call "warn".
+
+ * SNMP_Session.pm: Handle encoding errors in a sensible way.
+
+ ($SNMP_Session::errmsg, $SNMP_Session::suppress_warnings): New
+ variables that give the caller more control about error messages.
+
+Wed Dec 3 20:05:50 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Documented recent changes.
+
+ * BER.pm: No longer use integer.
+
+ No longer call "warn".
+
+ * SNMP_Session.pm: Handle encoding errors in a sensible way.
+
+ ($SNMP_Session::errmsg, $SNMP_Session::suppress_warnings): New
+ variables that give the caller more control about error messages.
+
+ Upped version number.
+
+Mon Dec 1 12:38:42 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm (decode_intlike_s): Multiply with 256 rather than shifting 8.
+
+Fri Nov 28 21:59:11 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm:
+ Better error handling. No longer calls die(), but returns undefined
+ values and leaves an error message in $BER::errmsg.
+
+ * SNMP_Session.pm (ber_error):
+ New subroutine that passes an error up from the BER module.
+
+ (unwrap_response_5): Use it.
+
+Tue Nov 25 13:58:40 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * README, index.html: Found an application for sending traps.
+
+Fri Nov 14 19:25:22 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Added test/arp.
+
+ * Makefile (TESTSRCS): Added test/arp.
+
+Fri Oct 31 18:21:36 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile (dist): Used to be called `shar'.
+
+ Install index.html, tol.
+
+ * index.html: Moved description of last change forward.
+
+ * SNMP_Session.pm ($VERSION): Incremented.
+
+ * SNMP_Session.pm: Added Daniel L. Needles' change to avoid
+ passing numeric IP addresses to inet_ntoa().
+
+ * README: Give credit to Daniel L. Needles.
+
+ * index.html: Mention Dan L. Needle's contribution.
+
+Mon Sep 22 19:25:36 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm (hex_string, hex_string_of_type): New subroutines.
+
+Fri Aug 22 10:54:00 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Bradford T. -> Brad.
+
+Thu Aug 21 14:05:37 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html, README (Set Requests): Point to test/set-test.pl.
+
+ * index.html: Added missing `mailto:' to URL.
+
+Tue Aug 19 14:43:17 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm: Use more canonic e-mail address for matter.
+
+ * Makefile (TESTSRCS): Added set-test.pl.
+
+ * README, index.html: Use more canonic e-mail address for matter.
+
+Sat Aug 16 00:06:21 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile (TESTSRCS): Added some, removed party-test.pl.
+
+ (DOCS): Added test/README, index.html.
+
+ (PKGSRCS): Removed Party.pm.
+
+ (TESTSRCS): Renamed from PROGSRCS.
+
+ Moved all test scripts to `test' subdirectory.
+
+ * index.html, README: snmp-set Support: Added credits for matter,
+ small description on snmpset usage, updated to-do items.
+
+ * SNMP_Session.pm: Added set-request support based on code
+ contributed my Matthew Trunnell <matter at media.mit.edu>:
+
+ (encode_request): The former list of encoded OIDs can now also
+ contain OID/value pairs. Those are represented by references to
+ two-element arrays, the encoded OID and the encoded value.
+
+ (encode_set_request): New subroutine.
+
+ (set_request_response): New subroutine.
+
+ (request_response_4): Return undef as soon as an error response
+ packet has been received, rather than retrying.
+
+ (unwrap_response_5): Recognize OID/value pairs (but print only the
+ OID).
+
+ This fix doesn't really have anything to do with set-request
+ support:
+
+ (receive_response_2): Undefine the "unwrapped" cache so that you
+ don't get the data from the last request if you ignore the error
+ return.
+
+ ($VERSION): Incremented to 0.52.
+
+Thu Jul 31 11:56:31 1997 leinen <leinen at bolivar>
+
+ * test.pl: Fixed pathname to Perl.
+
+ * SNMP_Session.pm (open): Better error messages.
+
+ * SNMP_Session.pm: Better diagnostics:
+
+ (request_response_4): Renamed from request_response_3, take
+ additional argument $oids.
+
+ (receive_response_2): Renamed from receive_response_1, take
+ additional argument $oids.
+
+ (unwrap_response_5): Renamed from unwrap_response_4, take
+ additional argument $oids. Use this to generate error message
+ when we have an errorIndex.
+
+ (error): New method to print an error message referring to a
+ specific session.
+
+ (to_string): New function, factored out from `describe'. Be much
+ more verbose.
+
+ ($VERSION): Incremented to 0.51.
+
+Wed Jul 9 16:17:30 1997 leinen <leinen at bolivar>
+
+ * index.html: Document recent changes: version numbers, unsigned
+ printing.
+
+ * SNMP_Session.pm: Added versioning as supported by Exporter.pm.
+
+ * BER.pm: Don't die in a few places.
+
+ ($VERSION): New variable, initially 0.50.
+
+ (version): New subroutine.
+
+Wed Jul 2 16:02:23 1997 leinen <leinen at bolivar>
+
+ * BER.pm (pretty_uptime): Avoid negative times.
+
+ * BER.pm: Removed debugging version of decode_intlike_s.
+
+ (pretty_uptime): Use decode_unsignedlike rather than
+ decode_intlike.
+
+ * BER.pm: Buggy version that should work with mrtg on all systems.
+
+Thu Jun 12 09:05:17 1997 leinen <leinen at bolivar>
+
+ * README: Updated sample code.
+
+ Added pointer to HTML page.
+
+Thu Jun 12 09:00:34 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * test.pl, walk-test.pl:
+ Better error message for session open failure.
+
+ * index.html: Updated for new error handling and adapted sample
+ code, note dependency on Perl 5.002, note use of strict.
+
+Thu Jun 12 08:47:53 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm, SNMP_Session.pm: Require Perl 5.002.
+
+ * SNMP_Session.pm: Be less strict.
+
+Mon Apr 28 18:55:20 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * README, index.html: Moved my home page to
+ http://www.switch.ch/misc/leinen/.
+
+Thu Apr 3 14:08:26 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Point to .tar.gz rather than .shar.
+
+ * Makefile (shar): Make a tar.gz file, too, and install both under
+ $(DESTDIR).
+
+ * BER.pm (encode_int_0): New exported subroutine that generates
+ the BER encoding for the integer zero.
+
+ * SNMP_Session.pm: Never die on exceptional situations, but write
+ a warning and return undef.
+
+ (encode_request): Use encode_int_0.
+
+ (unwrap_response_4): Better diagnostics for unparseable and error
+ responses.
+
+Mon Mar 24 09:16:03 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm: Handle unsigned integers correctly.
+
+Fri Feb 21 09:07:03 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm (unwrap_response_4): Integrated fix by Tobi
+ Oetiker for doubly declared lexical variable $request_id.
+
+Mon Feb 17 13:31:39 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * index.html: Mrtg: Don't mention beta version 2.0 anymore, since
+ this has now been released.
+
+ * README, index.html: SNMP_Session::open -> SNMP_Session->open
+ (thanks to "Robert Weatherford(grim)" <rweather at ctron.com>)
+
+Mon Feb 17 13:26:16 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * BER.pm (@EXPORT): Export `encoded_oid_prefix_p'.
+
+ (encoded_oid_prefix_p): Handle subids > 127.
+
+ Return length of prefix in second encoded OID as a result.
+
+ (decode_subid): New subroutine that decodes a single subid from an
+ encoded OID.
+
+Sun Feb 2 16:47:30 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * asn1-test.pl: Added test for sequence decoding.
+
+ * ASN_1.pm (ASN_1::Sequence): Fixed `new' method.
+
+ (decode): Added Sequence decoding.
+
+ * asn1-test.pl, ASN_1.pm: Initial revision
+
+Sat Feb 1 17:42:01 1997 Simon Leinen <simon.leinen at switch.ch>
+
+ * Makefile (PROGSRCS): Added ber-test.pl.
+
+ * BER.pm: Use integer.
+
+ (decode_intlike): Simplified by writing a proper loop to decode
+ octet by octet. This should also solve any problems with signed
+ vs. unsigned.
+
+ * ber-test.pl (decode_intlike_test): New subroutine.
+
+ (regression_test): Use it.
+
+ * ber-test.pl: Use integer.
+
+ Added several test cases.
+
+ * ber-test.pl: Initial revision
+
+ * BER.pm (regression_test): Removed. This is now in ber-test.pl.
+
+Wed Jan 15 08:56:54 1997 leinen <leinen at ohiggins>
+
+ * SNMP_Session.pm: Backed out the changes between revisions 1.27
+ and 1.28 that used the new subroutines in Socket.pm. It turned
+ out that those routines require Perl 5.002 or later. Put the new
+ code back in and commented it out with clear notices - look for
+ the markers "Perl 5.002 or later" and "end of pre-5.002 code".
+
+Tue Jan 14 09:01:57 1997 leinen <leinen at ohiggins>
+
+ * BER.pm, SNMP_Session.pm: Commented out use strict/use vars and
+ explain why.
+
+Mon Jan 13 13:28:04 1997 leinen <leinen at ohiggins>
+
+ * BER.pm: Updated my e-mail address in the header comment.
+
+Tue Jan 7 16:38:48 1997 leinen <leinen at ohiggins>
+
+ * index.html: Updated from latest README.
+
+ Added section with recent changes and credits.
+
+ * README: Updated from latest index.html.
+
+ * SNMP_Session.pm: Make better use of the functionality in
+ Socket.pm.
+
+Thu Dec 26 17:31:32 1996 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm (receive_response_1): Perform the address check
+ only if debugging is set on the session.
+
+ * SNMP_Session.pm (receive_response_1): Don't return failure if
+ response comes from a different address. Also, the warning is
+ only issued if debugging is set for the session.
+
+Fri Dec 20 14:50:03 1996 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm: Import @ISA from vars.
+
+ Added debugging.
+
+ Fixed header comment.
+
+ (unwrap_response_4): Removed spurious argument to
+ decode_by_template().
+
+ * BER.pm: Use strict.
+
+ (pretty_print): Declare $result as lexical variable.
+
+ (decode_by_template): Implement %i and %s without prefix arguments.
+
+ (encoded_oid_prefix_p): Declare $subid1, $subid2 lexical.
+
+Fri Dec 20 09:56:28 1996 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm ($default_timeout, $default_retries,
+ $default_backoff): New values. Improved following discussions on
+ the mrtg mailing list.
+
+ (encode_request): Increment request ID.
+
+ (request_response_3): Renamed from `request_response'. Added
+ `response_tag' argument. Unwrap each received response. If this
+ fails because of mismatching community or request ID, just ignore
+ the request and retry.
+
+ (SNMPv1_Session::open): Cast the random request ID to an int.
+
+ (unwrap_response_4): Renamed from `unwrap_response'. Added
+ `request_id' argument.
+
+ Check community and request ID and return undefined if there is a
+ mismatch.
+
+ (receive_response_1): Renamed from `receive_response'. Added
+ `response_tag' parameter.
+
+ Fixed report of response from bad address.
+
+Tue Dec 17 18:16:55 1996 Simon Leinen <simon.leinen at switch.ch>
+
+ * SNMP_Session.pm: Stripped contributor entries.
+
+ (decode_get_response): Call unwrap_response via eval and simply
+ return zero when it dies.
+
+ (receive_response): Return zero if response comes from different
+ address.
+
+ (request_response): Wait for another packet when receive_response
+ fails.
+
+ * BER.pm: Contributors list: slightly fixed.
+
+ (encode_oid): Removed comments.
+
+Tue Dec 17 13:54:42 1996 Simon Leinen <simon.leinen at switch.ch>
+
+ * walk-test.pl: Changed path to Perl.
+
+ ($hostname): Changed default.
+
+ * SNMP_Session.pm: Updated my e-mail address.
+
+ * BER.pm (encode_oid): Added check for negative OID subids, by
+ Yufang HU <yhu at casc.com>
+
+ support up to 32bit subids, by Philippe Simonet
+ <sip00 at vg.swissptt.ch>.
+
+Sun Aug 25 23:49:15 1996 Simon Leinen <simon at instrumatic.ch>
+
+ * SNMP_Session.pm (request_response):
+ Perform retries with exponential backoff.
+
+ (retries, backoff): New slots of the SNMP_Session object. Default
+ values are 3 and 1.5, respectively.
+
+Wed Jul 10 08:18:16 1996 Simon Leinen <simon at instrumatic.ch>
+
+ * README: Added Heine Peters to contributors list.
+
+ * walk-test.pl: Accept hostname and community as command-line
+ arguments. These default to ``neon-tetra'' and ``public'',
+ respectively.
+
+ * SNMP_Session.pm: New header comment.
+
+Wed Jul 10 08:06:11 1996 Tobias Oetiker <oetiker at ee.ethz.ch>
+
+ Changed default timeout from 2 to 10 seconds.
+
+Wed Jul 10 08:06:11 1996 Heine Peters <peters at dkrz.de>
+
+ Allow remote host to be specified as IP address in dotted-quad
+ notation rather than by name (integrated change dated April 25).
+
+ No longer bind the UDP socket to a specific address.
+
+Tue Jul 9 09:40:28 1996 Simon Leinen <simon at instrumatic.ch>
+
+ * README: Added list of contributors.
+
+ Added pointer to mrtg 2.0.
+
+Tue Jul 9 08:41:22 1996 Simon Leinen <simon at instrumatic.ch>
+
+ * BER.pm (decode_intlike): Fixed shifting of first byte.
+
+ * BER.pm: Reformatted the header comment and added my name.
+
+ * BER.pm (decode_string): Cleaned up by removing my old code that
+ had been commented out by Andrzej Tobola when he fixed it.
+
+Tue Jul 9 08:41:22 1996 Andrzej Tobola <san at iem.pw.edu.pl>
+
+ * BER.pm (decode_string): Support long strings by using
+ decode_length.
+
+Tue Jul 9 08:41:22 1996 Tobias Oetiker <oetiker at ee.ethz.ch>
+
+ * BER.pm (decode_intlike): Handle five-byte integers.
+
+Tue Jul 9 08:41:22 1996 Dave Rand <dlr at Bungi.com>
+
+ * BER.pm (pretty_uptime): New subroutine to print sysUpTime
+ readably.
+
+Mon Jul 8 17:15:43 1996 Simon Leinen <simon at instrumatic.ch>
+
+ * README: Updated my e-mail address.
+
+ Note that get-next is now supported.
+
+ * Makefile (PROGSRCS): Added walk-test.pl.
+
+ * walk-test.pl: Initial revision
+
+ * test.pl: Actually use the hostname passed on the command line.
+
+ * BER.pm (encoded_oid_prefix_p): New subroutine that checks
+ whether one encoded OID is a prefix of the other.
+
+ * SNMP_Session.pm (request_response): New function called by both
+ get_request_response and getnext_request_response.
+
+ * SNMP_Session.pm:
+ (getnext_request, encode_getnext_request,
+ getnext_request_response): New functions.
+
+ (encode_request): New subroutine called by both encode_get_request
+ and encode_getnext_request.
+
+ * BER.pm (pretty_oid): Print subids beyond 127 CORRECTLY.
+
+ * BER.pm (pretty_oid): Print subids beyond 127.
+
+ * BER.pm (pretty_print): Call `pretty_ip_address' for objects with
+ snmp_ip_address_tag.
+
+ (pretty_ip_address): New subroutine that prints an IP address
+ object in decimal quad notation.
+
+Wed Jun 12 15:55:20 1996 Simon Leinen <simon at instrumatic.ch>
+
+ * BER.pm (encode_oid): Support OID subids up to 16383, up from 127.
+
+Fri Dec 22 11:45:02 1995 Simon Leinen <simon at instrumatic.ch>
+
+ * wwwtest: Added forward definitions of all subroutines.
+
+ Check whether the hostname is in the list of allowed hosts.
+
+ (html_error_message): added horizontal line below the title.
+
+Fri Jul 28 17:23:14 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * wwwtest (write_query_form): added a footer with links to the
+ SNMP module's page, the gateway's source and my home page.
+
+Mon Jul 17 13:51:04 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * Makefile (DOCS): removed ChangeLog.
+
+Thu Jul 6 13:15:59 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * test.pl, wwwtest, SNMP_Session.pm:
+ Use accessor methods for slots of SNMP_Session.
+
+Thu Jul 6 12:57:37 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * test.pl (%ugly_oids): use qw() to define them, as in wwwtest.
+
+ * test.pl: Simplified a tiny bit by adding the instance numbers to
+ the parameters of snmp_get.
+
+Wed Jul 5 22:11:47 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * wwwtest, party-test.pl: Added header comment.
+
+ * BER.pm, Party.pm: Added comments.
+
+ * Makefile (ALLDIST): mention $(DOCS) before $(ALLSRCS) so that
+ the README is the first file in a shar archive.
+
+Wed Jul 5 21:39:49 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * wwwtest (html_error_message, html_quote): moved to the end of
+ the file.
+
+ * wwwtest (write_query_form): default to liasg7 to simplify
+ testing at home.
+
+ * wwwtest (query_to_html_response): added heading containing the
+ name of the queried host.
+
+ (write_query_form): use here document to reduce quoting.
+
+ * wwwtest: Moved handling of parameterless case to
+ `write_query_form()'.
+
+ (write_query_form): new procedure.
+
+ (parse_query): return result rather than putting it in global hash
+ %query.
+
+ * wwwtest: Changed form to use SELECT tags so that there is a very
+ limited choice of host and community names.
+
+ * wwwtest: If called without QUERY_STRING, act as a form that
+ allows the user to specify a host and community name.
+
+ If called with a QUERY_STRING, extract host and community name and
+ use this as the target of the query.
+
+ Wrap SNMP session creation and get query in evals, and generate a
+ HTML error message if one of them fails.
+
+ * SNMP_Session.pm (host_not_found_error): generate an error
+ message trying to explain why a given host hasn't been found by
+ one of the gethostby...() routines.
+
+ (open): use `host_not_found_error' to generate better error
+ messages.
+
+ * test.pl: Added header comment.
+
+ Parse command-line arguments.
+
+Mon Jul 3 13:53:26 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * README: Explain how to encode OIDs.
+
+ Explain (roughly) how to decode the results.
+
+Wed Jun 28 22:47:48 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * wwwtest: Removed HP-agent-specific variables so that I can test
+ the code against CMU SNMPv2.
+
+Wed Jun 28 22:46:55 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * Makefile (DOCS, ALLDIST): new variables.
+
+ (shar): new target.
+
+ * SNMP_Session.pm:
+ Moved the SNMPv1-specific parts to a subclass SNMPv1_Session.
+
+ * Makefile, ChangeLog: Initial revision
+
+Wed Jun 28 21:41:51 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * party-test.pl: Initial revision
+
+ * Party.pm: use BER.
+
+ * Party.pm: Initial revision
+
+ * SNMP_Session.pm (snmp_version_1, snmp_version_2): new subroutines.
+
+ (open): fixed typo in getprotobyname('udp').
+
+Mon Jun 19 19:43:12 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * README (Example): use package BER, remove BER:: prefixes.
+
+ * wwwtest, test.pl: use BER.
+
+ Removed BER:: package prefixes.
+
+ * BER.pm: use Exporter.
+
+ * README: Tabified.
+
+Wed Jun 14 21:59:15 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * wwwtest (snmp_get):
+ produce HTML error message when get_request_response fails.
+
+ * SNMP_Session.pm (get_request_response):
+ don't warn if wait_for_response fails, return
+ 0.
+
+ * wwwtest (%ugly_oids):
+ added a couple of statistics about the first two
+ interfaces.
+
+ * BER.pm (pretty_print): format null objects as "(null)".
+
+ * wwwtest: Generate more properly structured HTML.
+
+ (%ugly_oids): Reformatted, added some variables.
+
+Mon Jun 12 15:43:36 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * README: Initial revision
+
+Fri May 19 17:07:46 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * BER.pm (decode_by_template):
+ improve error message for length mismatch.
+
+ (decode_length): support lengths up to 65536.
+
+ * BER.pm (decode_intlike): support length 3 integers.
+
+ Fix error message for unsupported integer length.
+
+ * BER.pm: Added SNMP-specific tags (counter, gauge, IP address etc.)
+
+ (pretty_print): handle counters and gauges, which are treated just
+ like integers.
+
+ * wwwtest: Initial revision
+
+Thu May 18 22:18:41 1995 Simon Leinen <simon at lia.di.epfl.ch>
+
+ * BER.pm, SNMP_Session.pm: Added -*- mode -*- comment for Emacs.
+
+ * test.pl: Layout change.
+
+ * BER.pm: Export several symbols.
+
+ (decode_by_template): assume the constructor flag when the user gives
+ a tag value in a %{ option.
+
+ * SNMP_Session.pm: Use the BER package, removed BER:: prefixes.
+
+ * BER.pm (decode_by_template): avoid recursion.
+
+ * SNMP_Session.pm (open):
+ generate a file handle so that multiple sessions can be used
+ at the same time.
+
+ * SNMP_Session.pm:
+ Use symbolic constants for PDU tags and default UDP port.
+
+ (describe): include timeout value in the output.
+
+ * test.pl: Removed `&'s before subroutine names.
+
+ * BER.pm: Improved layout.
+
+ (decode_string): Improved error message.
+
+ * BER.pm: Use symbolic constants for tags and flags.
+
+ * SNMP_Session.pm:
+ Use `Socket.pm' to get the socket constants (AF_INET, SOCK_DGRAM).
+
+ * test.pl (snmp_get): new subroutine.
+
+ * BER.pm (pretty_using_decoder): new function.
+
+ (pretty_string, pretty_int): use it.
+
+ * test.pl: Use a different mechanism to pretty print OIDs.
+
+ * SNMP_Session.pm:
+ Renamed the `socket' member to `sock' in order to avoid confusion.
+
+ * test.pl: Moved the BER and SNMP_Session packages to subfiles.
+
+ * BER.pm (decode_by_template):
+ in the %i handler, cast $expected to int in
+ order to fix false mismatches.
+
+ * BER.pm, SNMP_Session.pm, test.pl: Initial revision
+
diff --git a/META.yml b/META.yml
deleted file mode 100644
index 0ca2e98..0000000
--- a/META.yml
+++ /dev/null
@@ -1,12 +0,0 @@
---- #YAML:1.0
-name: SNMP_Session
-version: 1.13
-abstract: ~
-license: ~
-author: ~
-generated_by: ExtUtils::MakeMaker version 6.42
-distribution_type: module
-requires:
-meta-spec:
- url: http://module-build.sourceforge.net/META-spec-v1.3.html
- version: 1.3
diff --git a/Net_SNMP_util.pm b/Net_SNMP_util.pm
new file mode 100644
index 0000000..b0299fc
--- /dev/null
+++ b/Net_SNMP_util.pm
@@ -0,0 +1,2130 @@
+### - *- mode: Perl -*-
+######################################################################
+### Net_SNMP_util -- SNMP utilities using Net::SNMP
+######################################################################
+### Copyright (c) 2005-2011 Mike Mitchell.
+###
+### This program is free software; you can redistribute it under the
+### "Artistic License" included in this distribution (file "Artistic").
+######################################################################
+### Created by: Mike Mitchell <Mike.Mitchell at sas.com>
+###
+### Contributions and fixes by:
+###
+### Laszlo Herczeg <laszlo.herczeg at austinenergy.com>
+### ignore unimplemented SNMP_Session.pm options
+###
+### Daniel McDonald <dmcdonald at digicontech.com>
+### make sure snmpwalk_flg stops when last instance in table is fetched
+###
+### Alexander Kozlov <avk at post.eao.ru>
+### Leave snmpwalk_flg early if no OIDs are returned
+###
+### <jaccobs at online.nl>
+### parse NOTIFICATION-TYPE in MIB
+###
+### Dan Thorson <Dan.Thorson at seagate.com>
+### Handle quotes in MIB comments better
+###
+### Daniel J McDonald <dan.mcdonald at austinenergy.com>
+### fix getbulk_request -> get_bulk_request typo
+###
+### Tobias Oetiker <tobi at oetiker.ch>
+### fix '-privpassword' error against snmpv2 hosts
+###
+######################################################################
+
+package Net_SNMP_util;
+
+=head1 NAME
+
+Net_SNMP_util - SNMP utilities based on Net::SNMP
+
+=head1 SYNOPSIS
+
+The Net_SNMP_util module implements SNMP utilities using the Net::SNMP module.
+It implements snmpget, snmpgetnext, snmpwalk, snmpset, snmptrap, and
+snmpgetbulk. The Net_SNMP_util module assumes that the user has a basic
+understanding of the Simple Network Management Protocol and related network
+management concepts.
+
+=head1 DESCRIPTION
+
+The Net_SNMP_util module simplifies SNMP queries even more than Net::SNMP
+alone. Easy-to-use "get", "getnext", "walk", "set", "trap", and "getbulk"
+routines are provided, hiding all the details of a SNMP query.
+
+=cut
+
+# ==========================================================================
+
+use strict;
+
+## Validate the version of Perl
+
+BEGIN
+{
+ die('Perl version 5.6.0 or greater is required') if ($] < 5.006);
+}
+
+## Handle importing/exporting of symbols
+
+use vars qw( @ISA @EXPORT $VERSION $ErrorMessage);
+use Exporter;
+
+our @ISA = qw( Exporter );
+
+our @EXPORT = qw(
+ snmpget snmpgetnext snmpwalk snmpset snmptrap snmpgetbulk snmpmaptable
+ snmpmaptable4 snmpwalkhash snmpmapOID snmpMIB_to_OID snmpLoad_OID_Cache
+ snmpQueue_MIB_File ErrorMessage
+);
+
+## Version of the Net_SNMP_util module
+
+our $VERSION = v1.0.20;
+
+use Carp;
+
+use Net::SNMP v5.0;
+
+# The OID numbers from RFC1213 (MIB-II) and RFC1315 (Frame Relay)
+# are pre-loaded below.
+%Net_SNMP_util::OIDS =
+ (
+ 'iso' => '1',
+ 'org' => '1.3',
+ 'dod' => '1.3.6',
+ 'internet' => '1.3.6.1',
+ 'directory' => '1.3.6.1.1',
+ 'mgmt' => '1.3.6.1.2',
+ 'mib-2' => '1.3.6.1.2.1',
+ 'system' => '1.3.6.1.2.1.1',
+ 'sysDescr' => '1.3.6.1.2.1.1.1.0',
+ 'sysObjectID' => '1.3.6.1.2.1.1.2.0',
+ 'sysUpTime' => '1.3.6.1.2.1.1.3.0',
+ 'sysUptime' => '1.3.6.1.2.1.1.3.0',
+ 'sysContact' => '1.3.6.1.2.1.1.4.0',
+ 'sysName' => '1.3.6.1.2.1.1.5.0',
+ 'sysLocation' => '1.3.6.1.2.1.1.6.0',
+ 'sysServices' => '1.3.6.1.2.1.1.7.0',
+ 'interfaces' => '1.3.6.1.2.1.2',
+ 'ifNumber' => '1.3.6.1.2.1.2.1.0',
+ 'ifTable' => '1.3.6.1.2.1.2.2',
+ 'ifEntry' => '1.3.6.1.2.1.2.2.1',
+ 'ifIndex' => '1.3.6.1.2.1.2.2.1.1',
+ 'ifInOctets' => '1.3.6.1.2.1.2.2.1.10',
+ 'ifInUcastPkts' => '1.3.6.1.2.1.2.2.1.11',
+ 'ifInNUcastPkts' => '1.3.6.1.2.1.2.2.1.12',
+ 'ifInDiscards' => '1.3.6.1.2.1.2.2.1.13',
+ 'ifInErrors' => '1.3.6.1.2.1.2.2.1.14',
+ 'ifInUnknownProtos' => '1.3.6.1.2.1.2.2.1.15',
+ 'ifOutOctets' => '1.3.6.1.2.1.2.2.1.16',
+ 'ifOutUcastPkts' => '1.3.6.1.2.1.2.2.1.17',
+ 'ifOutNUcastPkts' => '1.3.6.1.2.1.2.2.1.18',
+ 'ifOutDiscards' => '1.3.6.1.2.1.2.2.1.19',
+ 'ifDescr' => '1.3.6.1.2.1.2.2.1.2',
+ 'ifOutErrors' => '1.3.6.1.2.1.2.2.1.20',
+ 'ifOutQLen' => '1.3.6.1.2.1.2.2.1.21',
+ 'ifSpecific' => '1.3.6.1.2.1.2.2.1.22',
+ 'ifType' => '1.3.6.1.2.1.2.2.1.3',
+ 'ifMtu' => '1.3.6.1.2.1.2.2.1.4',
+ 'ifSpeed' => '1.3.6.1.2.1.2.2.1.5',
+ 'ifPhysAddress' => '1.3.6.1.2.1.2.2.1.6',
+ 'ifAdminHack' => '1.3.6.1.2.1.2.2.1.7',
+ 'ifAdminStatus' => '1.3.6.1.2.1.2.2.1.7',
+ 'ifOperHack' => '1.3.6.1.2.1.2.2.1.8',
+ 'ifOperStatus' => '1.3.6.1.2.1.2.2.1.8',
+ 'ifLastChange' => '1.3.6.1.2.1.2.2.1.9',
+ 'at' => '1.3.6.1.2.1.3',
+ 'atTable' => '1.3.6.1.2.1.3.1',
+ 'atEntry' => '1.3.6.1.2.1.3.1.1',
+ 'atIfIndex' => '1.3.6.1.2.1.3.1.1.1',
+ 'atPhysAddress' => '1.3.6.1.2.1.3.1.1.2',
+ 'atNetAddress' => '1.3.6.1.2.1.3.1.1.3',
+ 'ip' => '1.3.6.1.2.1.4',
+ 'ipForwarding' => '1.3.6.1.2.1.4.1',
+ 'ipOutRequests' => '1.3.6.1.2.1.4.10',
+ 'ipOutDiscards' => '1.3.6.1.2.1.4.11',
+ 'ipOutNoRoutes' => '1.3.6.1.2.1.4.12',
+ 'ipReasmTimeout' => '1.3.6.1.2.1.4.13',
+ 'ipReasmReqds' => '1.3.6.1.2.1.4.14',
+ 'ipReasmOKs' => '1.3.6.1.2.1.4.15',
+ 'ipReasmFails' => '1.3.6.1.2.1.4.16',
+ 'ipFragOKs' => '1.3.6.1.2.1.4.17',
+ 'ipFragFails' => '1.3.6.1.2.1.4.18',
+ 'ipFragCreates' => '1.3.6.1.2.1.4.19',
+ 'ipDefaultTTL' => '1.3.6.1.2.1.4.2',
+ 'ipAddrTable' => '1.3.6.1.2.1.4.20',
+ 'ipAddrEntry' => '1.3.6.1.2.1.4.20.1',
+ 'ipAdEntAddr' => '1.3.6.1.2.1.4.20.1.1',
+ 'ipAdEntIfIndex' => '1.3.6.1.2.1.4.20.1.2',
+ 'ipAdEntNetMask' => '1.3.6.1.2.1.4.20.1.3',
+ 'ipAdEntBcastAddr' => '1.3.6.1.2.1.4.20.1.4',
+ 'ipAdEntReasmMaxSize' => '1.3.6.1.2.1.4.20.1.5',
+ 'ipRouteTable' => '1.3.6.1.2.1.4.21',
+ 'ipRouteEntry' => '1.3.6.1.2.1.4.21.1',
+ 'ipRouteDest' => '1.3.6.1.2.1.4.21.1.1',
+ 'ipRouteAge' => '1.3.6.1.2.1.4.21.1.10',
+ 'ipRouteMask' => '1.3.6.1.2.1.4.21.1.11',
+ 'ipRouteMetric5' => '1.3.6.1.2.1.4.21.1.12',
+ 'ipRouteInfo' => '1.3.6.1.2.1.4.21.1.13',
+ 'ipRouteIfIndex' => '1.3.6.1.2.1.4.21.1.2',
+ 'ipRouteMetric1' => '1.3.6.1.2.1.4.21.1.3',
+ 'ipRouteMetric2' => '1.3.6.1.2.1.4.21.1.4',
+ 'ipRouteMetric3' => '1.3.6.1.2.1.4.21.1.5',
+ 'ipRouteMetric4' => '1.3.6.1.2.1.4.21.1.6',
+ 'ipRouteNextHop' => '1.3.6.1.2.1.4.21.1.7',
+ 'ipRouteType' => '1.3.6.1.2.1.4.21.1.8',
+ 'ipRouteProto' => '1.3.6.1.2.1.4.21.1.9',
+ 'ipNetToMediaTable' => '1.3.6.1.2.1.4.22',
+ 'ipNetToMediaEntry' => '1.3.6.1.2.1.4.22.1',
+ 'ipNetToMediaIfIndex' => '1.3.6.1.2.1.4.22.1.1',
+ 'ipNetToMediaPhysAddress' => '1.3.6.1.2.1.4.22.1.2',
+ 'ipNetToMediaNetAddress' => '1.3.6.1.2.1.4.22.1.3',
+ 'ipNetToMediaType' => '1.3.6.1.2.1.4.22.1.4',
+ 'ipRoutingDiscards' => '1.3.6.1.2.1.4.23',
+ 'ipInReceives' => '1.3.6.1.2.1.4.3',
+ 'ipInHdrErrors' => '1.3.6.1.2.1.4.4',
+ 'ipInAddrErrors' => '1.3.6.1.2.1.4.5',
+ 'ipForwDatagrams' => '1.3.6.1.2.1.4.6',
+ 'ipInUnknownProtos' => '1.3.6.1.2.1.4.7',
+ 'ipInDiscards' => '1.3.6.1.2.1.4.8',
+ 'ipInDelivers' => '1.3.6.1.2.1.4.9',
+ 'icmp' => '1.3.6.1.2.1.5',
+ 'icmpInMsgs' => '1.3.6.1.2.1.5.1',
+ 'icmpInTimestamps' => '1.3.6.1.2.1.5.10',
+ 'icmpInTimestampReps' => '1.3.6.1.2.1.5.11',
+ 'icmpInAddrMasks' => '1.3.6.1.2.1.5.12',
+ 'icmpInAddrMaskReps' => '1.3.6.1.2.1.5.13',
+ 'icmpOutMsgs' => '1.3.6.1.2.1.5.14',
+ 'icmpOutErrors' => '1.3.6.1.2.1.5.15',
+ 'icmpOutDestUnreachs' => '1.3.6.1.2.1.5.16',
+ 'icmpOutTimeExcds' => '1.3.6.1.2.1.5.17',
+ 'icmpOutParmProbs' => '1.3.6.1.2.1.5.18',
+ 'icmpOutSrcQuenchs' => '1.3.6.1.2.1.5.19',
+ 'icmpInErrors' => '1.3.6.1.2.1.5.2',
+ 'icmpOutRedirects' => '1.3.6.1.2.1.5.20',
+ 'icmpOutEchos' => '1.3.6.1.2.1.5.21',
+ 'icmpOutEchoReps' => '1.3.6.1.2.1.5.22',
+ 'icmpOutTimestamps' => '1.3.6.1.2.1.5.23',
+ 'icmpOutTimestampReps' => '1.3.6.1.2.1.5.24',
+ 'icmpOutAddrMasks' => '1.3.6.1.2.1.5.25',
+ 'icmpOutAddrMaskReps' => '1.3.6.1.2.1.5.26',
+ 'icmpInDestUnreachs' => '1.3.6.1.2.1.5.3',
+ 'icmpInTimeExcds' => '1.3.6.1.2.1.5.4',
+ 'icmpInParmProbs' => '1.3.6.1.2.1.5.5',
+ 'icmpInSrcQuenchs' => '1.3.6.1.2.1.5.6',
+ 'icmpInRedirects' => '1.3.6.1.2.1.5.7',
+ 'icmpInEchos' => '1.3.6.1.2.1.5.8',
+ 'icmpInEchoReps' => '1.3.6.1.2.1.5.9',
+ 'tcp' => '1.3.6.1.2.1.6',
+ 'tcpRtoAlgorithm' => '1.3.6.1.2.1.6.1',
+ 'tcpInSegs' => '1.3.6.1.2.1.6.10',
+ 'tcpOutSegs' => '1.3.6.1.2.1.6.11',
+ 'tcpRetransSegs' => '1.3.6.1.2.1.6.12',
+ 'tcpConnTable' => '1.3.6.1.2.1.6.13',
+ 'tcpConnEntry' => '1.3.6.1.2.1.6.13.1',
+ 'tcpConnState' => '1.3.6.1.2.1.6.13.1.1',
+ 'tcpConnLocalAddress' => '1.3.6.1.2.1.6.13.1.2',
+ 'tcpConnLocalPort' => '1.3.6.1.2.1.6.13.1.3',
+ 'tcpConnRemAddress' => '1.3.6.1.2.1.6.13.1.4',
+ 'tcpConnRemPort' => '1.3.6.1.2.1.6.13.1.5',
+ 'tcpInErrs' => '1.3.6.1.2.1.6.14',
+ 'tcpOutRsts' => '1.3.6.1.2.1.6.15',
+ 'tcpRtoMin' => '1.3.6.1.2.1.6.2',
+ 'tcpRtoMax' => '1.3.6.1.2.1.6.3',
+ 'tcpMaxConn' => '1.3.6.1.2.1.6.4',
+ 'tcpActiveOpens' => '1.3.6.1.2.1.6.5',
+ 'tcpPassiveOpens' => '1.3.6.1.2.1.6.6',
+ 'tcpAttemptFails' => '1.3.6.1.2.1.6.7',
+ 'tcpEstabResets' => '1.3.6.1.2.1.6.8',
+ 'tcpCurrEstab' => '1.3.6.1.2.1.6.9',
+ 'udp' => '1.3.6.1.2.1.7',
+ 'udpInDatagrams' => '1.3.6.1.2.1.7.1',
+ 'udpNoPorts' => '1.3.6.1.2.1.7.2',
+ 'udpInErrors' => '1.3.6.1.2.1.7.3',
+ 'udpOutDatagrams' => '1.3.6.1.2.1.7.4',
+ 'udpTable' => '1.3.6.1.2.1.7.5',
+ 'udpEntry' => '1.3.6.1.2.1.7.5.1',
+ 'udpLocalAddress' => '1.3.6.1.2.1.7.5.1.1',
+ 'udpLocalPort' => '1.3.6.1.2.1.7.5.1.2',
+ 'egp' => '1.3.6.1.2.1.8',
+ 'egpInMsgs' => '1.3.6.1.2.1.8.1',
+ 'egpInErrors' => '1.3.6.1.2.1.8.2',
+ 'egpOutMsgs' => '1.3.6.1.2.1.8.3',
+ 'egpOutErrors' => '1.3.6.1.2.1.8.4',
+ 'egpNeighTable' => '1.3.6.1.2.1.8.5',
+ 'egpNeighEntry' => '1.3.6.1.2.1.8.5.1',
+ 'egpNeighState' => '1.3.6.1.2.1.8.5.1.1',
+ 'egpNeighStateUps' => '1.3.6.1.2.1.8.5.1.10',
+ 'egpNeighStateDowns' => '1.3.6.1.2.1.8.5.1.11',
+ 'egpNeighIntervalHello' => '1.3.6.1.2.1.8.5.1.12',
+ 'egpNeighIntervalPoll' => '1.3.6.1.2.1.8.5.1.13',
+ 'egpNeighMode' => '1.3.6.1.2.1.8.5.1.14',
+ 'egpNeighEventTrigger' => '1.3.6.1.2.1.8.5.1.15',
+ 'egpNeighAddr' => '1.3.6.1.2.1.8.5.1.2',
+ 'egpNeighAs' => '1.3.6.1.2.1.8.5.1.3',
+ 'egpNeighInMsgs' => '1.3.6.1.2.1.8.5.1.4',
+ 'egpNeighInErrs' => '1.3.6.1.2.1.8.5.1.5',
+ 'egpNeighOutMsgs' => '1.3.6.1.2.1.8.5.1.6',
+ 'egpNeighOutErrs' => '1.3.6.1.2.1.8.5.1.7',
+ 'egpNeighInErrMsgs' => '1.3.6.1.2.1.8.5.1.8',
+ 'egpNeighOutErrMsgs' => '1.3.6.1.2.1.8.5.1.9',
+ 'egpAs' => '1.3.6.1.2.1.8.6',
+ 'transmission' => '1.3.6.1.2.1.10',
+ 'frame-relay' => '1.3.6.1.2.1.10.32',
+ 'frDlcmiTable' => '1.3.6.1.2.1.10.32.1',
+ 'frDlcmiEntry' => '1.3.6.1.2.1.10.32.1.1',
+ 'frDlcmiIfIndex' => '1.3.6.1.2.1.10.32.1.1.1',
+ 'frDlcmiState' => '1.3.6.1.2.1.10.32.1.1.2',
+ 'frDlcmiAddress' => '1.3.6.1.2.1.10.32.1.1.3',
+ 'frDlcmiAddressLen' => '1.3.6.1.2.1.10.32.1.1.4',
+ 'frDlcmiPollingInterval' => '1.3.6.1.2.1.10.32.1.1.5',
+ 'frDlcmiFullEnquiryInterval' => '1.3.6.1.2.1.10.32.1.1.6',
+ 'frDlcmiErrorThreshold' => '1.3.6.1.2.1.10.32.1.1.7',
+ 'frDlcmiMonitoredEvents' => '1.3.6.1.2.1.10.32.1.1.8',
+ 'frDlcmiMaxSupportedVCs' => '1.3.6.1.2.1.10.32.1.1.9',
+ 'frDlcmiMulticast' => '1.3.6.1.2.1.10.32.1.1.10',
+ 'frCircuitTable' => '1.3.6.1.2.1.10.32.2',
+ 'frCircuitEntry' => '1.3.6.1.2.1.10.32.2.1',
+ 'frCircuitIfIndex' => '1.3.6.1.2.1.10.32.2.1.1',
+ 'frCircuitDlci' => '1.3.6.1.2.1.10.32.2.1.2',
+ 'frCircuitState' => '1.3.6.1.2.1.10.32.2.1.3',
+ 'frCircuitReceivedFECNs' => '1.3.6.1.2.1.10.32.2.1.4',
+ 'frCircuitReceivedBECNs' => '1.3.6.1.2.1.10.32.2.1.5',
+ 'frCircuitSentFrames' => '1.3.6.1.2.1.10.32.2.1.6',
+ 'frCircuitSentOctets' => '1.3.6.1.2.1.10.32.2.1.7',
+ 'frOutOctets' => '1.3.6.1.2.1.10.32.2.1.7',
+ 'frCircuitReceivedFrames' => '1.3.6.1.2.1.10.32.2.1.8',
+ 'frCircuitReceivedOctets' => '1.3.6.1.2.1.10.32.2.1.9',
+ 'frInOctets' => '1.3.6.1.2.1.10.32.2.1.9',
+ 'frCircuitCreationTime' => '1.3.6.1.2.1.10.32.2.1.10',
+ 'frCircuitLastTimeChange' => '1.3.6.1.2.1.10.32.2.1.11',
+ 'frCircuitCommittedBurst' => '1.3.6.1.2.1.10.32.2.1.12',
+ 'frCircuitExcessBurst' => '1.3.6.1.2.1.10.32.2.1.13',
+ 'frCircuitThroughput' => '1.3.6.1.2.1.10.32.2.1.14',
+ 'frErrTable' => '1.3.6.1.2.1.10.32.3',
+ 'frErrEntry' => '1.3.6.1.2.1.10.32.3.1',
+ 'frErrIfIndex' => '1.3.6.1.2.1.10.32.3.1.1',
+ 'frErrType' => '1.3.6.1.2.1.10.32.3.1.2',
+ 'frErrData' => '1.3.6.1.2.1.10.32.3.1.3',
+ 'frErrTime' => '1.3.6.1.2.1.10.32.3.1.4',
+ 'frame-relay-globals' => '1.3.6.1.2.1.10.32.4',
+ 'frTrapState' => '1.3.6.1.2.1.10.32.4.1',
+ 'snmp' => '1.3.6.1.2.1.11',
+ 'snmpInPkts' => '1.3.6.1.2.1.11.1',
+ 'snmpInBadValues' => '1.3.6.1.2.1.11.10',
+ 'snmpInReadOnlys' => '1.3.6.1.2.1.11.11',
+ 'snmpInGenErrs' => '1.3.6.1.2.1.11.12',
+ 'snmpInTotalReqVars' => '1.3.6.1.2.1.11.13',
+ 'snmpInTotalSetVars' => '1.3.6.1.2.1.11.14',
+ 'snmpInGetRequests' => '1.3.6.1.2.1.11.15',
+ 'snmpInGetNexts' => '1.3.6.1.2.1.11.16',
+ 'snmpInSetRequests' => '1.3.6.1.2.1.11.17',
+ 'snmpInGetResponses' => '1.3.6.1.2.1.11.18',
+ 'snmpInTraps' => '1.3.6.1.2.1.11.19',
+ 'snmpOutPkts' => '1.3.6.1.2.1.11.2',
+ 'snmpOutTooBigs' => '1.3.6.1.2.1.11.20',
+ 'snmpOutNoSuchNames' => '1.3.6.1.2.1.11.21',
+ 'snmpOutBadValues' => '1.3.6.1.2.1.11.22',
+ 'snmpOutGenErrs' => '1.3.6.1.2.1.11.24',
+ 'snmpOutGetRequests' => '1.3.6.1.2.1.11.25',
+ 'snmpOutGetNexts' => '1.3.6.1.2.1.11.26',
+ 'snmpOutSetRequests' => '1.3.6.1.2.1.11.27',
+ 'snmpOutGetResponses' => '1.3.6.1.2.1.11.28',
+ 'snmpOutTraps' => '1.3.6.1.2.1.11.29',
+ 'snmpInBadVersions' => '1.3.6.1.2.1.11.3',
+ 'snmpEnableAuthenTraps' => '1.3.6.1.2.1.11.30',
+ 'snmpInBadCommunityNames' => '1.3.6.1.2.1.11.4',
+ 'snmpInBadCommunityUses' => '1.3.6.1.2.1.11.5',
+ 'snmpInASNParseErrs' => '1.3.6.1.2.1.11.6',
+ 'snmpInTooBigs' => '1.3.6.1.2.1.11.8',
+ 'snmpInNoSuchNames' => '1.3.6.1.2.1.11.9',
+ 'ifName' => '1.3.6.1.2.1.31.1.1.1.1',
+ 'ifInMulticastPkts' => '1.3.6.1.2.1.31.1.1.1.2',
+ 'ifInBroadcastPkts' => '1.3.6.1.2.1.31.1.1.1.3',
+ 'ifOutMulticastPkts' => '1.3.6.1.2.1.31.1.1.1.4',
+ 'ifOutBroadcastPkts' => '1.3.6.1.2.1.31.1.1.1.5',
+ 'ifHCInOctets' => '1.3.6.1.2.1.31.1.1.1.6',
+ 'ifHCInUcastPkts' => '1.3.6.1.2.1.31.1.1.1.7',
+ 'ifHCInMulticastPkts' => '1.3.6.1.2.1.31.1.1.1.8',
+ 'ifHCInBroadcastPkts' => '1.3.6.1.2.1.31.1.1.1.9',
+ 'ifHCOutOctets' => '1.3.6.1.2.1.31.1.1.1.10',
+ 'ifHCOutUcastPkts' => '1.3.6.1.2.1.31.1.1.1.11',
+ 'ifHCOutMulticastPkts' => '1.3.6.1.2.1.31.1.1.1.12',
+ 'ifHCOutBroadcastPkts' => '1.3.6.1.2.1.31.1.1.1.13',
+ 'ifLinkUpDownTrapEnable' => '1.3.6.1.2.1.31.1.1.1.14',
+ 'ifHighSpeed' => '1.3.6.1.2.1.31.1.1.1.15',
+ 'ifPromiscuousMode' => '1.3.6.1.2.1.31.1.1.1.16',
+ 'ifConnectorPresent' => '1.3.6.1.2.1.31.1.1.1.17',
+ 'ifAlias' => '1.3.6.1.2.1.31.1.1.1.18',
+ 'ifCounterDiscontinuityTime' => '1.3.6.1.2.1.31.1.1.1.19',
+ 'experimental' => '1.3.6.1.3',
+ 'private' => '1.3.6.1.4',
+ 'enterprises' => '1.3.6.1.4.1',
+ );
+
+# GIL
+my %revOIDS = (); # Reversed %Net_SNMP_util::OIDS hash
+my $RevNeeded = 1;
+
+undef $Net_SNMP_util::Host;
+undef $Net_SNMP_util::Session;
+undef $Net_SNMP_util::Version;
+undef $Net_SNMP_util::LHost;
+undef $Net_SNMP_util::IPv4only;
+undef $Net_SNMP_util::ContextEngineID;
+undef $Net_SNMP_util::ContextName;
+$Net_SNMP_util::Debug = 0;
+$Net_SNMP_util::SuppressWarnings = 0;
+$Net_SNMP_util::CacheFile = "OID_cache.txt";
+$Net_SNMP_util::CacheLoaded = 0;
+$Net_SNMP_util::ReturnArrayRefs = 0;
+$Net_SNMP_util::ReturnHashRefs = 0;
+$Net_SNMP_util::MaxRepetitions = 12;
+
+### Prototypes
+sub snmpget ($@);
+sub snmpgetnext ($@);
+sub snmpopen ($$$);
+sub snmpwalk ($@);
+sub snmpwalk_flg ($$@);
+sub snmpset ($@);
+sub snmptrap ($$$$$@);
+sub snmpgetbulk ($$$@);
+sub snmpwalkhash ($$@);
+sub toOID (@);
+sub snmpmapOID (@);
+sub snmpMIB_to_OID ($);
+sub Check_OID ($);
+sub snmpLoad_OID_Cache ($);
+sub snmpQueue_MIB_File (@);
+sub ASNtype ($);
+sub error_msg ($);
+sub MIB_fill_OID ($);
+
+sub version () { $VERSION; }
+
+=head1 Option Notes
+
+=over
+
+=item host Parameter
+
+SNMP parameters can be specified as part of the hostname/ip address passed
+as the first argument. The syntax is
+
+ community at host:port:timeout:retries:backoff:version
+
+If the community is left off, it defaults to "public".
+If the port is left off, it defaults to 161 for everything but snmptrap().
+The snmptrap() routine uses a default port of 162.
+Timeout and retries defaults to whatever Net::SNMP uses, currently 5.0 seconds
+and 1 retry (2 tries total).
+The backoff parameter is currently unimplemented.
+The version parameter defaults to SNMP version 1. Some SNMP values such as
+64-bit counters have to be queried using SNMP version 2. Specifying "2" or
+"2c" as the version parameter will accomplish this. The snmpgetbulk routine
+is only supported in SNMP version 2 and higher. Additional security features
+are available under SNMP version 3.
+
+Some machines have additional security features that only allow SNMP
+queries to come from certain IP addresses. If the host doing the query
+has multiple interfaces, it may be necessary to specify the interface
+the query should come from. The port parameter is further broken down into
+
+ remote_port!local_address!local_port
+
+Here are some examples:
+
+ somehost
+ somehost:161
+ somehost:161!192.168.2.4!4000 use 192.168.2.4 and port 4000 as source
+ somehost:!192.168.2.4 use 192.168.2.4 as source
+ somehost:!!4000 use port 4000 as source
+
+Most people will only need to use the first form ("somehost").
+
+=item OBJECT IDENTIFIERs
+
+To further simplify SNMP queries, the query routines use a small table that
+maps the textual representation of OBJECT IDENTIFIERs to their dotted notation.
+The OBJECT IDENTIFIERs from RFC1213 (MIB-II) and RFC1315 (Frame Relay) are
+preloaded. This allows OBJECT IDENTIFIERs like "ifInOctets.4" to be used
+instead of the more cumbersome "1.3.6.1.2.1.2.2.1.10.4".
+
+Several functions are provided to manage the mapping table. Mapping entries
+can be added directly, SNMP MIB files can be read, and a cache file with the
+text-to-OBJECT-IDENTIFIER mappings are maintained. By default, the file
+"OID_cache.txt" is loaded, but it can by changed by setting the variable
+$Net_SNMP_util::CacheFile to the desired file name. The functions to
+manipulate the mappings are:
+
+ snmpmapOID Add a textual OID mapping directly
+ snmpMIB_to_OID Read a SNMP MIB file
+ snmpLoad_OID_Cache Load an OID-mapping cache file
+ snmpQueue_MIB_File Queue a SNMP MIB file for loading on demand
+
+=item Net::SNMP extensions
+
+This module is built on top of Net::SNMP. Net::SNMP has a different method
+of specifying SNMP parameters. To support this different method, this module
+will accept an optional hash reference containing the SNMP parameters. The
+hash may contain the following:
+
+ [-port => $port,]
+ [-localaddr => $localaddr,]
+ [-localport => $localport,]
+ [-version => $version,]
+ [-domain => $domain,]
+ [-timeout => $seconds,]
+ [-retries => $count,]
+ [-maxmsgsize => $octets,]
+ [-debug => $bitmask,]
+ [-community => $community,] # v1/v2c
+ [-username => $username,] # v3
+ [-authkey => $authkey,] # v3
+ [-authpassword => $authpasswd,] # v3
+ [-authprotocol => $authproto,] # v3
+ [-privkey => $privkey,] # v3
+ [-privpassword => $privpasswd,] # v3
+ [-privprotocol => $privproto,] # v3
+ [-contextengineid => $engine_id,] # v3
+ [-contextname => $name,] # v3
+
+Please see the documentation for Net::SNMP for a description of these
+parameters.
+
+=item 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
+
+=back
+
+=cut
+
+# [public methods] ---------------------------------------------------
+
+=head1 Functions
+
+=head2 snmpget() - send a SNMP get-request to the remote agent
+
+ @result = snmpget(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ [\%param_hash],
+ @oids
+ );
+
+This function performs a SNMP get-request query to gather data from the remote
+agent on the host specified. The message is built using the list of OBJECT
+IDENTIFIERs passed as an array. Each OBJECT IDENTIFIER is placed into a single
+SNMP GetRequest-PDU in the same order that it held in the original list.
+
+The requested values are returned in an array in the same order as they were
+requested. In scalar context the first requested value is returned.
+
+=cut
+
+#
+# snmpget.
+#
+sub snmpget ($@) {
+ my($host, @vars) = @_;
+ my($session, @enoid, %args, $ret, $oid, @retvals);
+
+ @retvals = ();
+ $session = &snmpopen($host, 0, \@vars);
+ if (!defined($session)) {
+ carp "SNMPGET Problem for $host"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ return wantarray ? @retvals : undef;
+ }
+
+ @enoid = &toOID(@vars);
+ if ($#enoid < 0) {
+ return wantarray ? @retvals : undef;
+ }
+
+ $args{'-varbindlist'} = \@enoid;
+ if ($Net_SNMP_util::Version > 2) {
+ $args{'-contextengineid'} = $Net_SNMP_util::ContextEngineID
+ if (defined($Net_SNMP_util::ContextEngineID));
+ $args{'-contextname'} = $Net_SNMP_util::ContextName
+ if (defined($Net_SNMP_util::ContextName));
+ }
+
+ $ret = $session->get_request(%args);
+
+ if ($ret) {
+ foreach $oid (@enoid) {
+ push @retvals, $ret->{$oid} if (exists($ret->{$oid}));
+ }
+ return wantarray ? @retvals : $retvals[0];
+ }
+ $ret = join(' ', @vars);
+ error_msg("SNMPGET Problem for $ret on ${host}: " . $session->error());
+ return wantarray ? @retvals : undef;
+}
+
+=head2 snmpgetnext() - send a SNMP get-next-request to the remote agent
+
+ @result = snmpgetnext(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ [\%param_hash],
+ @oids
+ );
+
+This function performs a SNMP get-next-request query to gather data from the
+remote agent on the host specified. The message is built using the list of
+OBJECT IDENTIFIERs passed as an array. Each OBJECT IDENTIFIER is placed into a
+single SNMP GetNextRequest-PDU in the same order that it held in the original
+list.
+
+The requested values are returned in an array in the same order as they were
+requested. The OBJECT IDENTIFIER number is added as a prefix to each value
+using a colon as a separator, like '1.3.6.1.2.1.2.2.1.2.1:ethernet'.
+In scalar context the first requested value is returned.
+
+=cut
+
+#
+# snmpgetnext.
+#
+sub snmpgetnext ($@) {
+ my($host, @vars) = @_;
+ my($session, @enoid, %args, $ret, $oid, @retvals);
+
+ @retvals = ();
+ $session = &snmpopen($host, 0, \@vars);
+ if (!defined($session)) {
+ carp "SNMPGETNEXT Problem for $host"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ return wantarray ? @retvals : undef;
+ }
+
+ @enoid = &toOID(@vars);
+ if ($#enoid < 0) {
+ return wantarray ? @retvals : undef;
+ }
+
+ $args{'-varbindlist'} = \@enoid;
+ if ($Net_SNMP_util::Version > 2) {
+ $args{'-contextengineid'} = $Net_SNMP_util::ContextEngineID
+ if (defined($Net_SNMP_util::ContextEngineID));
+ $args{'-contextname'} = $Net_SNMP_util::ContextName
+ if (defined($Net_SNMP_util::ContextName));
+ }
+
+ $ret = $session->get_next_request(%args);
+
+ if ($ret) {
+ foreach $oid (@enoid) {
+ push @retvals, $oid . ':' . $ret->{$oid} if (exists($ret->{$oid}));
+ }
+ return wantarray ? @retvals : $retvals[0];
+ }
+ $ret = join(' ', @vars);
+ error_msg("SNMPGETNEXT Problem for $ret on ${host}: " . $session->error());
+ return wantarray ? @retvals : undef;
+}
+
+=head2 snmpgetbulk() - send a SNMP get-bulk-request to the remote agent
+
+ @result = snmpgetbulk(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ $nonrepeaters,
+ $maxrepetitions,
+ [\%param_hash],
+ @oids
+ );
+
+This function performs a SNMP get-bulk-request query to gather data from the
+remote agent on the host specified.
+
+=over
+
+=item *
+
+The B<$nonrepeaters> value specifies the number of variables in the @oids list
+for which a single successor is to be returned. If it is null or undefined,
+a value of 0 is used.
+
+=item *
+
+The B<$maxrepetitions> value specifies the number of successors to be returned
+for the remaining variables in the @oids list. If it is null or undefined,
+the default value of 12 is used.
+
+=item *
+
+The message is built using the list of
+OBJECT IDENTIFIERs passed as an array. Each OBJECT IDENTIFIER is placed into a
+single SNMP GetNextRequest-PDU in the same order that it held in the original
+list.
+
+=back
+
+The requested values are returned in an array in the same order as they were
+requested.
+
+B<NOTE:> This function can only be used when the SNMP version is set to
+SNMPv2c or SNMPv3.
+
+=cut
+
+#
+# snmpgetbulk.
+#
+sub snmpgetbulk ($$$@) {
+ my($host, $nr, $mr, @vars) = @_;
+ my($session, %args, @enoid, $ret);
+ my($oid, @retvals);
+
+ @retvals = ();
+ $session = &snmpopen($host, 0, \@vars);
+ if (!defined($session)) {
+ carp "SNMPGETBULK Problem for $host"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ return @retvals;
+ }
+
+ if ($Net_SNMP_util::Version < 2) {
+ carp "SNMPGETBULK Problem for $host : must use SNMP version > 1"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ return @retvals;
+ }
+
+ $args{'-nonrepeaters'} = $nr if ($nr > 0);
+ $mr = $Net_SNMP_util::MaxRepetitions if ($mr <= 0);
+ $args{'-maxrepetitions'} = $mr;
+
+ if ($Net_SNMP_util::Version > 2) {
+ $args{'-contextengineid'} = $Net_SNMP_util::ContextEngineID
+ if (defined($Net_SNMP_util::ContextEngineID));
+ $args{'-contextname'} = $Net_SNMP_util::ContextName
+ if (defined($Net_SNMP_util::ContextName));
+ }
+
+ @enoid = &toOID(@vars);
+ return @retvals if ($#enoid < 0);
+
+ $args{'-varbindlist'} = \@enoid;
+ $ret = $session->get_bulk_request(%args);
+
+ if ($ret) {
+ @enoid = &Net::SNMP::oid_lex_sort(keys %$ret);
+ foreach $oid (@enoid) {
+ push @retvals, $oid . ":" . $ret->{$oid};
+ }
+ return @retvals;
+ } else {
+ $ret = join(' ', @vars);
+ error_msg("SNMPGETBULK Problem for $ret on ${host}: " . $session->error());
+ return @retvals;
+ }
+}
+
+
+=head2 snmpwalk() - walk OBJECT IDENTIFIER tree(s) on the remote agent
+
+ @result = snmpwalk(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ [\%param_hash],
+ @oids
+ );
+
+This function performs a sequence of SNMP get-next-request or get-bulk-request
+(if the SNMP version is 2 or higher) queries to gather data from the remote
+agent on the host specified. The initial message is built using the list of
+OBJECT IDENTIFIERs passed as an array. Each OBJECT IDENTIFIER is placed into a
+single SNMP GetNextRequest-PDU in the same order that it held in the original
+list. Queries continue until all the returned OBJECT IDENTIFIERs are no longer
+a child of the base OBJECT IDENTIFIERs.
+
+The requested values are returned in an array in the same order as they were
+requested. The OBJECT IDENTIFIER number is added as a prefix to each value
+using a colon as a separator, like '1.3.6.1.2.1.2.2.1.2.1:ethernet'. If only
+one OBJECT IDENTIFIER is requested, just the "instance" part of the OBJECT
+IDENTIFIER is added as a prefix, like '1:ethernet', '2:ethernet', '3:fddi'.
+
+=cut
+
+#
+# snmpwalk.
+#
+sub snmpwalk ($@) {
+ my($host, @vars) = @_;
+ return(&snmpwalk_flg($host, undef, @vars));
+}
+
+=head2 snmpset() - send a SNMP set-request to the remote agent
+
+ @result = snmpset(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ [\%param_hash],
+ $oid1, $type1, $value1,
+ [$oid2, $type2, $value2 ...]
+ );
+
+This function is used to modify data on the remote agent using a SNMP
+set-request. The message is built using the list of values consisting of groups
+of an OBJECT IDENTIFIER, an object type, and the actual value to be set.
+The object type can be one of the following strings:
+
+ integer | int
+ string | octetstring | octet string
+ oid | object id | object identifier
+ ipaddr | ip addr4ess
+ timeticks
+ uint | uinteger | uinteger32 | unsigned int | unsigned integer | unsigned integer32
+ counter | counter 32
+ counter64
+ gauge | gauge32
+
+The object type may also be an octet corresponding to the ASN.1 type. See
+the Net::SNMP documentation for more information.
+
+The requested values are returned in an array in the same order as they were
+requested. In scalar context the first requested value is returned.
+
+=cut
+
+#
+# snmpset.
+#
+sub snmpset($@) {
+ my($host, @vars) = @_;
+ my($session, @vals, %args, $ret);
+ my($oid, $type, $value, @enoid, @retvals);
+
+ @retvals = ();
+ $session = &snmpopen($host, 0, \@vars);
+ if (!defined($session)) {
+ carp "SNMPSET Problem for $host"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ return wantarray ? @retvals : undef;
+ }
+
+ if ($Net_SNMP_util::Version > 2) {
+ $args{'-contextengineid'} = $Net_SNMP_util::ContextEngineID
+ if (defined($Net_SNMP_util::ContextEngineID));
+ $args{'-contextname'} = $Net_SNMP_util::ContextName
+ if (defined($Net_SNMP_util::ContextName));
+ }
+
+ while(@vars) {
+ ($oid) = toOID((shift @vars));
+ $ret = shift @vars;
+ $value = shift @vars;
+ $type = ASNtype($ret);
+ if (!defined($type)) {
+ carp "Unknown SNMP type: $type\n"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ }
+ push @vals, $oid, $type, $value;
+ push @enoid, $oid;
+ }
+ if ($#vals < 0) {
+ return wantarray ? @retvals : undef;
+ }
+
+ $args{'-varbindlist'} = \@vals;
+
+ $ret = $session->set_request(%args);
+ if ($ret) {
+ foreach $oid (@enoid) {
+ push @retvals, $ret->{$oid} if (exists($ret->{$oid}));
+ }
+ return wantarray ? @retvals : $retvals[0];
+ }
+ $ret = join(' ', @enoid);
+ error_msg("SNMPSET Problem for $ret on ${host}: " . $session->error());
+ return wantarray ? @retvals : undef;
+}
+
+=head2 snmptrap() - send a SNMP trap to the remote manager
+
+ @result = snmptrap(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ $enterprise,
+ $agentaddr,
+ $generictrap,
+ $specifictrap,
+ [\%param_hash],
+ $oid1, $type1, $value1,
+ [$oid2, $type2, $value2 ...]
+ );
+
+This function sends a SNMP trap to the remote manager on the host specified.
+The message is built using the list of values consisting of groups of an
+OBJECT IDENTIFIER, an object type, and the actual value to be set.
+The object type can be one of the following strings:
+
+ integer | int
+ string | octetstring | octet string
+ oid | object id | object identifier
+ ipaddr | ip addr4ess
+ timeticks
+ uint | uinteger | uinteger32 | unsigned int | unsigned integer | unsigned integer32
+ counter | counter 32
+ counter64
+ gauge | gauge32
+
+The object type may also be an octet corresponding to the ASN.1 type. See
+the Net::SNMP documentation for more information.
+
+A true value is returned if sending the trap is successful. The undefined value
+is returned when a failure has occurred.
+
+When the trap is sent as SNMPv2c, the B<$enterprise>, B<$agentaddr>,
+B<$generictrap>, and B<$specifictrap> arguments are ignored. Furthermore,
+the first two (oid, type, value) tuples should be:
+
+=over
+
+=item *
+
+sysUpTime.0 - ('1.3.6.1.2.1.1.3.0', 'timeticks', $timeticks)
+
+=item *
+
+snmpTrapOID.0 - ('1.3.6.1.6.3.1.1.4.1.0', 'oid', $oid)
+
+=back
+
+B<NOTE:> This function can only be used when the SNMP version is set to
+SNMPv1 or SNMPv2c.
+
+=cut
+
+#
+# Send an SNMP trap
+#
+sub snmptrap($$$$$@) {
+ my($host, $ent, $agent, $gen, $spec, @vars) = @_;
+ my($oid, $type, $value, $ret, @enoid, @vals);
+ my($session, %args);
+
+ $session = &snmpopen($host, 1, \@vars);
+ if (!defined($session)) {
+ carp "SNMPTRAP Problem for $host"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ return undef;
+ }
+
+ if ($Net_SNMP_util::Version == 1) {
+ $args{'-enterprise'} = $ent if (defined($ent) and (length($ent) > 0));
+ $args{'-agentaddr'} = $agent if (defined($agent) and (length($agent) > 0));
+ $args{'-generictrap'} = $gen if (defined($gen) and (length($gen) > 0));
+ $args{'-specifictrap'} = $spec if (defined($spec) and (length($spec) > 0));
+ } elsif ($Net_SNMP_util::Version > 2) {
+ carp "SNMPTRAP Problem for $host : must use SNMP version 1 or 2"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ }
+
+ while(@vars) {
+ ($oid) = toOID((shift @vars));
+ $ret = shift @vars;
+ $value = shift @vars;
+ $type = ASNtype($ret);
+ if (!defined($type)) {
+ carp "unknown SNMP type: $type"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ }
+ push @vals, $oid, $type, $value;
+ push @enoid, $oid;
+ }
+ return undef unless defined $vals[0];
+
+ $args{'-varbindlist'} = \@vals;
+
+ if ($Net_SNMP_util::Version == 1) {
+ $ret = $session->trap_request(%args);
+ } else {
+ $ret = $session->snmpv2_trap(%args);
+ }
+
+ if (!$ret) {
+ $ret = join(' ', @enoid);
+ error_msg("SNMPTRAP Problem for $ret on ${host}: " . $session->error());
+ }
+ return $ret;
+}
+
+=head2 snmpmaptable() - walk OBJECT IDENTIFIER tree(s) on the remote agent
+
+ $result = snmpmaptable(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ \&function,
+ [\%param_hash],
+ @oids
+ );
+
+This function performs a sequence of SNMP get-next-request or get-bulk-request
+(if the SNMP version is 2 or higher) queries to gather data from the remote
+agent on the host specified. The initial message is built using the list of
+OBJECT IDENTIFIERs passed as an array. Each OBJECT IDENTIFIER is placed into a
+single SNMP GetNextRequest-PDU in the same order that it held in the original
+list. Queries continue until all the returned OBJECT IDENTIFIERs are no longer
+a child of the base OBJECT IDENTIFIERs. The OBJECT IDENTIFIERs must correspond
+to column entries for a conceptual row in a table. They may however be columns
+in different tables as long as each table is indexed the same way.
+
+=over
+
+=item *
+
+The B<\&function> argument will be called once per row of the table. It
+will be passed the row index as a partial OBJECT IDENTIFIER in dotted notation,
+e.g. "1.3" or "10.0.1.34", and the values of the requested table columns in
+that row.
+
+=back
+
+The number of rows in the table is returned on success. The undefined value
+is returned when a failure has occurred.
+
+=cut
+
+#
+# walk a table, calling a user-supplied function for each
+# column of a table.
+#
+sub snmpmaptable($$@) {
+ my($host, $fun, @vars) = @_;
+ return snmpmaptable4($host, $fun, 0, @vars);
+}
+
+=head2 snmpmaptable4() - walk OBJECT IDENTIFIER tree(s) on the remote agent
+
+ $result = snmpmaptable4(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ \&function,
+ $maxrepetitions,
+ [\%param_hash],
+ @oids
+ );
+
+This function performs a sequence of SNMP get-next-request or get-bulk-request
+(if the SNMP version is 2 or higher) queries to gather data from the remote
+agent on the host specified. The initial message is built using the list of
+OBJECT IDENTIFIERs passed as an array. Each OBJECT IDENTIFIER is placed into a
+single SNMP GetNextRequest-PDU in the same order that it held in the original
+list. Queries continue until all the returned OBJECT IDENTIFIERs are no longer
+a child of the base OBJECT IDENTIFIERs. The OBJECT IDENTIFIERs must correspond
+to column entries for a conceptual row in a table. They may however be columns
+in different tables as long as each table is indexed the same way.
+
+=over
+
+=item *
+
+The B<\&function> argument will be called once per row of the table. It
+will be passed the row index as a partial OBJECT IDENTIFIER in dotted notation,
+e.g. "1.3" or "10.0.1.34", and the values of the requested table columns in
+that row.
+
+=item *
+
+The B<$maxrepetitions> argument specifies the number of rows to be returned
+by a single get-bulk-request. If it is null or undefined, the default value
+of 12 is used.
+
+=back
+
+The number of rows in the table is returned on success. The undefined value
+is returned when a failure has occurred.
+
+=cut
+
+sub snmpmaptable4($$$@) {
+ my($host, $fun, $max_reps, @vars) = @_;
+ my($session, @enoid, %args, $ret);
+ my($oid, $soid, $toid, $inst, @row, $nr);
+
+ $session = &snmpopen($host, 0, \@vars);
+ if (!defined($session)) {
+ carp "SNMPMAPTABLE Problem for $host"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ return undef;
+ }
+
+ @enoid = toOID(@vars);
+ return undef unless defined $enoid[0];
+
+ if ($Net_SNMP_util::Version > 1) {
+ $max_reps = $Net_SNMP_util::MaxRepetitions if ($max_reps <= 0);
+ $args{'-maxrepetitions'} = $max_reps;
+ }
+ if ($Net_SNMP_util::Version > 2) {
+ $args{'-contextengineid'} = $Net_SNMP_util::ContextEngineID
+ if (defined($Net_SNMP_util::ContextEngineID));
+ $args{'-contextname'} = $Net_SNMP_util::ContextName
+ if (defined($Net_SNMP_util::ContextName));
+ }
+
+ $args{'-columns'} = \@enoid;
+
+ $ret = $session->get_entries(%args);
+
+ if ($ret) {
+ $soid = $enoid[0];
+ $nr = 0;
+ foreach $oid (&Net::SNMP::oid_lex_sort(keys %$ret)) {
+ if (&Net::SNMP::oid_base_match($soid, $oid)) {
+ $inst = substr($oid, length($soid)+1);
+ undef @row;
+ foreach $toid (@enoid) {
+ push @row, $ret->{$toid . "." . $inst};
+ }
+ &$fun($inst, @row);
+ $nr++;
+ } else {
+ return($nr) if ($nr > 0);
+ }
+ }
+ return($nr);
+ } else {
+ $ret = join(' ', @vars);
+ error_msg("SNMPMAPTABLE Problem for $ret on ${host}: " . $session->error());
+ return undef;
+ }
+}
+
+=head2 snmpwalkhash() - send a SNMP get-next-request to the remote agent
+
+ @result = snmpwalkhash(
+ [community@]host[:port[:timeout[:retries[:backoff[:version]]]]],
+ \&function(),
+ [\%param_hash],
+ @oids,
+ [\%hash]
+ );
+
+This function performs a sequence of SNMP get-next-request or get-bulk-request
+(if the SNMP version is 2 or higher) queries to gather data from the remote
+agent on the host specified. The message is built using the list of
+OBJECT IDENTIFIERs passed as an array. Each OBJECT IDENTIFIER is placed into a
+single SNMP GetNextRequest-PDU in the same order that it held in the original
+list. Queries continue until all the returned OBJECT IDENTIFIERs are outside
+of the tree specified by the initial OBJECT IDENTIFIERs.
+
+The B<\&function> is called once for every returned value. It is passed a
+reference to a hash, the hostname, the textual OBJECT IDENTIFIER, the
+dotted-numberic OBJECT IDENTIFIER, the instance, the value and the requested
+textual OBJECT IDENTIFIER. That function can customize the result so the
+values can be extracted later by hosts, by oid_names, by oid_numbers,
+by instances... like these:
+
+ $hash{$host}{$name}{$inst} = $value;
+ $hash{$host}{$oid}{$inst} = $value;
+ $hash{$name}{$inst} = $value;
+ $hash{$oid}{$inst} = $value;
+ $hash{$oid . '.' . $ints} = $value;
+ $hash{$inst} = $value;
+ ...
+
+If the last argument to B<snmpwalkhash> is a reference to a hash, that hash
+reference is passed to the passed-in function instead of a local hash
+reference. That way the function can look up other objects unrelated
+to the current invocation of B<snmpwalkhash>.
+
+The snmpwalkhash routine returns the hash.
+
+=cut
+
+#
+# Walk the MIB, putting everything you find into hashes.
+#
+sub snmpwalkhash($$@) {
+# my($host, $hash_sub, @vars) = @_;
+ return(&snmpwalk_flg( @_ ));
+}
+
+
+=head2 snmpmapOID() - add texual OBJECT INDENTIFIER mapping
+
+ snmpmapOID(
+ $text1, $oid1,
+ [ $text2, $oid2 ...]
+ );
+
+This routine adds entries to the table that maps textual representation of
+OBJECT IDENTIFIERs to their dotted notation. For example,
+
+ snmpmapOID('ciscoCPU', '1.3.6.1.4.1.9.9.109.1.1.1.1.5.1');
+
+allows the string 'ciscoCPU' to be used as an OBJECT IDENTIFIER in any SNMP
+query routine.
+
+This routine doesn't return anything.
+
+=cut
+
+#
+# Add passed-in text, OID pairs to the OID mapping table.
+#
+sub snmpmapOID(@)
+{
+ my(@vars) = @_;
+ my($oid, $txt);
+
+ $Net_SNMP_util::ErrorMessage = '';
+ while($#vars >= 0) {
+ $txt = shift @vars;
+ $oid = shift @vars;
+
+ next unless($txt =~ /^[a-zA-Z][\w\-]*(\.[a-zA-Z][\w\-])*$/);
+ next unless($oid =~ /^\d+(\.\d+)*$/);
+
+ $Net_SNMP_util::OIDS{$txt} = $oid;
+ $RevNeeded = 1;
+ print "snmpmapOID: $txt => $oid\n" if $Net_SNMP_util::Debug;
+ }
+
+ return undef;
+}
+
+=head2 snmpLoad_OID_Cache() - Read a file of cached OID mappings
+
+ $result = snmpLoad_OID_Cache(
+ $file
+ );
+
+This routine opens the file named by the B<$file> argument and reads it.
+The file should contain text, OBJECT IDENTIFIER pairs, one pair
+per line. It adds the pairs as entries to the table that maps textual
+representation of OBJECT IDENTIFIERs to their dotted notation.
+Blank lines and anything after a '#' or between '--' is ignored.
+
+This routine returns 0 on success and -1 if the B<$file> could not be opened.
+
+=cut
+
+#
+# Open the passed-in file name and read it in to populate
+# the cache of text-to-OID map table. It expects lines
+# with two fields, the first the textual string like "ifInOctets",
+# and the second the OID value, like "1.3.6.1.2.1.2.2.1.10".
+#
+# blank lines and anything after a '#' or between '--' is ignored.
+#
+sub snmpLoad_OID_Cache ($) {
+ my($arg) = @_;
+ my($txt, $oid);
+
+ $Net_SNMP_util::ErrorMessage = '';
+ if (!open(CACHE, $arg)) {
+ error_msg("snmpLoad_OID_Cache: Can't open ${arg}: $!");
+ return -1;
+ }
+
+ while(<CACHE>) {
+ s/#.*//; # '#' starts a comment
+ s/--.*?--/ /g; # comment delimited by '--', like MIBs
+ s/--.*//; # comment started by '--'
+ next if (/^$/);
+ next unless (/\s/); # must have whitespace as separator
+ chomp;
+ ($txt, $oid) = split(' ', $_, 2);
+ $txt = $1 if ($txt =~ /^[\'\"](.*)[\'\"]/);
+ $oid = $1 if ($oid =~ /^[\'\"](.*)[\'\"]/);
+ if (($txt =~ /^\.?\d+(\.\d+)*\.?$/)
+ and ($oid !~ /^\.?\d+(\.\d+)*\.?$/)) {
+ my($a) = $oid;
+ $oid = $txt;
+ $txt = $a;
+ }
+ $oid =~ s/^\.//;
+ $oid =~ s/\.$//;
+ &snmpmapOID($txt, $oid);
+ }
+ close(CACHE);
+ return 0;
+}
+
+=head2 snmpMIB_to_OID() - Read a MIB file for textual OID mappings
+
+ $result = snmpMIB_to_OID(
+ $file
+ );
+
+This routine opens the file named by the B<$file> argument and reads it.
+The file should be an SNMP Management Information Base (MIB) file
+that describes OBJECT IDENTIFIERs supported by an SNMP agent.
+per line. It adds the textual representation of the OBJECT IDENTIFIERs
+to the text-to-OID mapping table.
+
+This routine returns the number of entries added to the table or -1 if
+the B<$file> could not be opened.
+
+=cut
+
+#
+# Read in the passed MIB file, parsing it
+# for their text-to-OID mappings
+#
+sub snmpMIB_to_OID ($) {
+ my($arg) = @_;
+ my($cnt, $quote, $buf, %tOIDs, $tgot);
+ my($var, @parts, $strt, $indx, $ind, $val);
+
+ $Net_SNMP_util::ErrorMessage = '';
+ if (!open(MIB, $arg)) {
+ error_msg("snmpMIB_to_OID: Can't open ${arg}: $!");
+ return -1;
+ }
+ print "snmpMIB_to_OID: loading $arg\n" if $Net_SNMP_util::Debug;
+ $cnt = 0;
+ $quote = 0;
+ $tgot = 0;
+ $buf = '';
+ while(<MIB>) {
+ if ($quote) {
+ next unless /"/;
+ $quote = 0;
+ }
+ chomp;
+ $buf .= ' ' . $_;
+
+ $buf =~ s/"[^"]*"//g; # throw away quoted strings
+ $buf =~ s/--.*?--/ /g; # throw away comments (-- anything --)
+ $buf =~ s/--.*//; # throw away comments (-- anything to EOL)
+ $buf =~ s/\s+/ /g; # clean up multiple spaces
+
+ if ($buf =~ /"/) {
+ $quote = 1;
+ next;
+ }
+
+ if ($buf =~ /DEFINITIONS *::= *BEGIN/) {
+ $cnt += MIB_fill_OID(\%tOIDs) if ($tgot);
+ $buf = '';
+ %tOIDs = ();
+ $tgot = 0;
+ next;
+ }
+ $buf =~ s/OBJECT-TYPE/OBJECT IDENTIFIER/;
+ $buf =~ s/OBJECT-IDENTITY/OBJECT IDENTIFIER/;
+ $buf =~ s/OBJECT-GROUP/OBJECT IDENTIFIER/;
+ $buf =~ s/MODULE-IDENTITY/OBJECT IDENTIFIER/;
+ $buf =~ s/NOTIFICATION-TYPE/OBJECT IDENTIFIER/;
+ $buf =~ s/ IMPORTS .*\;//;
+ $buf =~ s/ SEQUENCE *{.*}//;
+ $buf =~ s/ SYNTAX .*//;
+ $buf =~ s/ [\w\-]+ *::= *OBJECT IDENTIFIER//;
+ $buf =~ s/ OBJECT IDENTIFIER.*::= *{/ OBJECT IDENTIFIER ::= {/;
+
+ if ($buf =~ / ([\w\-]+) OBJECT IDENTIFIER *::= *{([^}]+)}/) {
+ $var = $1;
+ $buf = $2;
+ $buf =~ s/ +$//;
+ $buf =~ s/\s+\(/\(/g; # remove spacing around '('
+ $buf =~ s/\(\s+/\(/g;
+ $buf =~ s/\s+\)/\)/g; # remove spacing before ')'
+ @parts = split(' ', $buf);
+ $strt = '';
+ foreach $indx (@parts) {
+ if ($indx =~ /([\w\-]+)\((\d+)\)/) {
+ $ind = $1;
+ $val = $2;
+ if (exists($tOIDs{$strt})) {
+ $tOIDs{$ind} = $tOIDs{$strt} . '.' . $val;
+ } elsif ($strt ne '') {
+ $tOIDs{$ind} = "${strt}.${val}";
+ } else {
+ $tOIDs{$ind} = $val;
+ }
+ $strt = $ind;
+ $tgot = 1;
+ } elsif ($indx =~ /^\d+$/) {
+ if (exists($tOIDs{$strt})) {
+ $tOIDs{$var} = $tOIDs{$strt} . '.' . $indx;
+ } else {
+ $tOIDs{$var} = "${strt}.${indx}";
+ }
+ $tgot = 1;
+ } else {
+ $strt = $indx;
+ }
+ }
+ $buf = '';
+ }
+ }
+ $cnt += MIB_fill_OID(\%tOIDs) if ($tgot);
+ $RevNeeded = 1 if ($cnt > 0);
+ return $cnt;
+}
+
+=head2 snmpQueue_MIB_File() - queue a MIB file for reading "on demand"
+
+ snmpQueue_MIB_File(
+ $file1,
+ [$file2, ...]
+ );
+
+This routine queues the list of SNMP MIB files for later processing.
+Whenever a text-to-OBJECT IDENTIFIER lookup fails, the list of queued MIB
+files is consulted. If it isn't empty, the first MIB file in the list is
+removed and passed to B<snmpMIB_to_OID()>. The lookup is attempted again,
+and if that still fails the next MIB file in the list is removed and passed
+to B<snmpMIB_to_OID()>. This process continues until the lookup succeeds
+or the list is exhausted.
+
+This routine doesn't return anything.
+
+=cut
+
+#
+# Save the passed-in list of MIB files until an OID can't be
+# found in the existing table. At that time the MIB file will
+# be loaded, and the lookup attempted again.
+#
+sub snmpQueue_MIB_File (@) {
+ my(@files) = @_;
+ my($file);
+
+ $Net_SNMP_util::ErrorMessage = '';
+ foreach $file (@files) {
+ push(@Net_SNMP_util::MIB_Files, $file);
+ }
+}
+
+# [private methods] -------------------------------------
+
+#
+# Start an snmp session
+#
+sub snmpopen ($$$) {
+ my($host, $type, $vars) = @_;
+ my($nhost, $port, $community, $lhost, $lport, $nlhost);
+ my($timeout, $retries, $backoff, $version, $v4onlystr);
+ my($opts, %args, $tmp, $sess);
+ my($debug, $maxmsgsize);
+
+ $type = 0 if (!defined($type));
+ $community = "public";
+ $nlhost = "";
+
+ ($community, $host) = ($1, $2) if ($host =~ /^(.*)@([^@]+)$/);
+
+ # We can't split on the : character because a numeric IPv6
+ # address contains a variable number of :'s
+ if( ($host =~ /^(\[.*\]):(.*)$/) or ($host =~ /^(\[.*\])$/) ) {
+ # Numeric IPv6 address between []
+ ($host, $opts) = ($1, $2);
+ } else {
+ # Hostname or numeric IPv4 address
+ ($host, $opts) = split(':', $host, 2);
+ }
+ ($port, $timeout, $retries, $backoff, $version, $v4onlystr)
+ = split(':', $opts, 6) if(defined($opts) and (length $opts > 0) );
+
+ undef($timeout) if (defined($timeout) and length($timeout) <= 0);
+ undef($retries) if (defined($retries) and length($retries) <= 0);
+ undef($backoff) if (defined($backoff) and length($backoff) <= 0);
+ undef($version) if (defined($version) and length($version) <= 0);
+
+ $v4onlystr = "" unless defined $v4onlystr;
+
+ if (defined($port) and ($port =~ /^([^!]*)!(.*)$/)) {
+ ($port, $lhost) = ($1, $2);
+ $nlhost = $lhost;
+ ($lhost, $lport) = ($1, $2) if ($lhost =~ /^(.*)!(.*)$/);
+ undef($lport) if (defined($lport) and (length($lport) <= 0));
+ }
+ undef($port) if (defined($port) and length($port) <= 0);
+
+ if (ref $vars->[0] eq 'HASH') {
+ undef($debug);
+ undef($maxmsgsize);
+ undef $Net_SNMP_util::ContextEngineID;
+ undef $Net_SNMP_util::ContextName;
+ $opts = shift @$vars;
+ foreach $type (keys %$opts) {
+ if ($type =~ /^-?return_array_refs$/i) {
+ $Net_SNMP_util::ReturnArrayRefs = $opts->{$type};
+ } elsif ($type =~ /^-?return_hash_refs$/i) {
+ $Net_SNMP_util::ReturnHashRefs = $opts->{$type};
+ } elsif ($type =~ /^-?contextengineid$/i) {
+ $Net_SNMP_util::ContextEngineID = $opts->{$type};
+ } elsif ($type =~ /^-?contextname$/i) {
+ $Net_SNMP_util::ContextName = $opts->{$type};
+ } elsif ($type =~ /^-?maxrepetitions$/i) {
+ $Net_SNMP_util::MaxRepetitions = $opts->{$type};
+ } elsif ($type =~ /^-?default_max_repetitions$/i) {
+ $Net_SNMP_util::MaxRepetitions = $opts->{$type};
+ } elsif ($type =~ /^-?version$/i) {
+ $version = $opts->{$type};
+ } elsif ($type =~ /^-?port$/i) {
+ $port = $opts->{$type};
+ } elsif ($type =~ /^-?localaddr$/i) {
+ $lhost = $opts->{$type};
+ } elsif ($type =~ /^-?community$/i) {
+ $community = $opts->{$type};
+ } elsif ($type =~ /^-?timeout$/i) {
+ $timeout = $opts->{$type};
+ } elsif ($type =~ /^-?retries$/i) {
+ $retries = $opts->{$type};
+ } elsif ($type =~ /^-?maxmsgsize$/i) {
+ $maxmsgsize = $opts->{$type};
+ } elsif ($type =~ /^-?debug$/i) {
+ $debug = $opts->{$type};
+ } elsif ($type =~ /^-?backoff$/i) {
+ next; # XXXX not implemented in Net::SNMP
+ } elsif ($type =~ /^-?avoid_negative_request_ids$/i) {
+ next; # XXXX not implemented in Net::SNMP
+ } elsif ($type =~ /^-?lenient_source_/i) {
+ next; # XXXX not implemented in Net::SNMP
+ } elsif ($type =~ /^-?use_16bit_request_ids$/i) {
+ next; # XXXX not implemented in Net::SNMP
+ } elsif ($type =~ /^-?use_getbulk$/i) {
+ next; # XXXX not implemented in Net::SNMP
+ } else {
+ $tmp = $type;
+ $tmp = '-' . $tmp unless ($tmp =~ /^-/);
+ $args{$tmp} = $opts->{$type};
+ }
+ }
+ }
+
+ $port = 162 if ($type == 1 and !defined($port));
+ $nhost = "$community\@$host";
+ $nhost .= ":" . $port if (defined($port));
+ undef($lhost) if (defined($lhost) and (length($lhost) <= 0));
+
+ $version = '1' unless defined $version;
+ if ($version =~ /1/) {
+ $version = 1;
+ } elsif ($version =~ /2/) {
+ $version = 2;
+ } elsif ($version =~ /3/) {
+ $version = 3;
+ }
+ $Net_SNMP_util::ErrorMessage = '';
+ if ((!defined($Net_SNMP_util::Session))
+ or ($Net_SNMP_util::Host ne $nhost)
+ or ($Net_SNMP_util::Version ne $version)
+ or ($Net_SNMP_util::LHost ne $nlhost)
+ or ($Net_SNMP_util::IPv4only ne $v4onlystr)) {
+ if (defined($Net_SNMP_util::Session)) {
+ $Net_SNMP_util::Session->close();
+ undef $Net_SNMP_util::Session;
+ undef $Net_SNMP_util::Host;
+ undef $Net_SNMP_util::Version;
+ undef $Net_SNMP_util::LHost;
+ undef $Net_SNMP_util::IPv4only;
+ }
+
+ $args{'-hostname'} = $host;
+ $args{'-port'} = $port if (defined($port));
+ $args{'-localaddr'} = $lhost if (defined($lhost));
+ $args{'-localport'} = $lport if (defined($lport));
+ $args{'-version'} = $version;
+ $args{'-domain'} = "udp/ipv4" if (length($v4onlystr) > 0);
+ $args{'-timeout'} = $timeout if (defined($timeout));
+ $args{'-retries'} = $retries if (defined($retries));
+ $args{'-maxmsgsize'} = $maxmsgsize if (defined($maxmsgsize));
+ $args{'-debug'} = $debug if (defined($debug));
+ $args{'-community'} = $community unless ($community eq "public");
+ if ($version == 3) {
+ delete $args{'-community'}
+ } else {
+ delete $args{'-username'};
+ delete $args{'-authkey'};
+ delete $args{'-authpassword'};
+ delete $args{'-authprotocol'};
+ delete $args{'-privkey'};
+ delete $args{'-privpassword'};
+ delete $args{'-privprotocol'};
+ }
+
+ ($sess, $tmp) = Net::SNMP->session(%args);
+
+ if (defined($sess)) {
+ $Net_SNMP_util::Session = $sess;
+ $Net_SNMP_util::Host = $nhost;
+ $Net_SNMP_util::Version = $version;
+ $Net_SNMP_util::LHost = $nlhost;
+ $Net_SNMP_util::IPv4only = $v4onlystr;
+ } else {
+ error_msg("SNMPopen failed: $tmp\n");
+ return(undef);
+ }
+ return $Net_SNMP_util::Session;
+ } else {
+ $Net_SNMP_util::Session->timeout($timeout)
+ if (defined($timeout) and (length($timeout) > 0));
+ $Net_SNMP_util::Session->retries($retries)
+ if (defined($retries) and (length($retries) > 0));
+ $Net_SNMP_util::Session->maxmsgsize($maxmsgsize)
+ if (defined($maxmsgsize) and (length($maxmsgsize) > 0));
+ $Net_SNMP_util::Session->debug($debug)
+ if (defined($debug) and (length($debug) > 0));
+ $Net_SNMP_util::Session->{_context_engine_id} = undef
+ if (!defined($Net_SNMP_util::ContextEngineID));
+ $Net_SNMP_util::Session->{_context_name} = undef
+ if (!defined($Net_SNMP_util::ContextName));
+ }
+ return $Net_SNMP_util::Session;
+}
+
+#
+# Given an OID in either ASN.1 or mixed text/ASN.1 notation, return an OID.
+#
+sub toOID(@) {
+ my(@vars) = @_;
+ my($oid, $var, $tmp, $tmpv, @retvar);
+
+ @retvar = ();
+ foreach $var (@vars) {
+ ($oid, $tmp) = &Check_OID($var);
+ if (!$oid and $Net_SNMP_util::CacheLoaded == 0) {
+ $tmp = $Net_SNMP_util::SuppressWarnings;
+ $Net_SNMP_util::SuppressWarnings = 1000;
+
+ &snmpLoad_OID_Cache($Net_SNMP_util::CacheFile);
+
+ $Net_SNMP_util::CacheLoaded = 1;
+ $Net_SNMP_util::SuppressWarnings = $tmp;
+
+ ($oid, $tmp) = &Check_OID($var);
+ }
+ while (!$oid and $#Net_SNMP_util::MIB_Files >= 0) {
+ $tmp = $Net_SNMP_util::SuppressWarnings;
+ $Net_SNMP_util::SuppressWarnings = 1000;
+
+ snmpMIB_to_OID(shift(@Net_SNMP_util::MIB_Files));
+
+ $Net_SNMP_util::SuppressWarnings = $tmp;
+
+ ($oid, $tmp) = &Check_OID($var);
+ if ($oid) {
+ open(CACHE, ">>$Net_SNMP_util::CacheFile");
+ print CACHE "$tmp\t$oid\n";
+ close(CACHE);
+ }
+ }
+ if ($oid) {
+ $var =~ s/^$tmp/$oid/;
+ } else {
+ carp("Unknown SNMP var $var\n")
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ next;
+ }
+ while ($var =~ /\"([^\"]*)\"/) {
+ $tmp = sprintf("%d.%s", length($1), join(".", map(ord, split(//, $1))));
+ $var =~ s/\"$1\"/$tmp/;
+ }
+ print "toOID: $var\n" if $Net_SNMP_util::Debug;
+ push(@retvar, $var);
+ }
+ return @retvar;
+}
+
+#
+# Check to see if an OID is in the text-to-OID cache.
+# Returns the OID and the corresponding text as two separate
+# elements.
+#
+sub Check_OID ($) {
+ my($var) = @_;
+ my($tmp, $tmpv, $oid);
+
+ if ($var =~ /^[a-zA-Z][\w\-]*(\.[a-zA-Z][\w\-]*)*/) {
+ $tmp = $&;
+ $tmpv = $tmp;
+ for (;;) {
+ last if exists($Net_SNMP_util::OIDS{$tmpv});
+ last if !($tmpv =~ s/^[^\.]*\.//);
+ }
+ $oid = $Net_SNMP_util::OIDS{$tmpv};
+ if ($oid) {
+ return ($oid, $tmp);
+ } else {
+ my @empty = ();
+ return @empty;
+ }
+ }
+ return ($var, $var);
+}
+
+sub snmpwalk_flg ($$@) {
+ my($host, $hash_sub, @vars) = @_;
+ my($session, %args, @enoid, @poid, $toid, $oid, $got);
+ my($val, $ret, %soid, %nsoid, @retvals, $tmp);
+ my(%rethash, $h_ref, @tmprefs);
+ my($stop);
+
+ $h_ref = (ref $vars[$#vars] eq "HASH") ? pop(@vars) : \%rethash;
+
+ $session = &snmpopen($host, 0, \@vars);
+ if (!defined($session)) {
+ carp "SNMPWALK Problem for $host"
+ unless ($Net_SNMP_util::SuppressWarnings > 1);
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($SNMP_util::Return_hash_refs);
+ return (%$h_ref);
+ } else {
+ @retvals = ();
+ return (@retvals);
+ }
+ }
+
+ @enoid = toOID(@vars);
+ if ($#enoid < 0) {
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($SNMP_util::Return_hash_refs);
+ return (%$h_ref);
+ } else {
+ @retvals = ();
+ return (@retvals);
+ }
+ }
+
+ #
+ # Create/Refresh a reversed hash with oid -> name
+ #
+ if (defined($hash_sub) and ($RevNeeded)) {
+ %revOIDS = reverse %Net_SNMP_util::OIDS;
+ $RevNeeded = 0;
+ }
+
+ #
+ # Create temporary array of refs to return values
+ #
+ foreach $oid (0..$#enoid) {
+ my $tmparray = [];
+ $tmprefs[$oid] = $tmparray;
+ $nsoid{$oid} = $oid;
+ }
+
+ $got = 0;
+ @poid = @enoid;
+
+ if ($Net_SNMP_util::Version > 1 and $Net_SNMP_util::MaxRepetitions > 0) {
+ $args{'-maxrepetitions'} = $Net_SNMP_util::MaxRepetitions;
+ }
+ if ($Net_SNMP_util::Version > 2) {
+ $args{'-contextengineid'} = $Net_SNMP_util::ContextEngineID
+ if (defined($Net_SNMP_util::ContextEngineID));
+ $args{'-contextname'} = $Net_SNMP_util::ContextName
+ if (defined($Net_SNMP_util::ContextName));
+ }
+
+ while($#poid >= 0) {
+ $args{'-varbindlist'} = \@poid;
+ if (($Net_SNMP_util::Version > 1)
+ and ($Net_SNMP_util::MaxRepetitions > 1)) {
+ $ret = $session->get_bulk_request(%args);
+ } else {
+ $ret = $session->get_next_request(%args);
+ }
+ last if (!defined($ret));
+
+ %soid = %nsoid;
+ undef %nsoid;
+ $stop = 0;
+ foreach $oid (&Net::SNMP::oid_lex_sort(keys %$ret)) {
+ $got = 1;
+ $tmp = -1;
+ foreach $toid (@enoid) {
+ $tmp++;
+ if (&Net::SNMP::oid_base_match($toid, $oid)
+ and (!exists($soid{$toid}) or ($oid ne $soid{$toid}))) {
+ $nsoid{$toid} = $oid;
+ if (defined($hash_sub)) {
+ #
+ # extract name of the oid, if possible, the rest becomes the
+ # instance
+ #
+ my $inst = "";
+ my $upo = $toid;
+ while (!exists($revOIDS{$upo}) and length($upo)) {
+ $upo =~ s/(\.\d+?)$//;
+ if (defined($1) and length($1)) {
+ $inst = $1 . $inst;
+ } else {
+ $upo = "";
+ last;
+ }
+ }
+ if (length($upo) and exists($revOIDS{$upo})) {
+ $upo = $revOIDS{$upo} . $inst;
+ } else {
+ $upo = $toid;
+ }
+
+ my $qoid = $oid;
+ my $tmpo;
+ $inst = "";
+ while (!exists($revOIDS{$qoid}) and length($qoid)) {
+ $qoid =~ s/(\.\d+?)$//;
+ if (defined($1) and length($1)) {
+ $inst = $1 . $inst;
+ } else {
+ $qoid = "";
+ last;
+ }
+ }
+ if (length($qoid) and exists($revOIDS{$qoid})) {
+ $tmpo = $qoid;
+ $qoid = $revOIDS{$qoid};
+ } else {
+ $qoid = $oid;
+ $tmpo = $toid;
+ $inst = substr($oid, length($tmpo)+1);
+ }
+ #
+ # call hash_sub
+ #
+ &$hash_sub($h_ref, $host, $qoid, $tmpo, $inst, $ret->{$oid}, $upo);
+ } else {
+ my $tmpo;
+ my $tmpv = $ret->{$oid};
+ $tmpo = substr($oid, length($toid)+1);
+ push @{$tmprefs[$tmp]}, "$tmpo:$tmpv";
+ }
+ } else {
+ $stop = 1 if ($#enoid == 0);
+ }
+ }
+ }
+ undef @poid;
+ @poid = values %nsoid if (!$stop);
+ }
+ if ($got) {
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($Net_SNMP_util::ReturnHashRefs);
+ return (%$h_ref);
+ } elsif ($Net_SNMP_util::Return_array_refs) {
+ return (@tmprefs);
+ } else {
+ do {
+ $got = 0;
+ foreach $toid (0..$#enoid) {
+ next if (scalar(@{$tmprefs[$toid]}) <= 0);
+ $got = 1;
+ $oid = shift(@{$tmprefs[$toid]});
+ if ($#enoid > 0) {
+ ($oid, $val) = split(':', $oid, 2);
+ $oid = $enoid[$toid] . '.' . $oid;
+ push(@retvals, "$oid:$val");
+ } else {
+ push(@retvals, $oid);
+ }
+ }
+ } while($got);
+ return (@retvals);
+ }
+ } else {
+ $ret = join(' ', @vars);
+ error_msg("SNMPWALK Problem for $ret on ${host}: " . $session->error());
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($SNMP_util::Return_hash_refs);
+ return (%$h_ref);
+ } else {
+ @retvals = ();
+ return (@retvals);
+ }
+ }
+}
+
+#
+# When passed a string, return the ASN.1 type that corresponds to the
+# string.
+#
+sub ASNtype($) {
+ my($type) = @_;
+
+ $type =~ tr/A-Z/a-z/;
+ if ($type eq "int") {
+ $type = 0x02;
+ } elsif ($type eq "integer") {
+ $type = 0x02;
+ } elsif ($type eq "string") {
+ $type = 0x04;
+ } elsif ($type eq "octetstring") {
+ $type = 0x04;
+ } elsif ($type eq "octet string") {
+ $type = 0x04;
+ } elsif ($type eq "oid") {
+ $type = 0x06;
+ } elsif ($type eq "object id") {
+ $type = 0x06;
+ } elsif ($type eq "object identifier") {
+ $type = 0x06;
+ } elsif ($type eq "ipaddr") {
+ $type = 0x40;
+ } elsif ($type eq "ip address") {
+ $type = 0x40;
+ } elsif ($type eq "timeticks") {
+ $type = 0x43;
+ } elsif ($type eq "uint") {
+ $type = 0x47;
+ } elsif ($type eq "uinteger") {
+ $type = 0x47;
+ } elsif ($type eq "uinteger32") {
+ $type = 0x47;
+ } elsif ($type eq "unsigned int") {
+ $type = 0x47;
+ } elsif ($type eq "unsigned integer") {
+ $type = 0x47;
+ } elsif ($type eq "unsigned integer32") {
+ $type = 0x47;
+ } elsif ($type eq "counter") {
+ $type = 0x41;
+ } elsif ($type eq "counter32") {
+ $type = 0x41;
+ } elsif ($type eq "counter64") {
+ $type = 0x46;
+ } elsif ($type eq "gauge") {
+ $type = 0x42;
+ } elsif ($type eq "gauge32") {
+ $type = 0x42;
+ } elsif (($type <= 0) or ($type > 255)) {
+ return undef;
+ }
+ return $type;
+}
+
+#
+# set the ErrorMessage global and print an error message
+#
+sub error_msg($)
+{
+ my($msg) = @_;
+ $Net_SNMP_util::ErrorMessage = $msg;
+ if ($Net_SNMP_util::SuppressWarnings <= 1) {
+ $Carp::CarpLevel++;
+ carp($msg);
+ $Carp::CarpLevel--;
+ }
+}
+
+#
+# Fill the OIDS hash with results from the MIB parsing
+#
+sub MIB_fill_OID($)
+{
+ my($href) = @_;
+ my($cnt, $changed, @del, $var, $val, @parts, $indx);
+ my(%seen);
+
+ $cnt = 0;
+ do {
+ $changed = 0;
+ @del = ();
+ foreach $var (keys %$href) {
+ $val = $href->{$var};
+ @parts = split('\.', $val);
+ $val = '';
+ foreach $indx (@parts) {
+ if ($indx =~ /^\d+$/) {
+ $val .= '.' . $indx;
+ } else {
+ if (exists($Net_SNMP_util::OIDS{$indx})) {
+ $val = $Net_SNMP_util::OIDS{$indx};
+ } else {
+ $val .= '.' . $indx;
+ }
+ }
+ }
+ if ($val =~ /^[\d\.]+$/) {
+ $val =~ s/^\.+//;
+ if (!exists($Net_SNMP_util::OIDS{$var})
+ || (length($val) > length($Net_SNMP_util::OIDS{$var}))) {
+ $Net_SNMP_util::OIDS{$var} = $val;
+ print "'$var' => '$val'\n" if $Net_SNMP_util::Debug;
+ $changed = 1;
+ $cnt++;
+ }
+ push @del, $var;
+ }
+ }
+ foreach $var (@del) {
+ delete $href->{$var};
+ }
+ } while($changed);
+
+ $Carp::CarpLevel++;
+ foreach $var (sort keys %$href) {
+ $val = $href->{$var};
+ $val =~ s/\..*//;
+ next if (exists($seen{$val}));
+ $seen{$val} = 1;
+ $seen{$var} = 1;
+ error_msg(
+ "snmpMIB_to_OID: prefix \"$val\" unknown, load the parent MIB first.\n"
+ );
+ }
+ $Carp::CarpLevel--;
+ return $cnt;
+}
+
+
+# [documentation] ------------------------------------------------------------
+
+=head1 EXPORTS
+
+The Net_SNMP_util module uses the F<Exporter> module to export useful
+constants and subroutines. These exportable symbols are defined below and
+follow the rules and conventions of the F<Exporter> module (see L<Exporter>).
+
+=over
+
+=item Exportable
+
+&snmpget, &snmpgetnext, &snmpgetbulk, &snmpwalk, &snmpset, &snmptrap,
+&snmpmaptable, &snmpmaptable4, &snmpwalkhash, &snmpmapOID, &snmpMIB_to_OID,
+&snmpLoad_OID_Cache, &snmpQueue_MIB_File, ErrorMessage
+
+=back
+
+=head1 EXAMPLES
+
+=head2 1. SNMPv1 get-request for sysUpTime
+
+This example gets the sysUpTime from a remote host.
+
+ #! /usr/local/bin/perl
+ use strict;
+ use Net_SNMP_util;
+ my ($host, $ret)
+ $host = shift || 'localhost';
+ $ret = snmpget($host, 'sysUpTime');
+
+ print("sysUpTime for $host is $ret\n");
+
+ exit 0;
+
+=head2 2. SNMPv3 set-request of sysContact
+
+This example sets the sysContact information on the remote host to
+"Help Desk x911". The parameters passed to the snmpset function are for
+the demonstration of syntax only. These parameters will need to be
+set according to the SNMPv3 parameters of the remote host used by the script.
+
+ #! /usr/local/bin/perl
+ use strict;
+ use Net_SNMP_util;
+ my($host, %v3hash, $ret);
+ $host = shift || 'localhost';
+ $v3hash{'-version'} = 'snmpv3';
+ $v3hash{'-username'} = 'myv3Username';
+ $v3hash{'-authkey'} = '0x05c7fbde31916f64da4d5b77156bdfa7';
+ $v3hash{'-authprotocol'} = 'md5';
+ $v3hash{'-privkey'} = '0x93725fd3a02a48ce02df4e065a1c1746';
+
+ $ret = snmpset($host, \%v3hash, 'sysContact', 'string', 'Help Desk x911');
+
+ print "sysContact on $host is now $ret\n";
+ exit 0;
+
+=head2 3. SNMPv2c walk for ifTable
+
+This example gets the contents of the ifTable by sending get-bulk-requests
+until the responses are no longer part of the ifTable. The ifTable can also
+be retrieved using C<snmpmaptable>.
+
+ #! /usr/local/bin/perl
+ use strict;
+ use Net_SNMP_util;
+ my($host, @ret, $oid, $val);
+ $host = shift || 'localhost';
+
+ @ret = snmpwalk($host . ':::::2', 'ifTable');
+ foreach $val (@ret) {
+ ($oid, $val) = split(':', $val, 2);
+ print "$oid => $val\n";
+ }
+ exit 0;
+
+=head2 4. SNMPv2c maptable collecting ifDescr, ifInOctets, and ifOutOctets.
+
+This example collects a table containing the columns ifDescr, ifInOctets, and
+ifOutOctets. A printing function is called once per row.
+
+ #! /usr/local/bin/perl
+ use strict;
+ use Net_SNMP_util;
+
+ sub printfun($$$$) {
+ my($inst, $desc, $in, $out) = @_;
+ printf "%3d %-52.52s %10d %10d\n", $inst, $desc, $in, $out;
+ }
+
+ my($host, @ret);
+ $host = shift || 'localhost';
+
+ printf "%-3s %-52s %10s %10s\n", "Int", "Description", "In", "Out";
+ @ret = snmpmaptable($host . ':::::2', \&printfun,
+ 'ifDescr', 'ifInOctets', 'ifOutOctets');
+
+ exit 0;
+
+=head1 REQUIREMENTS
+
+=over
+
+=item *
+
+The Net_SNMP_util module uses syntax that is not supported in versions of Perl
+earlier than v5.6.0.
+
+=item *
+
+The Net_SNMP_util module uses the F<Net::SNMP> module, and as such may depend
+on other modules. Please see the documentaion on F<Net::SNMP> for more
+information.
+
+=back
+
+=head1 AUTHOR
+
+Mike Mitchell <Mike.Mitchell at sas.com>
+
+=head1 ACKNOWLEGEMENTS
+
+The original concept for this module was based on F<SNMP_Session.pm> written
+by Simon Leinen <simon at switch.ch>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2007 Mike Mitchell. All rights reserved. This program
+is free software; you may redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
+# ======================================================================
+1; # [end Net_SNMP_util]
diff --git a/README b/README
index 7a9c52a..5ede513 100644
--- a/README
+++ b/README
@@ -42,12 +42,13 @@ This program is free software; you can redistribute it under the
Andrew Cornford-Matheson <andrew.matheson at corenetworks.com>
Gerry Dalton <gerry.dalton at consolidated.com>
Jan van Keulen <cologne at email.com>
+ Armin Wolfermann <armin at wolfermann.org>
http://www.switch.ch/misc/leinen/snmp/perl/index.html
This archive contains Perl 5 modules SNMP_Session.pm and BER.pm,
which, when used together, provide rudimentary access to remote SNMP
-(v1) agents.
+(v1/v2) agents.
This module differs from existing SNMP packages in that it is
completely stand-alone, i.e. you don't need to have another SNMP
@@ -65,233 +66,7 @@ tool: <URL:http://oss.oetiker.ch/mrtg/>
Usage
.....
-The basic usage of these routines works like this:
-
- use BER;
- require 'SNMP_Session.pm';
-
- # Set $host to the name of the host whose SNMP agent you want
- # to talk to. Set $community to the community name under
- # which you want to talk to the agent. Set port to the UDP
- # port on which the agent listens (usually 161).
-
- $session = SNMP_Session->open ($host, $community, $port)
- or die "couldn't open SNMP session to $host";
-
- # Set $oid1, $oid2... to the BER-encoded OIDs of the MIB
- # variables you want to get.
-
- if ($session->get_request_response ($oid1, $oid2, ...)) {
- ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
-
- while ($bindings ne '') {
- ($binding,$bindings) = &decode_sequence ($bindings);
- ($oid,$value) = &decode_by_template ($binding, "%O%@");
- print &pretty_print ($oid)," => ", &pretty_print ($value), "\n";
- }
- } else {
- die "No response from agent on $host";
- }
- Encoding OIDs
- .............
-
-In order to BER-encode OIDs, you can use the function BER::encode_oid.
-It takes (a vector of) numeric subids as an argument. For example,
-
- use BER;
- encode_oid (1, 3, 6, 1, 2, 1, 1, 1, 0)
-
-will return the BER-encoded OID for the sysDescr.0 (1.3.6.1.2.1.1.1.0)
-instance of MIB-2.
-
- Decoding the results
- ....................
-
-When get_request_response returns success, you must decode the
-response PDU from the remote agent. The function
-`decode_get_response' can be used to do this. It takes a get-response
-PDU, checks its syntax and returns the "bindings" part of the PDU.
-This is where the remote agent actually returns the values of the
-variables in your query.
-
-You should iterate over the individual bindings in this "bindings"
-part and extract the value for each variable. In the example above,
-the returned bindings are simply printed using the BER::pretty_print
-function.
-
-For better readability of the OIDs, you can also use the following
-idiom, where the %pretty_oids hash maps BER-encoded numerical OIDs to
-symbolic OIDs. Note that this simple-minded mapping only works for
-response OIDs that exactly match known OIDs, so it's unsuitable for
-table walking (where the response OIDs include an additional row
-index).
-
- %ugly_oids = qw(sysDescr.0 1.3.6.1.2.1.1.1.0
- sysContact.0 1.3.6.1.2.1.1.4.0);
- foreach (keys %ugly_oids) {
- $ugly_oids{$_} = encode_oid (split (/\./, $ugly_oids{$_}));
- $pretty_oids{$ugly_oids{$_}} = $_;
- }
- ...
- if ($session->get_request_response ($ugly_oids{'sysDescr.0'},
- $ugly_oids{'sysContact.0'})) {
- ($bindings) = $session->decode_get_response
- ($session->{pdu_buffer});
- while ($bindings ne '') {
- ($binding,$bindings) = &decode_sequence ($bindings);
- ($oid,$value) = &decode_by_template ($binding, "%O%@");
- print $pretty_oids{$oid}," => ",
- &pretty_print ($value), "\n";
- }
- } ...
-
- Set Requests
- ............
-
-Set requests are generated much like get or getNext requests are, with
-the exception that you have to specify not just OIDs, but also the
-values the variables should be set to. Every binding is passed as a
-reference to a two-element array, the first element being the encoded
-OID and the second one the encoded value. See the `test/set-test.pl'
-script for an example, in particular the subroutine `snmpset'.
-
- Walking Tables
- ..............
-
-Beginning with version 0.57 of SNMP_Session.pm, there is API support
-for walking tables. The map_table method can be used for this as
-follows:
-
- sub walk_function ($$$) {
- my ($index, $val1, $val3) = @_;
- ...
- }
-
- ...
- $columns = [$base_oid1, $base_oid3];
- $n_rows = $session->map_table ($columns, \&walk_function);
-
-The COLUMNS argument must be a reference to a list of OIDs for table
-columns sharing the same index. The method will traverse the table
-and call the WALK_FUNCTION for each row. The arguments for these calls
-will be:
-
-* the row index as a partial OID in dotted notation, e.g. "1.3", or
- "10.0.1.34".
-
-* the values of the requested table columns in that row, in
- BER-encoded form. If you want to use the standard pretty_print
- subroutine to decode the values, you can use the following idiom:
-
- grep (defined $_ && ($_=pretty_print $_), ($val1, $val3));
-
- Sending Traps
- .............
-
-To send a trap, you have to open an SNMP session to the trap receiver.
-Usually this is a process listening to UDP port 162 on a network
-management station. Then you can use the trap_request_send method to
-encode and send the trap. There is no way to find out whether the
-trap was actually received at the management station - SNMP traps are
-fundamentally unreliable.
-
-When constructing an SNMPv1 trap, you must provide
-
-* the "enterprise" Object Identifier for the entity that generates the
- trap
-
-* your IP address
-
-* the generic trap type
-
-* the specific trap type
-
-* the sysUpTime at the time of trap generation
-
-* a sequence (may be empty) of variable bindings further describing
- the trap.
-
-For SNMPv2 traps, you need:
-
-* the trap's OID
-
-* the sysUpTime at the time of trap generation
-
-* the bindings list as above
-
-For SNMPv2 traps, the uptime and trap OID are encoded as bindings
-which are added to the front of the other bindings you provide.
-
-Here is a short example:
-
- my $trap_receiver = "netman.noc";
- my $trap_community = "SNMP_Traps";
- my $trap_session = $version eq '1'
- ? SNMP_Session->open ($trap_receiver, $trap_community, 162)
- : SNMPv2c_Session->open ($trap_receiver, $trap_community, 162);
- my $trap_session = SNMP_Session->open ($trap_receiver, $trap_community, 162);
- my $myIpAddress = ...;
- my $start_time = time;
-
- ...
-
- sub link_down_trap ($$) {
- my ($if_index, $version) = @_;
- my $genericTrap = 2; # linkDown
- my $specificTrap = 0;
- my @ifIndexOID = ( 1,3,6,1,2,1,2,2,1,1 );
- my $upTime = int ((time - $start_time) * 100.0);
- my @myOID = ( 1,3,6,1,4,1,2946,0,8,15 );
-
- warn "Sending trap failed"
- unless ($version eq '1')
- ? $trap_session->trap_request_send (encode_oid (@myOID),
- encode_ip_address ($myIpAddress),
- encode_int ($genericTrap),
- encode_int ($specificTrap),
- encode_timeticks ($upTime),
- [encode_oid (@ifIndex_OID,$if_index),
- encode_int ($if_index)],
- [encode_oid (@ifDescr_OID,$if_index),
- encode_string ("foo")])
- : $trap_session->v2_trap_request_send (\@linkDown_OID, $upTime,
- [encode_oid (@ifIndex_OID,$if_index),
- encode_int ($if_index)],
- [encode_oid (@ifDescr_OID,$if_index),
- encode_string ("foo")]);
- }
-
- Receiving Traps
- ...............
-
-Since version 0.60, SNMP_Session.pm supports the receipt and decoding
-of SNMPv1 trap requests. Since version 0.75, SNMPv2 Trap PDUs are
-also recognized.
-
-To receive traps, you have to create a special SNMP session that
-passively listens on the SNMP trap transport address (usually UDP port
-162). Then you can receive traps (actually, SNMPv1 traps, SNMPv2
-traps, and SNMPv2 informs) using the receive_trap method and decode
-them using decode_trap_request. The enterprise, agent, generic,
-specific and sysUptime return values are only defined for SNMPv1
-traps. In SNMPv2 traps and informs, the equivalent information is
-contained in the bindings.
-
- my $trap_session = SNMPv1_Session->open_trap_session ()
- or die "cannot open trap session";
- my ($trap, $sender_addr, $sender_port) = $trap_session->receive_trap ()
- or die "cannot receive trap";
- my ($community, $enterprise, $agent,
- $generic, $specific, $sysUptime, $bindings)
- = $session->decode_trap_request ($trap)
- or die "cannot decode trap received"
- ...
- my ($binding, $oid, $value);
- while ($bindings ne '') {
- ($binding,$bindings) = &decode_sequence ($bindings);
- ($oid, $value) = decode_by_template ("%O%@");
- print BER::pretty_oid ($oid)," => ",pretty_print ($value),"\n";
- }
+See the EXAMPLES section of the POD documentation in SNMP_Session.pm.
Future Plans
............
diff --git a/changes.html b/changes.html
new file mode 100644
index 0000000..f2cdb61
--- /dev/null
+++ b/changes.html
@@ -0,0 +1,709 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+ <HEAD>
+ <TITLE>SNMP support for Perl 5: Changes</TITLE>
+ </HEAD>
+ <BODY bgcolor="#ffffff">
+<DIV ALIGN=CENTER>
+ <H1>SNMP support for Perl 5: Changes</H1>
+
+ <p> Copyright (c) 1995-2009, Simon Leinen<br>
+ All rights reserved </p>
+
+ <em> This program is free software; you can redistribute it under
+ the <a href="http://language.perl.com/misc/Artistic.html">"Artistic
+ License"</a> included in this distribution. </em>
+
+ <p>Author: <A HREF="http://www.switch.ch/misc/leinen/">Simon
+Leinen</A> <<A
+HREF="mailto:simon.leinen at switch.ch">simon.leinen at switch.ch</A>></p>
+
+</DIV>
+
+<h2> Recent Changes: </h2>
+
+<ul>
+
+<li> SNMP_Session.pm 1.14: <tt>open_trap_session</tt> now takes an
+ optional <tt>ipv4only</tt> argument. It defaults to 1, but by
+ passing 0, you can create a session that accepts traps over IPv6 in
+ addition to IPv4. The <tt>receive_trap</tt> method has been
+ enhanced by returning an additional value which is the address
+ family. There is a new <tt>receive_trap_1</tt> method, which
+ differs from <tt>receive_trap</tt> in that it simply returns the
+ trap and a sockaddr structure, rather than the trap, the host
+ address, the port, and the address family. </li>
+
+<li> BER.pm 1.14: Added POD documentation. Started adding automated
+ unit tests. </li>
+
+<li> SNMP_Session.pm 1.14: Added POD documentation. </li>
+
+<li> SNMP_util.pm 1.13: Parse OIDs in NOTIFICATION-TYPE names. Change
+ by Mike Mitchell/jaccobs. </li>
+
+<li> SNMP_Session.pm 1.13: Fixed the optional socket-reuse code to
+ handle IPv4 and IPv6 sockets separately. </li>
+
+<li> SNMP_Session.pm 1.12: Upgraded to Artistic License 2.0 upon a
+ suggestion from Tom Callaway. </li>
+
+<li> SNMP_util.pm 1.12: Rewritten MIB parsing code from Mike
+ Mitchell. </li>
+
+<li> SNMP_util.pm 1.11: Improvement to loop detection in snmpwalk.
+ Should cope with more broken agents. Change by Mike Mitchell. </li>
+
+<li> SNMP_Session.pm 1.10: Mike Fischer reported that on some systems,
+ notably Linux, <tt>recv()</tt> may block even though a
+ preceding <tt>select()</tt> for readability has returned
+ successfully. Once condition where this can happen is when a UDP
+ checksum on an incoming datagram doesn't verify. To avoid our
+ library from blocking in this case, we have to
+ pass <tt>MSG_DONTWAIT</tt> to <tt>recv()</tt>. This behaviour is
+ selected by a new optional argument to <tt>receive_response_3</tt>,
+ and used by <tt>request_response_5</tt>. </li>
+
+<li> SNMP_util.pm 1.09: Fix from Mike Mitchell for parsing qualified
+ symbolic OIDs. </li>
+
+<li> SNMP_Session.pm 1.08: Fixed a bug in the SNMPv2c version of
+<tt>map_table_start_end</tt> that would cause errors when the
+<samp>$end</samp> argument is actually being used. Thanks to Jan van
+Keulen for submitting the patch. </li>
+
+<li> SNMP_Session.pm 1.07: Fixed <tt>strict subs</tt> error with
+newer versions of Perl. Thanks to Gerry Dalton for spotting
+this. </li>
+
+<li> SNMP_Session.pm 1.06: <tt>decode_trap_request</tt> now
+understands SNMPv2 inform requests in addition to SNMPv1 traps and
+SNMPv2 traps. Contributed by Andrew Cornford-Matheson. </li>
+
+<li> SNMP_util.pm 1.06: <tt>snmpwalkhash</tt> fix by Laurent
+Girod. </li>
+
+<li> <tt>index.html</tt>: The code is no longer available from
+<tt>ftp.switch.ch</tt>, but via HTTP from
+<tt>www.switch.ch</tt>. </li>
+
+<li> SNMP_Session.pm 1.05: No change; version incremented for
+<tt>SNMP_util.pm</tt> and <tt>BER.pm</tt>. </li>
+
+<li> BER.pm 1.05: Internal restructuring: The new (BER.pm 1.02)
+pretty-printer registration method is now used for most standard SNMP
+types, too. </li>
+
+<li> SNMP_util.pm 1.04: The subroutines <tt>snmpget</tt>,
+<tt>snmpgetnext</tt> and <tt>snmpset</tt> will now detect when they
+are called in scalar context, and return only the first (and typically
+the only) of the retrieved values in this case. </li>
+
+<li> SNMP_util.pm 1.03: Added a missing part of Mike Mitchell
+pretty-printer registration patch. </li>
+
+<li> BER.pm 1.02: Additional decoders (``pretty printers'') can now be
+registered and unregistered for type codes using the
+<tt>register_pretty_printer</tt> and
+<tt>unregister_pretty_printer</tt> subroutines. Implemented by Mike
+Mitchell. </li>
+
+<li> BER.pm 1.02: Corrected encoding of large integer-like
+values. </li>
+
+<li> SNMP_util.pm 1.02: <tt>snmpwalkhash</tt> now takes an additional
+optional argument that should be a reference to a hash. If used,
+<tt>snmpwalkhash</tt> will insert the retrieved values into that
+hash. </li>
+
+<li> BER.pm 1.01: Properly parse the variant length format in
+integers. Thanks to <a href="mailto:milen at batmbg.com">Milen
+Pavlov</a> for pointing out this bug. Also, decoding should be
+slightly more efficient in general, because one <tt>substr()</tt>
+operation per (sub-) object has been eliminated. </li>
+
+<li> SNMP_Session 1.00: Added Luc Pauwels' implementation of the
+<tt>use_16bit_request_ids</tt> option. Some SMC devices seem to
+require this. </li>
+
+<li> SNMP_Session 0.99: Improved request ID generation so that
+<tt>avoid_negative_request_ids</tt> is always obeyed. </li>
+
+<li> SNMP_util 0.99: Added <tt>Gauge32</tt> support to
+<tt>snmpset</tt>. Thanks to <a
+href="mailto:tengi at CS.Princeton.EDU">Christopher J. Tengi</a> for the
+idea. Mike Mitchell provided new code that supports all known
+types. </li>
+
+<li> SNMP_Session 0.98: Portability fix in IPv6 support. </li>
+
+<li> SNMP_util 0.98: Support encoding of OIDs containing multiple
+quoted strings. </li>
+
+<li> SNMP_util 0.97: Added support for TimeTicks values in
+<tt>snmpset</tt>. Patch from <a
+href="mailto:JOERG.KUMMER at Roche.COM">Joerg Kummer</a> </li>
+
+<li> SNMP_Session 0.97: Exported
+<tt>$SNMP_Session::default_avoid_negative_request_ids</tt>, by
+Philippe Simonet. </li>
+
+<li> SNMP_Session 0.97: IPv6 support added by Valerio Bontempi and
+Lorenzo Colitti. </li>
+
+<li> SNMP_Session 0.96: Intermediate version with IPv6 support; not
+published. </li>
+
+<li> BER.pm 0.95: Fixed operator precedence bug in
+decode_sequence(). </li>
+
+<li> SNMP_Session.pm 0.94: A new slot <tt>capture_buffer</tt> has been
+added to the SNMP_Session classes, courtesy <a
+href="mailto:jakob.ilves at oracle.com">Jakob Ilves</a>. See the sample
+script <tt>test/capturetest.pl</tt> for an example of how to use
+this. </li>
+
+<li> BER.pm 0.94: New subroutines <tt>pretty_generic_sequence</tt>,
+<tt>decode_generic_tlv</tt>, courtesy <a
+href="mailto:jakob.ilves at oracle.com">Jakob Ilves</a>. </li>
+
+<li> BER.pm 0.94: <tt>decode_by_template</tt>, <tt>decode_oid</tt>,
+<tt>decode_sequence</tt>, <tt>decode_string</tt>: Explicitly signal
+error when the PDU is undefined or too short. This improves error
+handling for some malformed PDUs, as generated by certain test
+suites. </li>
+
+<li> SNMP_Session.pm 0.93: There's a new variable
+<tt>$default_avoid_negative_request_ids</tt>. If it is set to a
+non-zero value, newly created <tt>SNMP_Session</tt> objects will be
+configured so that only request IDs in the range 0..2<sup>31</sup>-1
+will be used. This is needed to work around a bug in several SNMP
+agents. If you sometimes see requests fail, and the error message
+always shows a negative request ID when that happens, please notify
+the vendor of your agent of the bug. While the vendor fixes the
+problem, you can set the variable mentioned above to work around the
+bug. The problem has been described in a few mails on the
+<em>mrtg-developers</em> mailing list that can hopefully be found <a
+href="http://www.ee.ethz.ch/~slist/mrtg-developers/msg01609.html">here</a>
+in the archive. </li>
+
+<li> SNMP_util.pm 0.93: <tt>snmpwalk</tt> now will walk multiple OID
+trees simultaneously. If more than one OID is passed to the function,
+the returned <em>OID:value</em> pairs will contain the entire OID
+instead of just the leaf from the starting point. </li>
+
+<li> SNMP_util.pm 0.93: <tt>snmpwalkhash</tt> now passes the textual
+OID of the starting point as the seventh argument to the passed-in
+hash function. </li>
+
+<li> SNMP_util.pm 0.92: New subroutine <tt>snmpmaptable4</tt>, which
+allows specifying the <em>max-repeaters</em>. </li>
+
+<li> SNMP_util.pm 0.92: New MIB parsing code. Most MIBs are processed
+in one pass, but if an OID cannot be fully resolved, an extra pass is
+done. Also, <samp>OBJECT-GROUP</samp> entries are now
+recognized. </li>
+
+<li> SNMP_util.pm 0.92: The string-to-OID conversion routines now
+transparently convert quoted strings to instance indexes. For
+example, <samp>"/usr"</samp> (including the quotes) would be converted
+to <samp>4.47.118.97.114</samp>.
+
+<li> SNMP_Session.pm 0.91: Fixed a bug in <tt>map_table_start_end</tt>
+for <tt>SNMPv2_Session</tt> (which uses <tt>get-bulk</tt>) which had
+caused a superfluous query after the entire table has already been
+provably traversed. Thanks to Michael Deegan for pointing this
+out. </li>
+
+<li> SNMP_util.pm 0.90: New version from Mike Mitchell.
+ <tt>snmpwalkhash</tt> and <tt>snmpwalk</tt> now share most
+ code. </li>
+
+<li> SNMP_util.pm 0.90: Corrected handling of the optional port number in
+ <tt>snmpopen</tt>. </li>
+
+<li> test/if-counters.pl: Support 64-bit counters, and use
+<tt>ifAlias</tt> to portably get interface descriptions. </li>
+
+<li> SNMP_util.pm 0.89: New version from Mike Mitchell, with new
+ <tt>snmpwalkhash</tt> subroutine by <a
+ href="mailto:girod.laurent at pmintl.ch">Laurent Girod</a>. </li>
+
+<li> SNMP_Session.pm 0.89: Made <tt>lenient_source_port_matching</tt>
+the default. </li>
+
+<li> SNMP_util.pm 0.89: Fixed a long-standing bug where the code would
+generate PDUs with <tt>request-id</tt>s with large positive values,
+violating the SNMP spec which mandates that <tt>request-id</tt>s be
+<tt>Integer32</tt>s. Thanks to Sergio Macedo <macedo at tmp.com.br> for
+finding this bug. </li>
+
+<li> SNMP_util.pm 0.88: Added missing <samp>use Carp;</samp>
+statements in packages <samp>SNMPv1_Session</samp> and
+<samp>SNMPv2_Session</samp>. Thanks to <a
+href="mailto:michael at cnspc18.murdoch.edu.au">Michael Deegan</a>. </li>
+
+<li> SNMP_util.pm 0.87: No change from 0.86, but increased version
+number to reflect change in BER.pm (SNMPv2 exception codes). </li>
+
+<li> BER.pm 0.87: <samp>pretty_print</samp> now silently returns undef
+when decoding SNMPv2 exception codes (<tt>noSuchObject</tt>,
+<tt>noSuchInstance</tt>, or <tt>endOfMibView</tt>, see RFC 1905).
+Original patch by <a href="mailto:driehuis at playbeing.org">Bert
+Driehuis</a>. </li>
+
+<li> BER.pm 0.86: <samp>pretty_print</samp> now silently returns undef
+when given an undefined value, rather than issuing incomprehensible
+warnings. </li>
+
+<li> SNMP_util.pm 0.86: New <samp>snmpmaptable</samp> subroutine.
+This is a more user-friendly version of <samp>map_table</samp> and is
+described in the <a href="dist/README.SNMP_util">README.SNMP_util</a>
+file. From Mike Mitchell. </li>
+
+<li> SNMP_util.pm 0.86: Support for the awesome <tt>get-bulk</tt>
+operator, both directly through the new <samp>snmpgetbulk</samp>
+subroutine, and transparently via <samp>snmpwalk</samp> when the
+session's SNMP version is >= 2 and the <samp>use_getbulk</samp>
+slot is set (as it is by default). From Mike Mitchell. </li>
+
+<li> SNMP_Session.pm 0.85: If a local address is specified in
+<samp>snmpopen</samp>, don't convert it using <samp>inet_aton</samp>,
+because this is handled by the Socket library. Fix from <a
+href="mailto:mikem at open.com.au">Mike McCauley</a>. </li>
+
+<li> SNMP_Session.pm 0.85: Added
+<samp>lenient_source_port_matching</samp> slot to the session object.
+Set this to communicate with weird SNMP agents that send the response
+from a port other than 161. Suggestion from <a
+href="mailto:hgomez at slib.fr">Henri Gomez</a>. </li>
+
+<li> SNMP_util.pm 0.84: New version from Mike Mitchell.
+<samp>snmpopen</samp> now parses an optional hash argument for
+options. Also reintroduced defaulting of the UDP port to 161 for
+``normal'' sessions. </li>
+
+<li> SNMP_Session.pm 0.84: Clarified documentation concerning the
+<samp>%pretty_oids</samp> hash, upon a suggestion from <a
+href="mailto:alistair at alizta.com">Alistair Mills</a>. </li>
+
+<li> SNMP_Session.pm 0.83: The source address in response packets is
+now ignored when matching responses against outstanding queries. In a
+couple of previous revisions, the source address in response packets
+had to match the destination address in the corresponding query.
+Unfortunately some agents may use a different source address in
+responses. If you want the strict behavior back, you can send the
+<tt>lenient_source_address_matching</tt> slot of the session object to
+zero. </li>
+
+<li> SNMP_Session.pm 0.83: The source address for outgoing packets can
+now be specified as an additional optional argument to
+<tt>open</tt>. If you don't specify it, the system will choose the
+source address by itself, usually corresponding to the interface on
+which packets are sent. </li>
+
+<li> SNMP_Session.pm 0.83: Fixed a bug which had caused requests to be
+resent upon receipt of packets which don't match the outstanding
+query. </li>
+
+<li> SNMP_Session.pm 0.82: Fixed retry logic to avoid sending a last
+retry without waiting for the response anymore. Thanks to <a
+href="mailto:wardenb at eluminant.com">Brett T Warden</a> for the
+fix. </li>
+
+<li> BER.pm 0.82: OIDs with only two subids can now be encoded. The
+most common case is the "null" OID (0.0). Thanks to <a
+href="mailto:wardenb at eluminant.com">Brett T Warden</a> for pointing out
+that this didn't work. </li>
+
+<li> BER.pm 0.82: pretty_print() now handles UInteger32 objects.
+Patch by <a href="mailto:wardenb at eluminant.com">Brett T
+Warden</a>. </li>
+
+<li> BER.pm 0.81: Subids in the range 2^31 - 2^32-1 are now encoded
+correctly. Thanks to <a href="mailto:rik.hoorelbeke at pandora.be">Rik
+Hoorelbeke</a> for noticing the problem. </li>
+
+<li> SNMP_Session.pm 0.81: A cosmetic bug in the SNMPv2 version of
+<tt>map_table</tt> (which uses <tt>get-bulk</tt>) was corrected. The
+user-supplied function is now always called on the same number of
+arguments. Before this, missing values at the end of a table row
+would lead to the function being called with fewer arguments. Now
+there will be <tt>undef</tt> values for those, too. Thanks to <a
+href="mailto:schmid at switch.ch">Ulrich Schmid</a> for pointing this
+out. </li>
+
+<li> SNMP_Session.pm 0.80: A portability bug was fixed in the code
+that matches incoming responses to outstanding requests. The bug had
+manifested itself, notably on some FreeBSD versions, by timeouts
+waiting for responses, because the library thought the responses came
+from another address than the corresponding requests had been sent
+to. </li>
+
+<li> SNMP_Session.pm 0.79: A new variable
+<tt>$SNMP_Session::recycle_socket</tt> has been introduced. When this
+variable is set to a non-zero value (the default is zero), all newly
+created SNMP_Session objects will share the same UDP socket. This
+saves file descriptors and system calls, but will cause problems with
+multiple outstanding SNMP requests on different session objects.
+Applications which don't perform parallel/asynchronous SNMP requests
+can safely set this variable to reduce OS overhead somewhat.
+Suggestion from <a href="mailto:schmid at switch.ch">Ulrich
+Schmid</a>. </li>
+
+<li> SNMP_Session.pm 0.79: The handling of incoming packets from
+unexpected addresses has been cleaned up. If a packet is received
+from an IP address other than the one to which the request has been
+sent, this packet is silently ignored, as mandated by the SNMP
+standard. </li>
+
+<li> SNMP_Session.pm 0.79: Receive-only session objects (such as the
+ones created by <tt>open_trap_session</tt> now have <samp>undef</samp>
+as the <samp>remote_addr</samp> value, rather than IP address
+<samp>0.0.0.0</samp>. </li>
+
+<li> BER.pm 0.79: There are new exported subroutines for encoding
+different types of values: <samp>encode_uinteger32</samp>,
+<samp>encode_counter32</samp>, <samp>encode_counter64</samp>,
+<samp>encode_gauge32</samp>. </li>
+
+<li> SNMP_Session.pm 0.78: The <tt>map_table</tt> implementation for
+SNMPv2 sessions has been completely rewritten to reliably support
+tables with holes in them. Note that this hasn't been completely
+validated or tested yet, but at least it has been found to handle
+common cases reasonably. </li>
+
+<li> BER.pm 0.77: Added support for long integers, so that
+<tt>Counter64</tt> values can be handled without risk of losing
+precision (due to automatic coercion to floating-point representation
+by Perl). When a BER-encoded integer is so long that it might not fit
+in a 32-bit unsigned integer, then we use <tt>Math::BigInt</tt>
+arithmetics to convert it. <b>Warning:</b> code which calls the
+decoding functions should be prepared to handle <tt>Math::BigInt</tt>
+values if <tt>Counter64</tt> values can be accessed. In most respect,
+those long integers behave just like ordinary integers. A notable
+exception is that they print with a leading ``<tt>+</tt>'' sign. </li>
+
+<li> SNMP_Session 0.77: The SNMPv2 implementation of
+<tt>map_table_start_end</tt> has been enhanced to cope with the case
+where a response PDU ends with a truncated table row, courtesy <a
+href="mailto:pee at gblx.net">Paul E. Erkkila</a>. Note that tables with
+missing entries still aren't handled correctly when SNMPv2 (and thus
+the <tt>get-bulk</tt> operator is used). </li>
+
+<li> SNMP_util 0.73: Added a fix from <a
+href="mailto:mcm at unx.sas.com">Mike Mitchell</a> (originally <a
+href="mailto:demel at zid.tuwien.ac.at">Johannes Demel</a>) to treat
+<tt>OBJECT-IDENTITY</tt> like <tt>OBJECT IDENTIFIER</tt>.
+
+<li> SNMP_Session 0.76: Added some debugging support for
+<tt>map_table</tt>, which still doesn't work reliably when the SNMPv2
+<tt>get-bulk</tt> operator is used. </li>
+
+<li> test/if-counters.pl: Added <samp>-c</samp> option to enable
+Cisco-specific variables (which are no longer retrieved by default as
+in previous versions). </li>
+
+<li> SNMP_Session 0.75: Fixed a bug in versions 0.73-0.74 where
+creation of trap listener sockets would fail. Rather than using
+<tt>bind</tt> to bind to the trap port, we now pass a
+<tt>LocalPort</tt> argument to <tt>INET->new</tt>. </li>
+
+<li> SNMP_Session 0.75: Parse SNMPv2-Trap-Requests in addition to
+SNMPv1 ones. A caller can tell whether an SNMPv1 or an SNMPv2 request
+has been received by testing whether the SNMPv1-specific fields are
+defined. The sample script <tt>test/trap-listener</tt> has been
+updated to understand SNMPv2 Traps. </li>
+
+<li> SNMP_Session 0.75: New subroutine <tt>v2_trap_request_send</tt>
+which sends SNMPv2 Trap PDUs. The sample script
+<tt>test/trap-send</tt> has been extended to generate either type of
+trap on request. </li>
+
+<li> SNMP_Session 0.74: Put under copyright and Artistic
+License. </li>
+
+<li> SNMP_util 0.72: Changed <tt>snmpgetnext</tt> so it will return
+the next lexicographical larger OID number, even if it is not in the
+same OID tree. Before the change snmpget would discard the return
+value if the OID wasn't in the same tree. In <tt>snmpMIB_to_OID</tt>,
+an "unitialized variable" warning was removed. (Changes by <a
+href="mailto:mcm at unx.sas.com">Mike Mitchell</a>, the author) </li>
+
+<li> SNMP_Session 0.73: The UDP socket associated with each SNMP
+session object is now created using <tt>IO::Socket::INET->new</tt>.
+Before this change, a Perl file descriptor name was generated for each
+new socket. Apparently this caused a file descriptor leak. The new
+code is much cleaner and doesn't have that problem anymore. Hopefully
+the newly introduced dependency on the Socket::IO module is not a
+problem. Thanks to <a href="mailto:elble at icculus.nsg.nwu.edu">Andrew
+W. Elble</a> for suggesting this change. </li>
+
+<li> BER.pm 0.72: A new variable,
+<tt>$BER::pretty_print_timeticks</tt>, has been introduced to give
+users control over the degree of pretty-printing of <tt>TimeTicks</tt>
+values. If left at the defaults, <tt>TimeTicks</tt> values will be
+converted to strings such as <samp>14 days, 6:56:07</samp>. If you
+set it to zero, the same value will simply pretty-print as
+<samp>123456789</samp>, which should be interpreted in units (ticks)
+of 10ms. </li>
+
+<li> SNMP_util.pm 0.71: New subroutines <tt>snmpLoad_OID_Cache</tt> and
+<tt>snmpQueue_MIB_File</tt> for loading MIBs in compact format. </li>
+
+<li> SNMP_util.pm 0.71: <tt>snmpset</tt> now accepts <tt>ipaddr</tt>
+as a type specifier. </li>
+
+<li> SNMP_util.pm 0.70: <a href="mailto:mcm at unx.sas.com">Mike
+Mitchell</a> added code for parsing MIB files. See the description of
+<tt>snmpMIB_to_OID</tt> in <a
+href="dist/README.SNMP_util"><tt>README.SNMP_util</tt></a>. </li>
+
+<li> SNMP_util.pm 0.69: Enable parsing of community strings which
+contain <samp>@</samp> characters. </li>
+
+<li> SNMP_util.pm 0.58: Check for errors from <tt>encode_oid</tt>, so
+that illegal OIDs generate error messages. Allow for suppression of
+warnings by setting <tt>$SNMP_Session::suppress_warnings</tt> to a
+value greater than one. </li>
+
+<li> SNMP_Session.pm 0.68: Added methods <tt>receive_request</tt> and
+<tt>decode_request</tt> to support SNMP agents. Contributed by <a
+href="mailto:mikem at open.com.au">Mike McCauley</a>. </li>
+
+<li> SNMP_Session.pm 0.67: Implement a <tt>map_table_start_end</tt>
+method for <tt>SNMPv2c_Session</tt> which uses <tt>get-bulk</tt>. See
+<a href="#map-table-4-use">Walking Tables With <tt>get-bulk</tt></a>
+for more information. </li>
+
+<li> SNMP_Session.pm 0.66: Fix from <a
+href="mailto:Alan.Nichols at Ebay.Sun.COM">Alan Nichols</a> to the
+handling of error replies. In the last few revisions of SNMP_Session,
+requests to which the agent responded with a non-zero
+<tt>errorStatus</tt> were erroneously retried. </li>
+
+<li> SNMP_Session.pm 0.66: When the <tt>errorStatus</tt> is zero, we
+don't care about the <tt>errorIndex</tt>. This makes us liberal
+enough to cope with very old versions of the CMU agent code which
+sometimes put a non-zero <tt>errorIndex</tt> in normal response
+packets. </li>
+
+<li> BER.pm 0.66: Changed the <tt>$VERSION</tt> number to be in line
+with SNMP_Session.pm's. </li>
+
+<li> BER.pm 0.66: Changed <tt>encode_oid</tt> so that it signals an
+error when passed an illegal Object ID, such as one whose first subid
+isn't 0, 1 or 2. This should help people who try to use output from
+CMU/UCD SNMP directly in MRTG. </li>
+
+<li> SNMP_Session.pm 0.65: Fix error message when binding to UDP port
+fails (<a href="mailto:jzhukovs at staff.juno.com">Jonathan
+Zhukovsky</a>). </li>
+
+<li> SNMP_util.pm 0.57: Small change to avoid warnings with "-w" when
+session parameters are defaulted. </li>
+
+<li> SNMP_util.pm 0.56: New subroutine <tt>snmpmapOID</tt>, <a
+href="mailto:mcm at unx.sas.com">Mike Mitchell</a>. </li>
+
+<li> SNMP_Session.pm 0.64: Fix of a bug in the detection of missing
+responses by <a href="mailto:mcm at unx.sas.com">Mike Mitchell</a>. </li>
+
+<li> SNMP_Session.pm 0.63: Fixed response matching logic to ignore
+ out-of-sequence responses. This has been a long-standing bug which
+ made it very hard to use a single session object for multiple queries
+ to devices that are (sometimes) slow in responding. </li>
+
+<li> SNMP_Session.pm 0.62: Scoping problem with pretty_address()
+ fixed. </li>
+
+<li> Makefile.PL: New file which allows for easy installation
+ according to standard Perl convention. Kudos to <a
+ href="mailto:clintdw at netcom.com">Clinton Wong</a>. </li>
+
+<li> SNMP_Session.pm 0.61: The sender address of the last response
+ received for a session is stored in
+ <em>$session->{'last_sender_addr'}</em>. In connection with
+ broadcast or multicast addresses, this can be used to discover SNMP
+ agents listening to specific communities, as illustrated in
+ <tt>test/discover</tt>. </li>
+
+<li> SNMP_Session.pm 0.60: Added support for <a
+ href="#trap-recv">receiving SNMPv1 traps</a>. </li>
+
+<li> SNMP_session.pm 0.59: Added methods <tt>set_timeout</tt>,
+ <tt>set_retries</tt>, and <tt>set_backoff</tt> that can be used to
+ tune the retransmission algorithm. Fixed a bug in <tt>map_table</tt>
+ that would cause an index of "0" to terminate the table walk. </li>
+
+<li> SNMP_util.pm 0.54: First version to be distributed with the
+ package, courtesy <a href="mailto:mcm at unx.sas.com">Mike
+ Mitchell</a>. </li>
+
+<li> BER.pm 0.58: Added <tt>encode_timeticks()</tt> subroutine. This
+ was used in the sample script for <a href="#trap-send">sending
+ traps</a>, but was only defined in <tt>test/trap-test.pl</tt>.
+ Thanks to <a href="mailto:bergerg at att.net">Gary Berger</a> for
+ noticing this. </li>
+
+<li> BER.pm 0.57: Added <tt>encode_ip_address()</tt> subroutine on a
+ suggestion by <a href="mailto:mdiehn at mindspring.net">Mike
+ Diehn</a>. </li>
+
+<li> SNMP_Session 0.58: Added support for generating traps, courtesy
+ <a href="mailto:mcm at unx.sas.com">Mike Mitchell</a>, see <a
+ href="#trap-send">Sending Traps</a>. </li>
+
+<li> <tt>test/ber-test.pl</tt>: Added more test cases contributed by
+ <a href="mailto:mcm at unx.sas.com">Mike Mitchell</a>. </li>
+
+<li> SNMP_Session 0.57: table walking support See ``<a
+ href="#map-table-use">Walking Tables</a>'' below for how this is
+ used. </li>
+
+<li> BER 0.56: New <tt>encode_int()</tt> subroutine contributed by <a
+href="mailto:mcm at unx.sas.com">Mike Mitchell</a>. Fixes incorrect
+encoding in the range +-2^15-2^23 and generalized to integers of any
+size. </li>
+
+<li> BER 0.55: Fix an arithmetic bug in the uptime
+pretty-printer. Kudos to <a href="mailto:niels at euro.net">Niels
+Bakker</a> for noticing this. </li>
+
+<li> SNMP_Session 0.56: Fix a bug which occurs when an error should be
+signaled while an SNMPv1_Session is being opened. Noticed by <a
+href="mailto:dcox at lexmark.com">Dan Cox</a> and <a
+href="mailto:pakhomenko at gmd.de">Iouri Pakhomenko</a>. </li>
+
+<li> BER 0.52: Ignore a leading dot when encoding an OID. This is to
+avoid trouble when people cut&paste OIDs from CMU/UCD SNMP, where
+a leading dot is used to mark a "fully qualified" OID. </li>
+
+<li> SNMP_Session 0.55: The SNMP_Session module no longer calls
+<tt>warn</tt> when the variable
+<tt>$SNMP_Session::suppress_warnings</tt> is set to non-zero (it is
+zero by default). The error message from SNMP_Session can be
+retrieved as <tt>$SNMP_Session::errmsg</tt>. </li>
+
+<li> SNMP_Session 0.54, BER.pm 0.51: Errors in the BER module are now
+passed upwards by the SNMP_Session module. Before this change, one
+could not distinguish malformed SNMP responses from no response at
+all. The BER module now no longer calls die(), but returns undefined
+values. </li>
+
+<li> Added <tt>test/arp</tt> which prints the NetToMedia table from a
+remote host. </li>
+
+<li> SNMP_Session 0.53: Avoid passing numeric IP addresses to
+<tt>inet_ntoa()</tt>, by <a href="mailto:dan_needles at INS.COM">Daniel
+L. Needles</a> </li>
+
+<li> SNMP_Session 0.52: setRequest support based on code contributed
+by <a href="mailto:matter at media.mit.edu">Matthew Trunnell</a>. See
+``<a href="#set-req-use">Set Requests</a>'' below for how this is
+used. </li>
+
+<li> SNMP_Session 0.51: Improved error messages by printing the
+session in error messages if possible, and the OID and error message
+whenever the agent sends back an error. </li>
+
+<li> SNMP_Session 0.50, BER 0.50: The <b>BER</b> and
+<b>SNMP_Session</b> modules both have version numbers according to the
+convention in <b>Exporter.pm</b>. That is, you can now insist on a
+minimal version of the modules by saying e.g.
+
+<pre>
+use BER "0.50";
+use SNMP_Session "0.52";
+</pre>
+
+ The initial version numbers are 0.50 for both modules. </li>
+
+<li> The pretty printer should now print unsigned 32-bit values (such
+ as Counters and Gauges) correctly, i.e. values larger than
+ 2<sup><font size="-2">31</font></sup> are printed as large positive
+ numbers rather than negative numbers. Note that this can cause
+ problems depending on how you handle the output of the pretty
+ printer, since those string representations of large numbers may not
+ be convertible to integers using <b>atoi()</b> or similar
+ functions. </li>
+
+<li> The subroutines in SNMP_Session never call <b>die()</b> anymore
+ if it encouters error situations. Instead, they issue a warning and
+ return <b>undef</b>. <a href="mailto:btr at iol.unh.edu">Brad
+ Ritchie</a> managed to convince me that library code should never
+ <b>die</b>. Unfortunately I haven't revised <b>BER.pm</b> yet, so
+ subroutines related with BER transfer syntax encoding and decoding
+ may still <b>die</b>. </li>
+
+<li> The code has been cleaned up to use more of the standard
+ functionality of the Perl 5 <b>Socket.pm</b> module. That should
+ have eliminated some potential portability problems (and delegated
+ responsability for potential bugs :-). Note that this means that the
+ code requires Perl 5.002 or later. </li>
+
+<li> Both source files now make extensive use of <b>strict</b> for
+ better compile-time error checking. Please notify me in case you
+ have any problems because of this. </li>
+
+<li> The library now attempts to retransmit queries for which no
+ reponse has been received during a given time. The default
+ parameters for the retransmission logic have been discussed at length
+ in the <em>mrtg</em> mailing list, and seem to work quite well, both
+ against overloaded routers that simply drop some SNMP requests and
+ against routers that are behind slow or lossy links. If you have
+ feedback on the default parameters, please drop me an e-mail. </li>
+
+<li> When I implemented the retransmission logic, I also fixed
+ handling of request IDs. In older versions, the request ID was never
+ changed between reqeuest, which could lead to (late) resposes being
+ associated with the wrong request. Now the request ID is incremented
+ for each request, and mismatching responses are ignored. For
+ retransmissions, the request ID isn't changed. If we did change it,
+ we could estimate response time and implement an adaptive
+ retransmission algorithm. This has been left for further
+ study. </li>
+
+<li> Added code contributed by <em>mrtg</em> users:
+
+ <ul>
+
+ <li> Encoding of larger subids, by <a
+ href="mailto:sip00 at vg.swissptt.ch">Philippe Simonet</a> and <a
+ href="mailto:yhu at casc.com">Yufang HU</a> </li>
+
+ <li> Decoding <b>sysUpTime</b>, by <a
+ href="mailto:dlr at Bungi.com">Dave Rand</a> </li>
+
+ <li> Decoding longer (unsigned) integers, by <a
+ href="mailto:tobi at oetiker.ch">Tobias Oetiker</a> </li>
+
+ <li> Decoding longer strings, by <a
+ href="mailto:san at iem.pw.edu.pl">Andrzej Tobola</a> </li>
+
+ <li> More reasonable socket initialization, by <a
+ href="mailto:peters at dkrz.de">Heine Peters</a> </li>
+
+ <li> Correct integer BER-encoding, by <a
+ href="mailto:mcm at unx.sas.com">Mike Mitchell</a> </li>
+
+ </ul> </li>
+
+</ul>
+
+
+<HR>
+<ADDRESS>
+<!-- hhmts start -->20091228
+<!-- hhmts end -->
+<A HREF="http://www.switch.ch/misc/leinen/">
+ Simon Leinen <simon.leinen at switch.ch></A>
+
+<A HREF="http://validator.w3.org/"><IMG ALIGN=RIGHT BORDER=0
+ SRC="../../images/vh40.gif"
+ ALT="Valid HTML 4.0!" HEIGHT=31 WIDTH=88></A>
+
+</ADDRESS>
+
+</BODY>
+</HTML>
diff --git a/faq.html b/faq.html
new file mode 100644
index 0000000..9160dcd
--- /dev/null
+++ b/faq.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html> <head>
+<title>Frequently Asked Questions (FAQ) on SNMP_Session.pm</title>
+</head>
+
+<body bgcolor="#ffffff">
+<h1>Frequently Asked Questions (FAQ) on <tt>SNMP_Session.pm</tt></h1>
+
+<hr>
+
+<h2> <a name="snmpv3"></a>When will MRTG/<tt>SNMP_Session.pm</tt>
+support SNMPv3? </h2>
+
+<p> MRTG 2.13 and higher includes support for SNMPv3, using the
+<tt>Net::SNMP</tt> library. See the sections about <a
+href="http://oss.oetiker.ch/mrtg/doc/mrtg-reference.en.html#enablesnmpv3"><tt>EnableSnmpV3</tt></a>,
+<a
+href="http://oss.oetiker.ch/mrtg/doc/mrtg-reference.en.html#target"><tt>Target</tt></a>,
+and <a
+href="http://oss.oetiker.ch/mrtg/doc/mrtg-reference.en.html#snmpoptions"><tt>SnmpOptions</tt></a>,
+in the MRTG Reference Manual. </p>
+
+<p> <tt>SNMP_Session.pm</tt> only supports SNMPv1 and
+(community-based) SNMPv2 ("SNMPv2c"). There are no plans to add
+SNMPv3 support to <tt>SNMP_Session.pm</tt>. I'd recommend looking at
+<a href="http://search.cpan.org/dist/Net-SNMP/">Net::SNMP</a> instead,
+which has been supporting SNMPv3 for quite a while. <a
+href="http://search.cpan.org/dist/Net-SNMP/">Net::SNMP</a> is also
+written much cleaner than <tt>SNMP_Session.pm</tt>, and like
+<tt>SNMP_Session.pm</tt> doesn't require any external binaries. Some
+non-standard (crypto) Perl modules might be required to use SNMP's
+security features, however. </p>
+
+<p> A nice project would be to write a variant of
+<tt>SNMP_util.pm</tt> that sits on top of <a
+href="http://search.cpan.org/dist/Net-SNMP/">Net::SNMP</a> rather than
+on top of <tt>SNMP_Session.pm</tt>. Since <a
+href="http://www.mrtg.org/">MRTG</a> uses the <tt>SNMP_util.pm</tt>
+layer, that should make it work with SNMPv3. </p>
+
+<p> However, since SNMPv3 doesn't have communities, MRTG's target
+syntax would have to be changed to accomodate SNMPv3 targets. The
+current syntax of <samp>community{at}host[:options]</samp> would have
+to be modified (I think it should be changed, rather than further
+extended, because it already looks too ugly because of its multiple
+extensions. </p>
+
+<p> For the target syntax, one should probably look at the SNMP URI
+syntax (<a
+href="ftp://ftp.rfc-editor.org/in-notes/rfc4088.txt"><tt>RFC 4088</tt></a>. </p>
+
+<p> Anther issue with SNMPv3 is that it uses engine IDs, which are
+usually "discovered" using a well-defined discovery mechanism. It
+would be good to cache discovered engine IDs somewhere, to avoid
+repeating the discovery process for every round of MRTG. If MRTG is
+run from cron every five minutes, then such an engine ID cache should
+be written to disk between runs. I'm not sure whether Net::SNMP
+already supports this. If MRTG is run as a long-running "daemon"
+process, making the cache persistent might not be necessary. </p>
+
+<hr>
+<!-- hhmts start -->
+2007/10/13 19:15:00
+<!-- hhmts end -->
+<address>
+
+ <a href="http://www.switch.ch/misc/leinen/">
+ Simon Leinen <simon.leinen at switch.ch>
+ </a>
+</address>
+
+</body> </html>
diff --git a/index.html b/index.html
index 24fb5cd..5ed8cfd 100644
--- a/index.html
+++ b/index.html
@@ -7,7 +7,7 @@
<DIV ALIGN=CENTER>
<H1>SNMP support for Perl 5</H1>
- <p> Copyright (c) 1995-2008, Simon Leinen<br>
+ <p> Copyright (c) 1995-2009, Simon Leinen<br>
All rights reserved </p>
<em> This program is free software; you can redistribute it under
@@ -22,11 +22,10 @@ HREF="mailto:simon.leinen at switch.ch">simon.leinen at switch.ch</A>></p>
<p> This package contains Perl 5 modules <tt>SNMP_Session.pm</tt>,
<tt>BER.pm</tt>, and <tt>SNMP_util.pm</tt> which, when used together,
-provide rudimentary access to remote <a
-href="http://www.switch.ch/misc/leinen/snmp/">SNMP</a> (v1/v2)
+provide rudimentary access to remote SNMP (v1/v2)
agents. </p>
-<p> <b> Download it from <a href="http://www.switch.ch/misc/leinen/snmp/perl/dist/"><tt>http://www.switch.ch/misc/leinen/snmp/perl/dist/</tt></a></b> </p>
+<p> <b> Download it from <a href="http://code.google.com/p/snmp-session/downloads/"><tt>http://code.google.com/p/snmp-session/downloads/</tt></a></b> </p>
<!-- <iframe marginwidth="0" marginheight="0" width="120" height="240" scrolling="no" frameborder="0" src="http://rcm.amazon.com/e/cm?o=1&l=as1&f=ifr&t=simonleinensbook&p=8&asins=0596000200&IS2=1<1=_blank"><MAP NAME="boxmap-p8"><AREA SHAPE="RECT" COORDS="14, 200, 103, 207" HREF="http://rcm.amazon.com/e/cm/privacy-policy.html?o=1" ><AREA COORDS="0,0,10000,10000" HREF="http://www.amazon.com/exec/obidos/redirect-home/simonleinensbook" ></map><img src="http://rcm-images.amaz [...]
@@ -76,301 +75,10 @@ remainder of this page desribes the low-level API in
href="changes.html">changes.html</a></samp> file packaged with this
system. </p>
-<H2>Usage</H2>
+<h2> Usage </h2>
-<P> The basic usage of these routines works like this: </P>
-
-<PRE>
-use BER;
-require 'SNMP_Session.pm';
-
-# Set $host to the name of the host whose SNMP agent you want
-# to talk to. Set $community to the community name under
-# which you want to talk to the agent. Set port to the UDP
-# port on which the agent listens (usually 161).
-
-$session = SNMP_Session->open ($host, $community, $port)
- or die "couldn't open SNMP session to $host";
-
-# Set $oid1, $oid2... to the BER-encoded OIDs of the MIB
-# variables you want to get.
-
-if ($session->get_request_response ($oid1, $oid2, ...)) {
- ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
-
- while ($bindings ne '') {
- ($binding,$bindings) = &decode_sequence ($bindings);
- ($oid,$value) = &decode_by_template ($binding, "%O%@");
- print &pretty_print ($oid)," => ", &pretty_print ($value), "\n";
- }
-} else {
- die "No response from agent on $host";
-}
-</PRE>
-
-<H2>Encoding OIDs</H2>
-
-<P> In order to BER-encode OIDs, you can use the function
-<B>BER::encode_oid</B>. It takes (a vector of) numeric subids as an
-argument. For example, </P>
-
-<PRE>
-use BER;
-encode_oid (1, 3, 6, 1, 2, 1, 1, 1, 0)
-</PRE>
-
-<P> will return the BER-encoded OID for the <B>sysDescr.0</B>
- (1.3.6.1.2.1.1.1.0) instance of <A
- HREF="ftp://ftp.isi.edu/in-notes/rfc1213.txt">MIB-2</A>. </P>
-
-<H2>Decoding the results</H2>
-
-<P> When get_request_response returns success, you must decode the
- response PDU from the remote agent. The function
- <B>decode_get_response</B> can be used to do this. It takes a
- get-response PDU, checks its syntax and returns the <EM>bindings</EM>
- part of the PDU. This is where the remote agent actually returns the
- values of the variables in your query. </P>
-
-<p> You should iterate over the individual bindings in this
- <EM>bindings</EM> part and extract the value for each variable. In
- the example above, the returned bindings are simply printed using the
- <B>BER::pretty_print</B> function. </p>
-
-<p> For better readability of the OIDs, you can also use the following
- idiom, where the <samp>%pretty_oids</samp> hash maps BER-encoded
- numerical OIDs to symbolic OIDs. Note that this simple-minded
- mapping only works for response OIDs that exactly match known OIDs,
- so it's unsuitable for table walking (where the response OIDs include
- an additional row index). </p>
-
-<pre>
-<font color="#c00000">%ugly_oids = qw(sysDescr.0 1.3.6.1.2.1.1.1.0
- sysContact.0 1.3.6.1.2.1.1.4.0);
-foreach (keys %ugly_oids) {
- $ugly_oids{$_} = encode_oid (split (/\./, $ugly_oids{$_}));
- $pretty_oids{$ugly_oids{$_}} = $_;
-}</font>
-...
-if ($session->get_request_response ($ugly_oids{'sysDescr.0'},
- $ugly_oids{'sysContact.0'})) {
- ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
- while ($bindings ne '') {
- ($binding,$bindings) = &decode_sequence ($bindings);
- ($oid,$value) = &decode_by_template ($binding, "%O%@");
- print <font color="#c00000">$pretty_oids{$oid},</font>" => ",
- &pretty_print ($value), "\n";
- }
-} ...
-</pre>
-
-<H2><a name="set-req-use">Set Requests</a></H2>
-
-<p> Set requests are generated much like get or getNext requests are,
- with the exception that you have to specify not just OIDs, but also
- the values the variables should be set to. Every binding is passed
- as a reference to a two-element array, the first element being the
- encoded OID and the second one the encoded value. See the
- <tt>test/set-test.pl</tt> script for an example, in particular the
- subroutine <tt>snmpset</tt>. </p>
-
-<H2><a name="map-table-use">Walking Tables</a></H2>
-
-<p> Beginning with version 0.57 of <tt>SNMP_Session.pm</tt>, there is API
- support for walking tables. The <tt>map_table</tt> method can be
- used for this as follows: </p>
-
-<pre>
-sub walk_function ($$$) {
- my ($index, $val1, $val3) = @_;
- ...
-}
-
-...
-$columns = [$base_oid1, $base_oid3];
-$n_rows = $session->map_table ($columns, \&walk_function);
-</pre>
-
-<p> The <em>columns</em> argument must be a reference to a list of
- OIDs for table columns sharing the same index. The method will
- traverse the table and call the <em>walk_function</em> for each row.
- The arguments for these calls will be:
-
-<ol>
-
-<li> the <em>row index</em> as a partial OID in dotted notation,
- e.g. "1.3", or "10.0.1.34".
-
-<li> the values of the requested table columns in that row, in
- BER-encoded form. If you want to use the standard
- <tt>pretty_print</tt> subroutine to decode the values, you can use
- the following idiom:
-
-<pre>
- grep (defined $_ && ($_=pretty_print $_), ($val1, $val3));
-</pre>
-
-</ol>
-
-<H3><a name="map-table-4-use">Walking Tables With
-<tt>get-bulk</tt></a></H3>
-
-<p> Since version 0.67, <tt>SNMP_Session</tt> uses a different
-<tt>get_table</tt> implementation for <tt>SNMPv2c_Session</tt>s. This
-version uses the ``powerful <tt>get-bulk</tt> operator'' to retrieve
-many table rows with each request. In general, this will make table
-walking much faster under SNMPv2c, especially when round-trip times to
-the agent are long. </p>
-
-<p> There is one difficulty, however: With <tt>get-bulk</tt>, a
-management application can specify the maximum number of rows to
-return in a single response. <tt>SNMP_Session.pm</tt> provides a new
-function, <tt>map_table_4</tt>, in which this <tt>maxRepetitions</tt>
-value can be specified explicitly. </p>
-
-<p> For maximum efficiency, it should be set to a value that is one
-greater than the number of rows in the table. If it is smaller, then
-<tt>map_table</tt> will use more request/response cycles than
-necessary; if it is bigger, the agent will have to compute variable
-bindings beyond the end of the table (which <tt>map_table</tt> will
-throw away). </p>
-
-<p> Of course it is usually impossible to know the size of the table
-in advance. If you don't specify <tt>maxRepetitions</tt> when walking
-a table, then <tt>map_table</tt> will use a per-session default
-(<tt>$session->default_max_repetitions</tt>). The default value for
-this default is 12. </p>
-
-<p> If you walk a table multiple times, and the size of the table is
-relatively stable, you should use the return value of
-<tt>map_table</tt> (which is the number of rows it has encountered) to
-compute the next value of <tt>maxRepetitions</tt>. Remember to add
-one so that <tt>map_table</tt> notices when the table is finished!
-</p>
-
-<p> Note that for really big tables, this doesn't make a big
-difference, since the table won't fit in a single response packet
-anyway. </p>
-
-<H2><a name="trap-send">Sending Traps</a></H2>
-
-<p> To send a trap, you have to open an SNMP session to the trap
- receiver. Usually this is a process listening to UDP port 162 on a
- network management station. Then you can use the
- <tt>trap_request_send</tt> method to encode and send SNMPv1 traps.
- There is no way to find out whether the trap was actually received at
- the management station - SNMP traps are fundamentally unreliable.
-</p>
-
-<p> When constructing an SNMPv1 trap, you must provide
-
-<ul>
-
-<li> the "enterprise" Object Identifier for the entity that generates
- the trap
-
-<li> your IP address
-
-<li> the generic trap type
-
-<li> the specific trap type
-
-<li> the sysUpTime at the time of trap generation
-
-<li> a sequence (may be empty) of variable bindings further describing
- the trap.
-
-</ul>
-
-<p> For SNMPv2 traps, you need:
-
-<ul>
-
-<li> the trap's OID
-
-<li> the sysUpTime at the time of trap generation
-
-<li> the bindings list as above
-
-</ul>
-
-<p> For SNMPv2 traps, the uptime and trap OID are encoded as bindings
-which are added to the front of the other bindings you provide. </p>
-
-<p> Here is a short example: </p>
-
-<pre>
-my $trap_receiver = "netman.noc";
-my $trap_community = "SNMP_Traps";
-my $trap_session = $version eq '1'
- ? SNMP_Session->open ($trap_receiver, $trap_community, 162)
- : SNMPv2c_Session->open ($trap_receiver, $trap_community, 162);
-my $myIpAddress = ...;
-my $start_time = time;
-
-...
-
-sub link_down_trap ($$) {
- my ($if_index, $version) = @_;
- my $genericTrap = 2; # linkDown
- my $specificTrap = 0;
- my @ifIndexOID = ( 1,3,6,1,2,1,2,2,1,1 );
- my $upTime = int ((time - $start_time) * 100.0);
- my @myOID = ( 1,3,6,1,4,1,2946,0,8,15 );
-
- warn "Sending trap failed"
- unless ($version eq '1')
- ? $trap_session->trap_request_send (encode_oid (@myOID),
- encode_ip_address ($myIpAddress),
- encode_int ($genericTrap),
- encode_int ($specificTrap),
- encode_timeticks ($upTime),
- [encode_oid (@ifIndex_OID,$if_index),
- encode_int ($if_index)],
- [encode_oid (@ifDescr_OID,$if_index),
- encode_string ("foo")])
- : $trap_session->v2_trap_request_send (\@linkDown_OID, $upTime,
- [encode_oid (@ifIndex_OID,$if_index),
- encode_int ($if_index)],
- [encode_oid (@ifDescr_OID,$if_index),
- encode_string ("foo")]);
-}
-</pre>
-
-<H2><a name="trap-recv">Receiving Traps</a></H2>
-
-<p> Since version 0.60, SNMP_Session.pm supports the receipt and
- decoding of SNMPv1 trap requests. Since version 0.75, SNMPv2 Trap
- PDUs are also recognized. </p>
-
-<p> To receive traps, you have to create a special SNMP session that
- passively listens on the SNMP trap transport address (usually UDP
- port 162). Then you can receive traps (actually, SNMPv1 traps,
- SNMPv2 traps, and SNMPv2 informs) using the <tt>receive_trap</tt>
- method and decode them using <tt>decode_trap_request</tt>. The
- <em>enterprise</em>, <em>agent</em>, <em>generic</em>,
- <em>specific</em> and <em>sysUptime</em> return values are only
- defined for SNMPv1 traps. In SNMPv2 traps and informs, the
- equivalent information is contained in the bindings.
-</p>
-
-<pre>
-my $trap_session = SNMPv1_Session->open_trap_session ()
- or die "cannot open trap session";
-my ($trap, $sender_addr, $sender_port) = $trap_session->receive_trap ()
- or die "cannot receive trap";
-my ($community, $enterprise, $agent,
- $generic, $specific, $sysUptime, $bindings)
- = $trap_session->decode_trap_request ($trap)
- or die "cannot decode trap received"
-...
-my ($binding, $oid, $value);
-while ($bindings ne '') {
- ($binding,$bindings) = &decode_sequence ($bindings);
- ($oid, $value) = decode_by_template ($binding, "%O%@");
- print BER::pretty_oid ($oid)," => ",pretty_print ($value),"\n";
-}
-</pre>
+See the <b>EXAMPLES</b> section in the POD documentation
+of <tt>SNMP_Session.pm</tt>.
<H2>Future Plans</H2>
@@ -392,8 +100,7 @@ while ($bindings ne '') {
<HR>
<ADDRESS>
-<!-- hhmts start -->
-20080402
+<!-- hhmts start -->20091228
<!-- hhmts end -->
<A HREF="http://www.switch.ch/misc/leinen/">
Simon Leinen <simon.leinen at switch.ch></A>
diff --git a/lib/BER.pm b/lib/BER.pm
index 739b48d..5298735 100644
--- a/lib/BER.pm
+++ b/lib/BER.pm
@@ -2,7 +2,7 @@
######################################################################
### BER (Basic Encoding Rules) encoding and decoding.
######################################################################
-### Copyright (c) 1995-2008, Simon Leinen.
+### Copyright (c) 1995-2009, Simon Leinen.
###
### This program is free software; you can redistribute it under the
### "Artistic License 2.0" included in this distribution
@@ -12,27 +12,32 @@
### structures using the Basic Encoding Rules (BER). Only the subset
### necessary for SNMP is implemented.
######################################################################
-### Created by: Simon Leinen <simon at switch.ch>
-###
-### Contributions and fixes by:
-###
-### Andrzej Tobola <san at iem.pw.edu.pl>: Added long String decode
-### Tobias Oetiker <tobi at oetiker.ch>: Added 5 Byte Integer decode ...
-### Dave Rand <dlr at Bungi.com>: Added SysUpTime decode
-### Philippe Simonet <sip00 at vg.swissptt.ch>: Support larger subids
-### Yufang HU <yhu at casc.com>: Support even larger subids
-### Mike Mitchell <Mike.Mitchell at sas.com>: New generalized encode_int()
-### Mike Diehn <mdiehn at mindspring.net>: encode_ip_address()
-### Rik Hoorelbeke <rik.hoorelbeke at pandora.be>: encode_oid() fix
-### Brett T Warden <wardenb at eluminant.com>: pretty UInteger32
-### Bert Driehuis <driehuis at playbeing.org>: Handle SNMPv2 exception codes
-### Jakob Ilves (/IlvJa) <jakob.ilves at oracle.com>: PDU decoding
-### Jan Kasprzak <kas at informatics.muni.cz>: Fix for PDU syntax check
-### Milen Pavlov <milen at batmbg.com>: Recognize variant length for ints
+### Create by: See AUTHORS below
######################################################################
package BER;
+=head1 NAME
+
+BER
+
+=head1 SYNOPSIS
+
+ use BER;
+ $encoded = encode_sequence (encode_int (123), encode_string ("foo"));
+ ($i, $s) = decode_by_template ($encoded, "%{%i%s");
+ # $i will now be 123, $s the string "foo".
+
+=head1 DESCRIPTION
+
+This is a simple library to encode and decode data using the Basic
+Encoding Rules (BER) of Abstract Syntax Notation One (ASN.1). It does
+not claim to be a complete implementation of the standard, but
+implements enough of the BER standard to encode and decode SNMP
+messages.
+
+=cut
+
require 5.002;
use strict;
@@ -40,7 +45,7 @@ use vars qw(@ISA @EXPORT $VERSION $pretty_print_timeticks
%pretty_printer %default_printer $errmsg);
use Exporter;
-$VERSION = '1.05';
+$VERSION = '1.14';
@ISA = qw(Exporter);
@@ -56,17 +61,31 @@ $VERSION = '1.05';
encoded_oid_prefix_p errmsg
register_pretty_printer unregister_pretty_printer);
-### Variables
+=head1 VARIABLES
+
+=cut
+
+=head2 $pretty_print_timeticks (default: 1)
+
+If non-zero (the default), C<pretty_print> will convert TimeTicks to
+"human readable" strings containing days, hours, minutes and seconds.
+
+If the variable is zero, C<pretty_print> will simply return an
+unsigned integer representing hundredths of seconds. If you prefer
+this, bind C<$pretty_print_timeticks> to zero.
+
+=cut
-## Bind this to zero if you want to avoid that TimeTicks are converted
-## into "human readable" strings containing days, hours, minutes and
-## seconds.
-##
-## If the variable is zero, pretty_print will simply return an
-## unsigned integer representing hundredths of seconds.
-##
$pretty_print_timeticks = 1;
+=head2 $errmsg - error message from last failed operation.
+
+When they encounter errors, the routines in this module will generally
+return C<undef>) and leave an informative error message in
+C<$errmsg>).
+
+=cut
+
### Prototypes
sub encode_header ($$);
sub encode_int_0 ();
@@ -110,6 +129,9 @@ sub template_error ($$$);
sub version () { $VERSION; }
+=head1 METHODS
+=cut
+
### Flags for different types of tags
sub universal_flag { 0x00 }
@@ -173,36 +195,74 @@ BEGIN {
sub encode_header ($$) {
my ($type,$length) = @_;
- return pack ("C C", $type, $length) if $length < 128;
- return pack ("C C C", $type, long_length | 1, $length) if $length < 256;
- return pack ("C C n", $type, long_length | 2, $length) if $length < 65536;
+ return pack ("CC", $type, $length) if $length < 128;
+ return pack ("CCC", $type, long_length | 1, $length) if $length < 256;
+ return pack ("CCn", $type, long_length | 2, $length) if $length < 65536;
return error ("Cannot encode length $length yet");
}
+=head2 encode_int_0() - encode the integer 0.
+
+This is functionally identical to C<encode_int(0)>.
+
+=cut
+
sub encode_int_0 () {
- return pack ("C C C", 2, 1, 0);
+ return pack ("CCC", 2, 1, 0);
}
+=head2 encode_int() - encode an integer using the generic
+ "integer" type tag.
+
+=cut
+
sub encode_int ($) {
return encode_intlike ($_[0], int_tag);
}
+=head2 encode_uinteger32() - encode an integer using the SNMP
+ UInteger32 tag.
+
+=cut
+
sub encode_uinteger32 ($) {
return encode_intlike ($_[0], snmp_uinteger32_tag);
}
+=head2 encode_counter32() - encode an integer using the SNMP
+ Counter32 tag.
+
+=cut
+
sub encode_counter32 ($) {
return encode_intlike ($_[0], snmp_counter32_tag);
}
+=head2 encode_counter64() - encode an integer using the SNMP
+ Counter64 tag.
+
+=cut
+
sub encode_counter64 ($) {
return encode_intlike ($_[0], snmp_counter64_tag);
}
+=head2 encode_gauge32() - encode an integer using the SNMP Gauge32
+ tag.
+
+=cut
+
sub encode_gauge32 ($) {
return encode_intlike ($_[0], snmp_gauge32_tag);
}
+### encode_intlike ($int, $tag)
+###
+### Generic function to BER-encode an arbitrary integer using a given
+### tag. This function can handle large integers. It doesn't check
+### whether the integer is in a suitable range for the given type tag
+### - that is expected to be done by the caller.
+###
sub encode_intlike ($$) {
my ($int, $tag)=@_;
my ($sign, $val, @vals);
@@ -226,6 +286,12 @@ sub encode_intlike ($$) {
}
}
+=head2 encode_oid() - encode an object ID, passed as a list of
+ sub-IDs.
+
+ $encoded = encode_oid (1,3,6,1,...);
+=cut
+
sub encode_oid (@) {
my @oid = @_;
my ($result,$subid);
@@ -284,7 +350,27 @@ sub encode_oid (@) {
encode_header (object_id_tag, length $result).$result;
}
+=head2 encode_null() - encode a null object.
+
+This is used e.g. in binding lists for variables that don't have a
+value (yet)
+
+=cut
+
sub encode_null () { encode_header (null_tag, 0); }
+
+=head2 encode_sequence()
+
+=head2 encode_tagged_sequence()
+
+ $encoded = encode_sequence (encoded1, encoded2, ...);
+ $encoded = encode_tagged_sequence (tag, encoded1, encoded2, ...);
+
+Take already encoded values, and extend them to an encoded sequence.
+C<encoded_sequence> uses the generic sequence tag, while with
+C<encode_tagged_sequence> you can specify your own tag.
+=cut
+
sub encode_sequence (@) { encode_tagged_sequence (sequence_tag, @_); }
sub encode_tagged_sequence ($@) {
@@ -295,11 +381,22 @@ sub encode_tagged_sequence ($@) {
return encode_header ($tag | constructor_flag, length $result).$result;
}
+=head2 encode_string() - encode a Perl string as an OCTET STRING.
+=cut
+
sub encode_string ($) {
my ($string)=@_;
return encode_header (octet_string_tag, length $string).$string;
}
+=head2 encode_ip_address() - encode an IPv4 address.
+
+This can either be passed as a four-octet sequence in B<network byte
+order>, or as a text string in dotted-quad notation,
+e.g. "192.0.2.234".
+
+=cut
+
sub encode_ip_address ($) {
my ($addr)=@_;
my @octets;
@@ -315,6 +412,14 @@ sub encode_ip_address ($) {
}
}
+=head2 encode_timeticks() - encode an integer as a C<TimeTicks>
+ object.
+
+The integer should count hundredths of a second since the epoch
+defined by C<sysUpTime.0>.
+
+=cut
+
sub encode_timeticks ($) {
my ($tt) = @_;
return encode_intlike ($tt, snmp_timeticks_tag);
@@ -322,13 +427,22 @@ sub encode_timeticks ($) {
#### Decoding
+=head2 pretty_print() - convert an encoded byte sequence into
+ human-readable form.
+
+This function can be extended by registering pretty-printing methods
+for specific type codes. Most BER type codes used in SNMP already
+have such methods pre-registered by default. See
+C<register_pretty_printer> for how new methods can be added.
+
+=cut
+
sub pretty_print ($) {
my ($packet) = @_;
return undef unless defined $packet;
my $result = ord (substr ($packet, 0, 1));
if (exists ($pretty_printer{$result})) {
- my $c_ref = $pretty_printer{$result};
- return &$c_ref ($packet);
+ return &{$pretty_printer{$result}} ($packet);
}
return ($pretty_print_timeticks
? pretty_uptime ($packet)
@@ -524,12 +638,21 @@ sub pretty_generic_sequence ($) {
$first_elem = '' if $first_elem;
}
return $pretty_result;
-}
+}
+
+=head2 hex_string() - convert OCTET STRING to hexadecimal notation.
+
+=cut
sub hex_string ($) {
- &hex_string_of_type ($_[0], octet_string_tag);
+ hex_string_of_type ($_[0], octet_string_tag);
}
+=head2 hex_string_of_type() - convert octet string to hex, and check
+type against given tag.
+
+=cut
+
sub hex_string_of_type ($$) {
my ($pdu, $wanted_type) = @_;
my ($length);
@@ -582,6 +705,44 @@ sub decode_generic_tlv ($) {
@result;
}
+=head2 decode_by_template() - decode complex object according to a
+ template.
+
+ ($var1, ...) = decode_by_template ($pdu, $template, ...);
+
+The template can contain various %X directives. Some directives
+consume additional arguments following the template itself. Most
+directives will cause values to be returned. The values are returned
+as a sequence in the order of the directives that generated them.
+
+=over 4
+
+=item %{ - decode sequence.
+
+This doesn't assign any return value, just checks and skips the
+tag/length fields of the sequence. By default, the tag should be the
+generic sequence tag, but a tag can also be specified in the
+directive. The directive can either specify the tag as a prefix,
+e.g. C<%99{> will require a sequence tag of 99, or if the directive is
+given as C<%*{>, the tag will be taken from the next argument.
+
+=item %s - decode string
+
+=item %i - decode integer
+
+=item %u - decode unsigned integer
+
+=item %O - decode Object ID (OID)
+
+=item %A - decode IPv4 address
+
+=item %@ - assigns the remaining undecoded part of the PDU to the next
+ return value.
+
+=back
+
+=cut
+
sub decode_by_template {
my ($pdu) = shift;
local ($_) = shift;
@@ -606,7 +767,7 @@ sub decode_by_template_2 {
if $template_debug;
$_ = substr ($_,1);
++$template_index;
- if (($expected) = /^(\d*|\*)\{(.*)/) {
+ if (($expected) = /^(\d+|\*|)\{(.*)/) {
## %{
$template_index += length ($expected) + 1;
print STDERR "%{\n" if $template_debug;
@@ -622,8 +783,8 @@ sub decode_by_template_2 {
$template,
$template_index)
unless (ord (substr ($pdu, 0, 1)) == $expected);
- (($length,$pdu) = decode_length ($pdu, 1))
- || return template_error ("cannot read length",
+ ($length,$pdu) = decode_length ($pdu, 1)
+ or return template_error ("cannot read length",
$template, $template_index);
return template_error ("Expected length $length, got ".length $pdu ,
$template, $template_index)
@@ -632,8 +793,8 @@ sub decode_by_template_2 {
## %s
$template_index += length ($expected) + 1;
($expected = shift) if $expected eq '*';
- (($read,$pdu) = decode_string ($pdu))
- || return template_error ("cannot read string",
+ ($read, $pdu) = decode_string ($pdu)
+ or return template_error ("cannot read string",
$template, $template_index);
print STDERR "%s => $read\n" if $template_debug;
if ($expected eq '') {
@@ -668,19 +829,19 @@ sub decode_by_template_2 {
## %O
$template_index += 1;
$_ = $1;
- (($read,$pdu) = decode_oid ($pdu))
- || return template_error ("cannot read OID",
- $template, $template_index);
+ ($read,$pdu) = decode_oid ($pdu)
+ or return template_error ("cannot read OID",
+ $template, $template_index);
print STDERR "%O => ".pretty_oid ($read)."\n"
if $template_debug;
push @results, $read;
- } elsif (($expected,$rest) = /^(\d*|\*|)i(.*)/) {
+ } elsif (($expected,$rest) = /^(\d+|\*|)i(.*)/) {
## %i
$template_index += length ($expected) + 1;
print STDERR "%i\n" if $template_debug;
$_ = $rest;
- (($read,$pdu) = decode_int ($pdu))
- || return template_error ("cannot read int",
+ ($read, $pdu) = decode_int ($pdu)
+ or return template_error ("cannot read int",
$template, $template_index);
if ($expected eq '') {
push @results, $read;
@@ -696,8 +857,8 @@ sub decode_by_template_2 {
$template_index += 1;
print STDERR "%u\n" if $template_debug;
$_ = $rest;
- (($read,$pdu) = decode_unsignedlike ($pdu))
- || return template_error ("cannot read uptime",
+ ($read, $pdu) = decode_unsignedlike ($pdu)
+ or return template_error ("cannot read uptime",
$template, $template_index);
push @results, $read;
} elsif (/^\@(.*)/) {
@@ -727,6 +888,16 @@ sub decode_by_template_2 {
@results;
}
+=head2 decode_sequence() - Split sequence into components.
+
+ ($first, $rest) = decode_sequence ($pdu);
+
+Checks whether the PDU has a sequence type tag and a plausible length
+field. Splits the initial element off the list, and returns both this
+and the remainder of the PDU.
+
+=cut
+
sub decode_sequence ($) {
my ($pdu) = @_;
my ($result);
@@ -809,9 +980,13 @@ sub decode_length ($@) {
@result;
}
-# This takes a hashref that specifies functions to call when
-# the specified value type is being printed. It returns the
-# number of functions that were registered.
+=head2 register_pretty_printer() - register pretty-printing methods for
+ typecodes.
+
+This function takes a hashref that specifies functions to call when
+the specified value type is being printed. It returns the number of
+functions that were registered.
+=cut
sub register_pretty_printer($)
{
my ($h_ref) = shift;
@@ -911,3 +1086,48 @@ sub template_error ($$$) {
}
1;
+
+=head1 AUTHORS
+
+Created by: Simon Leinen E<lt>simon.leinen at switch.chE<gt>
+
+Contributions and fixes by:
+
+=over
+
+=item Andrzej Tobola E<lt>san at iem.pw.edu.plE<gt>: Added long String decode
+
+=item Tobias Oetiker E<lt>tobi at oetiker.chE<gt>: Added 5 Byte Integer decode ...
+
+=item Dave Rand E<lt>dlr at Bungi.comE<gt>: Added C<SysUpTime> decode
+
+=item Philippe Simonet E<lt>sip00 at vg.swissptt.chE<gt>: Support larger subids
+
+=item Yufang HU E<lt>yhu at casc.comE<gt>: Support even larger subids
+
+=item Mike Mitchell E<lt>Mike.Mitchell at sas.comE<gt>: New generalized C<encode_int()>
+
+=item Mike Diehn E<lt>mdiehn at mindspring.netE<gt>: C<encode_ip_address()>
+
+=item Rik Hoorelbeke E<lt>rik.hoorelbeke at pandora.beE<gt>: C<encode_oid()> fix
+
+=item Brett T Warden E<lt>wardenb at eluminant.comE<gt>: pretty C<UInteger32>
+
+=item Bert Driehuis E<lt>driehuis at playbeing.orgE<gt>: Handle SNMPv2 exception codes
+
+=item Jakob Ilves (/IlvJa) E<lt>jakob.ilves at oracle.comE<gt>: PDU decoding
+
+=item Jan Kasprzak E<lt>kas at informatics.muni.czE<gt>: Fix for PDU syntax check
+
+=item Milen Pavlov E<lt>milen at batmbg.comE<gt>: Recognize variant length for ints
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (c) 1995-2009, Simon Leinen.
+
+This program is free software; you can redistribute it under the
+"Artistic License 2.0" included in this distribution (file "Artistic").
+
+=cut
diff --git a/lib/SNMP_Session.pm b/lib/SNMP_Session.pm
index 4b9ccf9..9b624eb 100644
--- a/lib/SNMP_Session.pm
+++ b/lib/SNMP_Session.pm
@@ -2,47 +2,38 @@
######################################################################
### SNMP Request/Response Handling
######################################################################
-### Copyright (c) 1995-2008, Simon Leinen.
+### Copyright (c) 1995-2009, 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 <simon at switch.ch>
-###
-### Contributions and fixes by:
-###
-### Matthew Trunnell <matter at media.mit.edu>
-### Tobias Oetiker <tobi at oetiker.ch>
-### Heine Peters <peters at dkrz.de>
-### Daniel L. Needles <dan_needles at INS.COM>
-### Mike Mitchell <mcm at unx.sas.com>
-### Clinton Wong <clintdw at netcom.com>
-### Alan Nichols <Alan.Nichols at Ebay.Sun.COM>
-### Mike McCauley <mikem at open.com.au>
-### Andrew W. Elble <elble at icculus.nsg.nwu.edu>
-### Brett T Warden <wardenb at eluminant.com>: pretty UInteger32
-### Michael Deegan <michael at cnspc18.murdoch.edu.au>
-### Sergio Macedo <macedo at tmp.com.br>
-### Jakob Ilves (/IlvJa) <jakob.ilves at oracle.com>: PDU capture
-### Valerio Bontempi <v.bontempi at inwind.it>: IPv6 support
-### Lorenzo Colitti <lorenzo at colitti.com>: IPv6 support
-### Philippe Simonet <Philippe.Simonet at swisscom.com>: Export avoid...
-### Luc Pauwels <Luc.Pauwels at xalasys.com>: use_16bit_request_ids
-### Andrew Cornford-Matheson <andrew.matheson at corenetworks.com>: inform
-### Gerry Dalton <gerry.dalton at consolidated.com>: strict subs bug
-### Mike Fischer <mlf2 at tampabay.rr.com>: pass MSG_DONTWAIT to recv()
+### Created by: See AUTHORS below
######################################################################
-package SNMP_Session;
+package SNMP_Session;
+
+=head1 NAME
+
+SNMP_Session - SNMPv1/v2 Protocol Handling
+
+=head1 SYNOPSIS
+
+ use SNMP_Session;
+ $session = SNMP_Session->open ($host, $community, $port)
+ or die "couldn't open SNMP session to $host";
+ if ($session->get_request_response ($oid1, $oid2, ...)) {
+ ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print pretty_print ($oid)," => ", pretty_print ($value), "\n";
+ }
+ } else {
+ die "No response from agent on $host";
+ }
+
+=cut
require 5.002;
@@ -62,62 +53,65 @@ sub map_table_start_end ($$$$$$);
sub index_compare ($$);
sub oid_diff ($$);
-$VERSION = '1.13';
+$VERSION = '1.14';
@ISA = qw(Exporter);
@EXPORT = qw(errmsg suppress_warnings index_compare oid_diff recycle_socket ipv6available);
+=head1 VARIABLES
+
+The C<default_...> variables all specify default values that are used
+for C<SNMP_Session> objects when no other value is specified. These
+values can be overridden on a per-session basis, for example by
+passing additional arguments to the constructor.
+
+=cut
+
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).
+### Default values for the TIMEOUT, RETRIES, and BACKOFF slots of
+### SNMP_Session objects - see their documentation 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().
-###
+=head2 $default_max_repetitions - default value for C<maxRepetitions>.
+
+This specifies how many table rows are requested in C<getBulk>
+requests. Used when walking tables using C<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
+C<map_table_4()>.
+
+=cut
+
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.
-###
+=head2 $default avoid_negative_request_ids - default value for
+ C<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
+C<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.
+
+=cut
+
$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.
-###
+=head2 $default_use_16bit_request_ids - default value for C<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.
+
+=cut
+
$SNMP_Session::default_use_16bit_request_ids = 0;
### Whether all SNMP_Session objects should share a single UDP socket.
@@ -159,9 +153,38 @@ BEGIN {
###
my %the_socket = ();
+=head2 $errmsg - error message from last failed operation.
+
+When they encounter errors, the routines in this module will generally
+return C<undef>) and leave an informative error message in C<$errmsg>).
+
+=cut
+
$SNMP_Session::errmsg = '';
+
+=head2 $suppress_warnings - whether warnings should be suppressed.
+
+If this variable is zero, as is the default, this code will output
+informative error messages whenever it encounters an error. Set this
+to a non-zero value if you want to suppress these messages. In any
+case, the last error message can be found in C<$errmsg>.
+
+=cut
+
$SNMP_Session::suppress_warnings = 0;
+=head1 METHODS in package SNMP_Session
+
+The abstract class C<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: C<SNMPv1_Session> implements
+SNMPv1 (RFC 1157) functionality C<SNMPv2c_Session> implements
+community-based SNMPv2 (RFC 3410-3417).
+
+=cut
+
sub get_request { 0 | context_flag () };
sub getnext_request { 1 | context_flag () };
sub get_response { 2 | context_flag () };
@@ -173,14 +196,70 @@ sub trap2_request { 7 | context_flag () };
sub standard_udp_port { 161 };
+=head2 open() - create an SNMP session object
+
+ $session = SNMP_Session->open
+ ($host, $community, $port,
+ $max_pdu_len, $local_port, $max_repetitions,
+ $local_host, $ipv4only);
+
+The calling and return conventions are identical to
+C<SNMPv1_Session::open()>.
+
+=cut
+
sub open
{
return SNMPv1_Session::open (@_);
}
+=head2 timeout() - return timeout value.
+
+Initial timeout, in seconds, to wait for a response PDU after a
+request is sent. Note that when a request is retried, the timeout is
+increased by B<backoff> (see below). The standard value is 2.0
+(seconds).
+
+=cut
+
sub timeout { $_[0]->{timeout} }
+
+=head2 retries() - number of attempts to get a reply.
+
+Maximum number of attempts to get a reply for an SNMP request. If no
+response is received after B<timeout> seconds, the request is resent
+and a new response awaited with a longer timeout, see the
+documentation on B<backoff> below. The B<retries> value should be at
+least 1, because the first attempt counts, too (the name "retries" is
+confusing, sorry for that).
+
+=cut
+
sub retries { $_[0]->{retries} }
+
+=head2 backoff() - backoff factor.for timeout on successive retries.
+
+Default backoff factor for C<SNMP_Session> objects. This factor is
+used to increase the TIMEOUT every time an SNMP request is retried.
+The standard value is 1.0, which means the same timeout is used for
+all attempts.
+
+=cut
+
sub backoff { $_[0]->{backoff} }
+
+=head2 set_timeout() - set initial timeout for session
+
+=head2 set_retries() - set maximum number of attempts for session
+
+=head2 set_backoff() - set backoff factor for session
+
+Example usage:
+
+ $session->set_backoff (1.5);
+
+=cut
+
sub set_timeout {
my ($session, $timeout) = @_;
croak ("timeout ($timeout) must be a positive number") unless $timeout > 0.0;
@@ -282,38 +361,6 @@ sub decode_get_response {
@{$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;
@@ -323,24 +370,60 @@ sub wait_for_response {
select($rout=$rin,$wout=$win,$eout=$ein,$timeout);
}
+=head2 ..._request_response() - Send some request and receive response.
+
+Encodes a specific SNMP request, sends it to the destination address
+of the session, and waits for a matching response. If such a response
+is received, this function will return the size of the response, which
+is necessarily greater than zero.
+
+An undefined value is returned if some error happens during encoding
+or sending, or if no matching response is received after the
+wait/retry schedule is exhausted. See the documentation on the
+C<timeout()>, C<retries()>, and C<backoff()> methods on how the
+wait/retry logic works.
+
+=head2 get_request_response() - Send C<get> request and receive response.
+
+=head2 getnext_request_response() - Send C<get-next> request and receive response.
+
+ $result = $session->get_request_response (@encoded_oids);
+ $result = $session->getnext_request_response (@encoded_oids);
+
+=cut
+
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, at oids) = @_;
return $this->request_response_5 ($this->encode_getnext_request (@oids),
get_response, \@oids, 1);
}
+=head2 set_request_response() - Send C<set> request and receive response.
+
+ $result = $session->set_request_response (@encoded_pair_list);
+
+This method takes its arguments in a different form; they are a list
+of pairs - references to two-element arrays - which respresent the
+variables to be set and the intended values, e.g.
+
+ ([$encoded_oid_0, $encoded_value_0],
+ [$encoded_oid_1, $encoded_value_1],
+ [$encoded_oid_2, $encoded_value_2], ...)
+
+=cut
+
+sub set_request_response ($@) {
+ my($this, @pairs) = @_;
+ return $this->request_response_5 ($this->encode_set_request (@pairs),
+ get_response, \@pairs, 1);
+}
+
sub getbulk_request_response ($$$@) {
my($this,$non_repeaters,$max_repetitions, at oids) = @_;
return $this->request_response_5
@@ -348,6 +431,12 @@ sub getbulk_request_response ($$$@) {
get_response, \@oids, 1);
}
+=head2 trap_request_send() - send SNMPv1 Trap.
+
+ $result = $session->trap_request_send ($ent, $gent, $gen, $spec, $dt, @pairs);
+
+=cut
+
sub trap_request_send ($$$$$$@) {
my($this, $ent, $agent, $gen, $spec, $dt, @pairs) = @_;
my($req);
@@ -360,6 +449,12 @@ sub trap_request_send ($$$$$$@) {
return 1;
}
+=head2 v2_trap_request_send() - send SNMPv2 Trap.
+
+ $result = $session->v2_trap_request_send ($trap_oid, $dt, @pairs);
+
+=cut
+
sub v2_trap_request_send ($$$@) {
my($this, $trap_oid, $dt, @pairs) = @_;
my @sysUptime_OID = ( 1,3,6,1,2,1,1,3 );
@@ -434,15 +529,49 @@ sub request_response_5 ($$$$$) {
if (defined $this->{'capture_buffer'}
and ref $this->{'capture_buffer'} eq 'ARRAY');
#
- $this->error ("no response received");
+ return $this->error ("no response received");
}
+=head2 map_table() - traverse an SNMP table.
+
+ $result = $session->map_table ([$col0, $col1, ...], $mapfn);
+
+This will call the provided function (C<&$mapfn>) once for each row of
+the table defined by the column OIDs C<$col0>, C<$col1>... If the
+session can handle SNMPv2 operations, C<get-bulk> will be used to
+traverse the table. Otherwise, C<get-next> will be used.
+
+If the first argument is a list of I<n> columns, the mapping function
+will be called with I<n+1> arguments. The first argument will be the
+row index, i.e. the list of sub-IDs that was appended to the provided
+column OIDs for this row. Note that the row index will be represented
+as a string, using dot-separated numerical OID notation.
+
+The remaining arguments to the mapping function will be the values of
+each column at the current index. It is possible that the table has
+"holes", i.e. that for a given row index, not all columns have a
+value. For columns with no value at the current row index, C<undef>
+will be passed to the mapping function.
+
+If an error is encountered at any point during the table traversal,
+this method will return undef and leave an error message in C<$errmsg>
+(which is also written out unless C<$suppress_warnings> is non-zero).
+
+Otherwise, the function will return the number of rows traversed,
+i.e. the number of times that the mapping function has been called.
+
+=cut
+
sub map_table ($$$) {
my ($session, $columns, $mapfn) = @_;
return $session->map_table_4 ($columns, $mapfn,
$session->default_max_repetitions ());
}
+=head2 map_table_4() - traverse an SNMP table with more control.
+
+=cut
+
sub map_table_4 ($$$$) {
my ($session, $columns, $mapfn, $max_repetitions) = @_;
return $session->map_table_start_end ($columns, $mapfn,
@@ -450,6 +579,16 @@ sub map_table_4 ($$$$) {
$max_repetitions);
}
+=head2 map_table_start_end() - traverse an SNMP table with lower/upper index limits.
+
+ $result = $session->map_table_start_end ($columns, $mapfn,
+ $start, $end, $max_repetition);
+
+Similar to C<map_table_4()>, except that the start and end index can
+be specified.
+
+=cut
+
sub map_table_start_end ($$$$$$) {
my ($session, $columns, $mapfn, $start, $end, $max_repetitions) = @_;
@@ -477,7 +616,7 @@ sub map_table_start_end ($$$$$$) {
my $out_index;
- $out_index = &oid_diff ($base, $oid);
+ $out_index = oid_diff ($base, $oid);
my $cmp;
if (!defined $smallest_index
|| ($cmp = index_compare ($out_index,$smallest_index)) == -1) {
@@ -592,6 +731,112 @@ sub ber_error ($$) {
return $this->error ("$type:\n$errmsg");
}
+=head2 receive_trap_1() - receive message on trap socket.
+
+This method waits until a message is received on the trap socket. If
+successful, it returns two values: the message that was received, and
+the address of the sender as a C<sockaddr> structure. This address
+can be passed to C<getnameinfo()> to convert it to readable output.
+
+This method doesn't check whether the message actually encodes a trap
+or anything else - the caller should use C<decode_trap_request()> to
+find out.
+
+=cut
+
+sub receive_trap_1 ($ ) {
+ my ($this) = @_;
+ my ($remote_addr, $iaddr, $port, $trap, $af);
+ $remote_addr = recv ($this->sock,
+ $this->{'pdu_buffer'},
+ $this->max_pdu_len,0);
+ return undef unless $remote_addr;
+ $trap = $this->{'pdu_buffer'};
+ return ($trap, $remote_addr);
+}
+
+=head2 receive_trap() - receive message on trap socket (deprecated version).
+
+This function is identical to C<receive_trap_1()>, except that it
+returns the sender address as three (formerly two) separate values:
+The host IP address, the port, and (since version 1.14) the address
+family. If you use this, please consider moving to
+C<receive_trap_1()>, because it is easier to process the sender
+address in sockaddr format, in particular in a world where IPv4 and
+IPv6 coexist.
+
+=cut
+
+sub receive_trap ($ ) {
+ my ($this) = @_;
+ my ($trap, $remote_addr) = $this->receive_trap_1 ();
+ return undef unless defined $trap;
+
+ my ($iaddr, $port, $af);
+ if ((defined $ipv6_addr_len) && (length $remote_addr == $ipv6_addr_len)) {
+ ($port, $iaddr) = unpack_sockaddr_in6 ($remote_addr);
+ $af = AF_INET6;
+ } else {
+ ($port, $iaddr) = unpack_sockaddr_in ($remote_addr);
+ $af = AF_INET;
+ }
+ return ($trap, $iaddr, $port, $af);
+}
+
+=head2 decode_trap_request()
+
+ ($community, $ent, $agent, $gen, $spec, $dt, $bindings)
+ = $session->decode_trap_request ($trap);
+
+Given a message such as one returned as the first return value from
+C<receive_trap_1()> or C<receive_trap()>, try to decode it as some
+notification PDU. The code can handle SNMPv1 and SNMPv2 traps as well
+as SNMPv2 INFORMs, although it fails to distinguish traps from
+informs, which makes it hard to handle informs correctly (they should
+be acknowledged).
+
+The C<$ent>, C<$agent>, C<$gen>, C<$spec>, and C<$dt> values will only
+be defined for SNMPv1 traps. For SNMPv2 traps and informs, some of
+this information will be encoded as bindings.
+
+=cut
+
+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);
+}
+
+=head1 METHODS in package SNMPv1_Session
+
+=cut
+
package SNMPv1_Session;
use strict qw(vars subs); # see above
@@ -613,8 +858,43 @@ BEGIN {
sub snmp_version { 0 }
-# Supports both IPv4 and IPv6.
-# Numeric IPv6 addresses must be passed between square brackets []
+=head2 open() - create an SNMPv1 session object
+
+ $session = SNMPv1_Session->open
+ ($host, $community, $port,
+ $max_pdu_len, $local_port, $max_repetitions,
+ $local_host, $ipv4only);
+
+Note that all arguments except for C<$host> are optional. The
+C<$host> can be specified either as a hostname or as a numeric
+address. Numeric IPv6 addresses must be enclosed in square brackets
+[]
+
+C<$community> defaults to C<public>.
+
+C<$port> defaults to 161, the standard UDP port to send SNMP requests
+to.
+
+C<$max_pdu_len> defaults to 8000.
+
+C<$local_port> can be specified if a specific local port is desired,
+for example because of firewall rules for the response packets. If
+none is specified, the operating system will choose a random port.
+
+C<$max_repetitions> is the maximum number of repetitions requested in
+C<get-bulk> requests. It is only relevant in SNMPv2(c) and later.
+
+C<$local_host> can be used to specify a specific address/interface.
+It is useful on hosts that have multiple addresses if a specific
+address should be used, for example because of firewall rules.
+
+If C<$ipv4only> is either not present or non-zero, then an IPv4-only
+socket will be used. This is also the case if the system only
+supports IPv4. Otherwise, an IPv6 socket is created. IPv6 sockets
+support both IPv6 and IPv4 requests and responses.
+
+=cut
+
sub open {
my($this,
$remote_hostname,$community,$port,
@@ -633,37 +913,40 @@ sub open {
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 && exists $the_socket{$sockfamily}) {
- $socket = $the_socket{$sockfamily};
- } 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{$sockfamily} = $socket
- if $SNMP_Session::recycle_socket;
- }
- $remote_addr = pack_sockaddr_in ($port, $remote_addr)
- if defined $remote_addr;
+ 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 && exists $the_socket{$sockfamily}) {
+ $socket = $the_socket{$sockfamily};
+ } 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{$sockfamily} = $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 (defined $remote_hostname) {
+ # 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");
+ }
+ } else {
+ $sockfamily = AF_INET6;
}
if ($SNMP_Session::recycle_socket && exists $the_socket{$sockfamily}) {
@@ -675,7 +958,8 @@ sub open {
LocalPort => $local_port)
|| return $this->error_return ("creating socket: $!");
} else {
- $socket = IO::Socket::INET6->new(Proto => 17,
+ $socket = IO::Socket::INET6->new(Domain => AF_INET6,
+ Proto => 17,
Type => SOCK_DGRAM,
LocalAddr => $local_hostname,
LocalPort => $local_port)
@@ -711,10 +995,26 @@ sub open {
};
}
+=head2 open_trap_session() - create a session for receiving SNMP traps.
+
+ $session = open_trap_session ($port, $ipv4only);
+
+C<$port> defaults to 162, the standard UDP port that SNMP
+notifications are sent to.
+
+If C<$ipv4only> is either not present or non-zero, then an IPv4-only
+socket will be used. This is also the case if the system only
+supports IPv4. Otherwise, an IPv6 socket is created. IPv6 sockets
+can receive messages over both IPv6 and IPv4.
+
+=cut
+
sub open_trap_session (@) {
- my ($this, $port) = @_;
+ my ($this, $port, $ipv4only) = @_;
$port = 162 unless defined $port;
- return $this->open (undef, "", 161, undef, $port);
+ $ipv4only = 1 unless defined $ipv4only;
+ return $this->open (undef, "", 161, undef, $port,
+ undef, undef, $ipv4only);
}
sub sock { $_[0]->{sock} }
@@ -892,22 +1192,6 @@ sub receive_response_3 {
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) = 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";
@@ -990,6 +1274,10 @@ sub decode_request {
return undef;
}
+=head1 METHODS in package SNMPv2c_Session
+
+=cut
+
package SNMPv2c_Session;
use strict qw(vars subs); # see above
use vars qw(@ISA);
@@ -1001,6 +1289,19 @@ use Carp;
sub snmp_version { 1 }
+=head2 open() - create an SNMPv2(c) session object
+
+ $session = SNMPv2c_Session->open
+ ($host, $community, $port,
+ $max_pdu_len, $local_port, $max_repetitions,
+ $local_host, $ipv4only);
+
+The calling and return conventions are identical to
+C<SNMPv1_Session::open()>, except that this returns a session object
+that supports SNMPv2 operations.
+
+=cut
+
sub open {
my $session = SNMPv1_Session::open (@_);
return undef unless defined $session;
@@ -1121,3 +1422,334 @@ sub map_table_start_end ($$$$$$) {
}
1;
+
+=head1 EXAMPLES
+
+The basic usage of these routines works like this:
+
+ use BER;
+ use SNMP_Session;
+
+ # Set $host to the name of the host whose SNMP agent you want
+ # to talk to. Set $community to the community name under
+ # which you want to talk to the agent. Set port to the UDP
+ # port on which the agent listens (usually 161).
+
+ $session = SNMP_Session->open ($host, $community, $port)
+ or die "couldn't open SNMP session to $host";
+
+ # Set $oid1, $oid2... to the BER-encoded OIDs of the MIB
+ # variables you want to get.
+
+ if ($session->get_request_response ($oid1, $oid2, ...)) {
+ ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print pretty_print ($oid)," => ", pretty_print ($value), "\n";
+ }
+ } else {
+ die "No response from agent on $host";
+ }
+
+=head2 Encoding OIDs
+
+In order to BER-encode OIDs, you can use the function
+B<BER::encode_oid>. It takes (a vector of) numeric subids as an
+argument. For example,
+
+ use BER;
+ encode_oid (1, 3, 6, 1, 2, 1, 1, 1, 0)
+
+will return the BER-encoded OID for the B<sysDescr.0>
+(1.3.6.1.2.1.1.1.0) instance of MIB-2.
+
+=head2 Decoding the results
+
+When C<get_request_response()> returns success, you must decode the
+response PDU from the remote agent. The function
+C<decode_get_response()> can be used to do this. It takes a
+C<get-response> PDU, checks its syntax and returns the I<bindings>
+part of the PDU. This is where the remote agent actually returns the
+values of the variables in your query.
+
+You should iterate over the individual bindings in this I<bindings>
+part and extract the value for each variable. In the example above,
+the returned bindings are simply printed using the
+C<BER::pretty_print()> function.
+
+For better readability of the OIDs, you can also use the following
+idiom, where the C<%pretty_oids> hash maps BER-encoded numerical OIDs
+to symbolic OIDs. Note that this simple-minded mapping only works for
+response OIDs that exactly match known OIDs, so it's unsuitable for
+table walking (where the response OIDs include an additional row
+index).
+
+ %ugly_oids = qw(sysDescr.0 1.3.6.1.2.1.1.1.0
+ sysContact.0 1.3.6.1.2.1.1.4.0);
+ foreach (keys %ugly_oids) {
+ $ugly_oids{$_} = encode_oid (split (/\./, $ugly_oids{$_}));
+ $pretty_oids{$ugly_oids{$_}} = $_;
+ }
+ ...
+ if ($session->get_request_response ($ugly_oids{'sysDescr.0'},
+ $ugly_oids{'sysContact.0'})) {
+ ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print $pretty_oids{$oid}," => ",
+ pretty_print ($value), "\n";
+ }
+ } ...
+
+=head2 Set Requests
+
+Set requests are generated much like C<get> or C<getNext> requests
+are, with the exception that you have to specify not just OIDs, but
+also the values the variables should be set to. Every binding is
+passed as a reference to a two-element array, the first element being
+the encoded OID and the second one the encoded value. See the
+C<test/set-test.pl> script for an example, in particular the
+subroutine C<snmpset>.
+
+=head2 Walking Tables
+
+Beginning with version 0.57 of C<SNMP_Session.pm>, there is API
+support for walking tables. The C<map_table()> method can be used for
+this as follows:
+
+ sub walk_function ($$$) {
+ my ($index, $val1, $val3) = @_;
+ ...
+ }
+
+ ...
+ $columns = [$base_oid1, $base_oid3];
+ $n_rows = $session->map_table ($columns, \&walk_function);
+
+The I<columns> argument must be a reference to a list of OIDs for table
+columns sharing the same index. The method will traverse the table and
+call the I<walk_function> for each row. The arguments for these calls
+will be:
+
+=over
+
+=item 1. the I<row index> as a partial OID in dotted notation, e.g.
+C<1.3>, or C<10.0.1.34>.
+
+=item 2. the values of the requested table columns in that row, in
+BER-encoded form. If you want to use the standard C<pretty_print()>
+subroutine to decode the values, you can use the following idiom:
+
+ grep (defined $_ && ($_=pretty_print $_), ($val1, $val3));
+
+=back
+
+=head2 Walking Tables With C<get-bulk>
+
+Since version 0.67, C<SNMP_Session> uses a different C<get_table>
+implementation for C<SNMPv2c_Session>s. This version uses the
+``powerful C<get-bulk> operator'' to retrieve many table rows with
+each request. In general, this will make table walking much faster
+under SNMPv2c, especially when round-trip times to the agent are long.
+
+There is one difficulty, however: With C<get-bulk>, a management
+application can specify the maximum number of rows to return in a
+single response. C<SNMP_Session.pm> provides a new function,
+C<map_table_4>, in which this C<maxRepetitions> value can be specified
+explicitly.
+
+For maximum efficiency, it should be set to a value that is one
+greater than the number of rows in the table. If it is smaller, then
+C<map_table()> will use more request/response cycles than necessary;
+if it is bigger, the agent will have to compute variable bindings
+beyond the end of the table (which C<map_table()> will throw away).
+
+Of course it is usually impossible to know the size of the table in
+advance. If you don't specify C<maxRepetitions> when walking a table,
+then C<map_table()> will use a per-session default
+(C<$session-E<gt>default_max_repetitions>). The default value for this
+default is 12.
+
+If you walk a table multiple times, and the size of the table is
+relatively stable, you should use the return value of C<map_table()>
+(which is the number of rows it has encountered) to compute the next
+value of C<maxRepetitions>. Remember to add one so that C<map_table()>
+notices when the table is finished!
+
+Note that for really big tables, this doesn't make a big difference,
+since the table won't fit in a single response packet anyway.
+
+=head2 Sending Traps
+
+To send a trap, you have to open an SNMP session to the trap receiver.
+Usually this is a process listening to UDP port 162 on a network
+management station. Then you can use the C<trap_request_send()> method
+to encode and send SNMPv1 traps. There is no way to find out whether
+the trap was actually received at the management station - SNMP traps
+are fundamentally unreliable.
+
+When constructing an SNMPv1 trap, you must provide
+
+=over
+
+=item * the "enterprise" Object Identifier for the entity that
+generates the trap
+
+=item * your IP address
+
+=item * the generic trap type
+
+=item * the specific trap type
+
+=item * the C<sysUpTime> at the time of trap generation
+
+=item * a sequence (may be empty) of variable bindings further
+describing the trap.
+
+=back
+
+For SNMPv2 traps, you need:
+
+=over
+
+=item * the trap's OID
+
+=item * the C<sysUpTime> at the time of trap generation
+
+=item * the bindings list as above
+
+=back
+
+For SNMPv2 traps, the uptime and trap OID are encoded as bindings which
+are added to the front of the other bindings you provide.
+
+Here is a short example:
+
+ my $trap_receiver = "netman.noc";
+ my $trap_community = "SNMP_Traps";
+ my $trap_session = $version eq '1'
+ ? SNMP_Session->open ($trap_receiver, $trap_community, 162)
+ : SNMPv2c_Session->open ($trap_receiver, $trap_community, 162);
+ my $myIpAddress = ...;
+ my $start_time = time;
+
+ ...
+
+ sub link_down_trap ($$) {
+ my ($if_index, $version) = @_;
+ my $genericTrap = 2; # linkDown
+ my $specificTrap = 0;
+ my @ifIndexOID = ( 1,3,6,1,2,1,2,2,1,1 );
+ my $upTime = int ((time - $start_time) * 100.0);
+ my @myOID = ( 1,3,6,1,4,1,2946,0,8,15 );
+
+ warn "Sending trap failed"
+ unless ($version eq '1')
+ ? $trap_session->trap_request_send (encode_oid (@myOID),
+ encode_ip_address ($myIpAddress),
+ encode_int ($genericTrap),
+ encode_int ($specificTrap),
+ encode_timeticks ($upTime),
+ [encode_oid (@ifIndex_OID,$if_index),
+ encode_int ($if_index)],
+ [encode_oid (@ifDescr_OID,$if_index),
+ encode_string ("foo")])
+ : $trap_session->v2_trap_request_send (\@linkDown_OID, $upTime,
+ [encode_oid (@ifIndex_OID,$if_index),
+ encode_int ($if_index)],
+ [encode_oid (@ifDescr_OID,$if_index),
+ encode_string ("foo")]);
+ }
+
+=head2 Receiving Traps
+
+Since version 0.60, C<SNMP_Session.pm> supports the receipt and
+decoding of SNMPv1 trap requests. Since version 0.75, SNMPv2 Trap PDUs
+are also recognized.
+
+To receive traps, you have to create a special SNMP session that
+passively listens on the SNMP trap transport address, usually on UDP
+port 162. Then you can receive traps - actually, SNMPv1 traps, SNMPv2
+traps, and SNMPv2 informs, using the C<receive_trap_1()> method and
+decode them using C<decode_trap_request()>. The I<enterprise>,
+I<agent>, I<generic>, I<specific> and I<sysUptime> return values are
+only defined for SNMPv1 traps. In SNMPv2 traps and informs, the
+equivalent information is contained in the bindings.
+
+ my $trap_session = SNMPv1_Session->open_trap_session (162, 0)
+ or die "cannot open trap session";
+ my ($trap, $sender_sockaddr) = $trap_session->receive_trap_1 ()
+ or die "cannot receive trap";
+ my ($community, $enterprise, $agent,
+ $generic, $specific, $sysUptime, $bindings)
+ = $trap_session->decode_trap_request ($trap)
+ or die "cannot decode trap received"
+ ...
+ my ($binding, $oid, $value);
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid, $value) = decode_by_template ($binding, "%O%@");
+ print BER::pretty_oid ($oid)," => ",pretty_print ($value),"\n";
+ }
+
+=head1 AUTHORS
+
+Created by: Simon Leinen E<lt>simon.leinen at switch.chE<gt>
+
+Contributions and fixes by:
+
+=over
+
+=item Matthew Trunnell E<lt>matter at media.mit.eduE<gt>
+
+=item Tobias Oetiker E<lt>tobi at oetiker.chE<gt>
+
+=item Heine Peters E<lt>peters at dkrz.deE<gt>
+
+=item Daniel L. Needles E<lt>dan_needles at INS.COME<gt>
+
+=item Mike Mitchell E<lt>mcm at unx.sas.comE<gt>
+
+=item Clinton Wong E<lt>clintdw at netcom.comE<gt>
+
+=item Alan Nichols E<lt>Alan.Nichols at Ebay.Sun.COME<gt>
+
+=item Mike McCauley E<lt>mikem at open.com.auE<gt>
+
+=item Andrew W. Elble E<lt>elble at icculus.nsg.nwu.eduE<gt>
+
+=item Brett T Warden E<lt>wardenb at eluminant.comE<gt>: pretty C<UInteger32>
+
+=item Michael Deegan E<lt>michael at cnspc18.murdoch.edu.auE<gt>
+
+=item Sergio Macedo E<lt>macedo at tmp.com.brE<gt>
+
+=item Jakob Ilves (/IlvJa) E<lt>jakob.ilves at oracle.comE<gt>: PDU capture
+
+=item Valerio Bontempi E<lt>v.bontempi at inwind.itE<gt>: IPv6 support
+
+=item Lorenzo Colitti E<lt>lorenzo at colitti.comE<gt>: IPv6 support
+
+=item Philippe Simonet E<lt>Philippe.Simonet at swisscom.comE<gt>: Export C<avoid...>
+
+=item Luc Pauwels E<lt>Luc.Pauwels at xalasys.comE<gt>: C<use_16bit_request_ids>
+
+=item Andrew Cornford-Matheson E<lt>andrew.matheson at corenetworks.comE<gt>: inform
+
+=item Gerry Dalton E<lt>gerry.dalton at consolidated.comE<gt>: C<strict subs> bug
+
+=item Mike Fischer E<lt>mlf2 at tampabay.rr.comE<gt>: pass MSG_DONTWAIT to C<recv()>
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright (c) 1995-2009, Simon Leinen.
+
+This program is free software; you can redistribute it under the
+"Artistic License 2.0" included in this distribution (file "Artistic").
+
+=cut
diff --git a/lib/SNMP_Table.pm b/lib/SNMP_Table.pm
new file mode 100644
index 0000000..033e4f6
--- /dev/null
+++ b/lib/SNMP_Table.pm
@@ -0,0 +1,132 @@
+### SNMP_Table.pm
+###
+### Convenience routines for dealing with SNMP tables
+###
+### Author: Simon Leinen <simon at switch.ch>
+### Date created: 24-Apr-2005
+###
+### Tables are a central concept to SNMP (or, more precisely, of the
+### SMI or Structure of Management Instrumentation). SNMP tables are
+### conceptually similar to tables in relational database management
+### systems (RDBMS). Each SNMP table has at least one conceptual
+### columns, of which at least one serve as an index columns.
+###
+### This little library implements a form of "object-relational
+### mapping", or rather, a "relational-object mapping" from SNMP
+### tables to Perl objects.
+###
+### In particular, they convert SNMP table rows into Perl objects, and
+### allow a user to store these objects into some composite structure
+### based on the index(es).
+
+package SNMP_Table;
+
+require 5.004;
+
+use strict;
+use vars qw(@ISA @EXPORT $VERSION);
+use Exporter;
+
+use SNMP_util;
+
+ at ISA = qw(Exporter);
+
+ at EXPORT = qw(snmp_rows_to_objects snmp_row_to_object snmp_map_row_objects);
+
+### snmp_rows_to_objects TARGET, CLASS, PREFIX, COLUMNS...
+###
+### Returns a reference to a hash that maps a table's index to objects
+### created from the set of COLUMNS. The COLUMNS are partial OID
+### names, to each of which the PREFIX is prepended. An object is
+### created for each row in the table, by creating a hash reference
+### with a slot for each column, named by the (partial) column name.
+### It is blessed to the CLASS.
+###
+### For example, if we have the following table at $TARGET:
+###
+### index fooBar fooBaz fooBlech
+###
+### 1000 asd 23498 vohdajae
+### 1001 fgh 45824 yaohetoo
+### 1002 jkl 89732 engahghi
+###
+### Then the call:
+###
+### snmp_rows_to_objects ($TARGET, 'MyFoo', 'foo', 'bar', 'baz', 'blech')
+###
+### will create a hash reference similar to this:
+###
+### $result = {};
+### $result{1000} = bless { 'bar' => 'asd',
+### 'baz' => 23498,
+### 'blech' => 'vohdajae' }, 'MyFoo';
+### $result{1001} = bless { 'bar' => 'fgh',
+### 'baz' => 45824,
+### 'blech' => 'yaohetoo' }, 'MyFoo';
+### $result{1002} = bless { 'bar' => 'jkl',
+### 'baz' => 89732,
+### 'blech' => 'engahghi' }, 'MyFoo';
+###
+sub snmp_rows_to_objects ($$$@) {
+ my ($target, $class, $prefix, @cols) = @_;
+ my $result = {};
+ snmp_map_row_objects
+ ($target, $class,
+ sub () {
+ my ($index, $object) = @_;
+ $result->{$index} = $object;
+ },
+ $prefix, @cols);
+ return $result;
+}
+
+### snmp_map_row_objects TARGET, CLASS, MAPFN, PREFIX, COLUMNS...
+###
+### This function traverses a table, creating an object for each row,
+### and applying the user-supplied MAPFN to each of these objects.
+###
+### The table is defined by PREFIX and COLUMNS, as described for
+### snmp_rows_to_objects above. An object is created according to
+### CLASS and COLUMNS, as described above. The difference is that,
+### rather than putting all objects in a hash, we simply apply the
+### user-supplied MAPFN to each row object.
+###
+sub snmp_map_row_objects ($$$$@) {
+ my ($target, $class, $mapfn, $prefix, @cols) = @_;
+ my @coloids = map ($prefix.ucfirst $_, at cols);
+ my @slotnames = @cols;
+ snmpmaptable ($target,
+ sub () {
+ my ($index, @colvals) = @_;
+ my $object = bless { 'index' => $index }, $class;
+ foreach my $slotname (@slotnames) {
+ $object->{$slotname} = shift @colvals;
+ }
+ &$mapfn ($index, $object);
+ },
+ @coloids);
+}
+
+### snmp_row_to_object TARGET, CLASS, INDEX, PREFIX, COLUMNS...
+###
+### This can be used if one is only interested in a single row,
+### defined by INDEX. This function uses a large SNMP get request to
+### retrieve all columns of interest, and assembles the result into a
+### hash blessed to CLASS. This hash directly represents the row
+### object. Note that this function returns just a single hash, as
+### opposed to snmp_rows_to_objects, which returns a hash that maps
+### index values to such hashes.
+###
+sub snmp_row_to_object ($$$$@ ) {
+ my ($target, $class, $index, $prefix, @cols) = @_;
+ my @coloids = map ($prefix.ucfirst $_.".".$index, at cols);
+ my @result = snmpget ($target, @coloids);
+ my %result = ();
+ foreach my $col (@cols) {
+ $result{$col} = shift @result;
+ }
+ bless \%result, $class;
+ return \%result;
+}
+
+1;
diff --git a/lib/SNMP_util.pm b/lib/SNMP_util.pm
index 20226f5..7ce96a6 100644
--- a/lib/SNMP_util.pm
+++ b/lib/SNMP_util.pm
@@ -2,7 +2,7 @@
######################################################################
### SNMP_util -- SNMP utilities using SNMP_Session.pm and BER.pm
######################################################################
-### Copyright (c) 1998-2008, Mike Mitchell.
+### Copyright (c) 1998-2010, Mike Mitchell.
###
### This program is free software; you can redistribute it under the
### "Artistic License 2.0" included in this distribution
@@ -29,6 +29,8 @@
### Joerg Kummer <JOERG.KUMMER at Roche.COM>: TimeTicks support in snmpset()
### Christopher J. Tengi <tengi at CS.Princeton.EDU>: Gauge32 support in snmpset()
### Nicolai Petri <nicolai at catpipe.net>: hashref passing for snmpwalkhash()
+### <jaccobs at online.nl>: parse NOTIFICATION-TYPE in MIB
+### Dan Thorson <Dan.Thorson at seagate.com>: handle quotes in MIB comments better
######################################################################
package SNMP_util;
@@ -44,7 +46,7 @@ use BER "1.02";
use SNMP_Session "1.00";
use Socket;
-$VERSION = '1.13';
+$VERSION = '1.15';
@ISA = qw(Exporter);
@@ -479,15 +481,18 @@ sub snmpget ($@) {
my(@enoid, $var, $response, $bindings, $binding, $value, $oid, @retvals);
my $session;
+ @retvals = ();
$session = &snmpopen($host, 0, \@vars);
if (!defined($session)) {
carp "SNMPGET Problem for $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return wantarray ? @retvals : undef;
}
@enoid = &toOID(@vars);
- return undef unless defined $enoid[0];
+ if ($#enoid < 0) {
+ return wantarray ? @retvals : undef;
+ }
if ($session->get_request_response(@enoid)) {
$response = $session->pdu_buffer;
@@ -503,7 +508,7 @@ sub snmpget ($@) {
$var = join(' ', @vars);
carp "SNMPGET Problem for $var on $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return wantarray ? @retvals : undef;
}
#
@@ -516,15 +521,18 @@ sub snmpgetnext ($@) {
my($noid);
my $session;
+ @retvals = ();
$session = &snmpopen($host, 0, \@vars);
if (!defined($session)) {
carp "SNMPGETNEXT Problem for $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return wantarray ? @retvals : undef;
}
@enoid = &toOID(@vars);
- return undef unless defined $enoid[0];
+ if ($#enoid < 0) {
+ return wantarray ? @retvals : undef;
+ }
undef @vars;
undef @retvals;
@@ -547,7 +555,7 @@ sub snmpgetnext ($@) {
$var = join(' ', @vars);
carp "SNMPGETNEXT Problem for $var on $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return wantarray ? @retvals : undef;
}
}
@@ -576,17 +584,31 @@ sub snmpwalk_flg ($$@) {
my(%soid);
my(%done, %rethash, $h_ref);
+ $h_ref = (ref $vars[$#vars] eq "HASH") ? pop(@vars) : \%rethash;
+
$session = &snmpopen($host, 0, \@vars);
if (!defined($session)) {
carp "SNMPWALK Problem for $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($SNMP_util::Return_hash_refs);
+ return (%$h_ref);
+ } else {
+ @retvals = ();
+ return (@retvals);
+ }
}
- $h_ref = (ref $vars[$#vars] eq "HASH") ? pop(@vars) : \%rethash;
-
@enoid = toOID(@vars);
- return undef unless defined $enoid[0];
+ if ($#enoid < 0) {
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($SNMP_util::Return_hash_refs);
+ return (%$h_ref);
+ } else {
+ @retvals = ();
+ return (@retvals);
+ }
+ }
# GIL
#
@@ -662,7 +684,15 @@ sub snmpwalk_flg ($$@) {
}
if ($ok) {
my $tmp = encode_oid_with_errmsg ($tempo);
- return undef unless defined $tmp;
+ if (!defined $tmp) {
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($SNMP_util::Return_hash_refs);
+ return (%$h_ref);
+ } else {
+ @retvals = ();
+ return (@retvals);
+ }
+ }
if (exists($done{$tmp})) { # GIL, Ilvja
#
# We've detected a loop for $nnoid[$ix], so mark it as finished.
@@ -743,18 +773,17 @@ sub snmpwalk_flg ($$@) {
last if ($#nnoid < 0); # @nnoid empty means we are done walking.
}
- if ($got) {
- if (defined($hash_sub)) {
- return ($h_ref) if ($SNMP_util::Return_hash_refs);
- return (%$h_ref);
- } else {
- return (@retvals);
- }
- } else {
+ if (!$got) {
$var = join(' ', @vars);
carp "SNMPWALK Problem for $var on $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ @retvals = ();
+ }
+ if (defined($hash_sub)) {
+ return ($h_ref) if ($SNMP_util::Return_hash_refs);
+ return (%$h_ref);
+ } else {
+ return (@retvals);
}
}
@@ -767,11 +796,12 @@ sub snmpset($@) {
my($oid, @retvals, $type, $value, $val);
my $session;
+ @retvals = ();
$session = &snmpopen($host, 0, \@vars);
if (!defined($session)) {
carp "SNMPSET Problem for $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return wantarray ? @retvals : undef;
}
while(@vars) {
@@ -826,16 +856,18 @@ sub snmpset($@) {
} else {
carp "unknown SNMP type: $type\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return wantarray ? @retvals : undef;
}
if (!defined($val)) {
carp "SNMP type $type value $value didn't encode properly\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return wantarray ? @retvals : undef;
}
push @enoid, [$oid,$val];
}
- return undef unless defined $enoid[0];
+ if ($#enoid < 0) {
+ return wantarray ? @retvals : undef;
+ }
if ($session->set_request_response(@enoid)) {
$response = $session->pdu_buffer;
($bindings) = $session->decode_get_response($response);
@@ -847,7 +879,7 @@ sub snmpset($@) {
}
return wantarray ? @retvals : $retvals[0];
}
- return undef;
+ return wantarray ? @retvals : undef;
}
#
@@ -912,18 +944,18 @@ sub snmpgetbulk ($$$@) {
my($noid);
my $session;
+ @retvals = ();
$session = &snmpopen($host, 0, \@vars);
if (!defined($session)) {
carp "SNMPGETBULK Problem for $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return @retvals;
}
@enoid = &toOID(@vars);
- return undef unless defined $enoid[0];
+ return @retvals if ($#enoid < 0);
undef @vars;
- undef @retvals;
foreach $noid (@enoid) {
$upoid = pretty_print($noid);
push(@vars, $upoid);
@@ -938,12 +970,12 @@ sub snmpgetbulk ($$$@) {
my $tempv = pretty_print($value);
push @retvals, "$tempo:$tempv";
}
- return (@retvals);
+ return @retvals;
} else {
$var = join(' ', @vars);
carp "SNMPGETBULK Problem for $var on $host\n"
unless ($SNMP_Session::suppress_warnings > 1);
- return undef;
+ return @retvals;
}
}
@@ -1037,7 +1069,10 @@ sub toOID(@) {
}
print "toOID: $var\n" if $SNMP_util::Debug;
$tmp = encode_oid_with_errmsg($var);
- return undef unless defined $tmp;
+ if (!defined($tmp)) {
+ my @empty = ();
+ return @empty;
+ }
push(@retvar, $tmp);
}
return @retvar;
@@ -1086,7 +1121,7 @@ sub snmpLoad_OID_Cache ($) {
while(<CACHE>) {
s/#.*//; # '#' starts a comment
- s/--.*--//g; # comment delimited by '--', like MIBs
+ s/--.*?--/ /g; # comment delimited by '--', like MIBs
s/--.*//; # comment started by '--'
next if (/^$/);
next unless (/\s/); # must have whitespace as separator
@@ -1129,7 +1164,8 @@ sub Check_OID ($) {
if ($oid) {
return ($oid, $tmp);
} else {
- return undef;
+ my @empty = ();
+ return @empty;
}
}
return ($var, $var);
@@ -1172,22 +1208,20 @@ sub snmpMIB_to_OID ($) {
if ($quote) {
next unless /"/;
$quote = 0;
- } else {
- s/--.*--//g; # throw away comments (-- anything --)
- s/^\s*--.*//; # throw away comments at start of line
}
chomp;
-
$buf .= ' ' . $_;
- $buf =~ s/"[^"]*"//g;
- if ($buf =~ /"/) {
+ $buf =~ s/"[^"]*"//g; # throw away quoted strings
+ $buf =~ s/--.*?--/ /g; # throw away comments (-- anything --)
+ $buf =~ s/--.*//; # throw away comments (-- anything to EOL)
+ $buf =~ s/\s+/ /g; # clean up multiple spaces
+
+ if ($buf =~ /"/) { # look for quoted string
$quote = 1;
next;
}
- $buf =~ s/--.*--//g; # throw away comments (-- anything --)
- $buf =~ s/--.*//; # throw away comments (-- anything EOL)
- $buf =~ s/\s+/ /g;
+
if ($buf =~ /DEFINITIONS *::= *BEGIN/) {
$cnt += MIB_fill_OID(\%tOIDs) if ($tgot);
$buf = '';
diff --git a/security.html b/security.html
new file mode 100644
index 0000000..dc11974
--- /dev/null
+++ b/security.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+<html>
+ <head>
+ <title>SNMP support for Perl 5 - Notes on Security</title>
+ </head>
+ <body bgcolor="#ffffff">
+<div align=center>
+ <h1>SNMP support for Perl 5 - Notes on Security</h1>
+</div>
+
+<p> On February 12, 2002, the Computer Emergency Response Team issued
+<em><a
+href="http://www.cert.org/advisories/CA-2002-03.html">CERT™
+Advisory CA-2002-03 Multiple Vulnerabilities in Many Implementations
+of the Simple Network Management Protocol (SNMP)</a></em>. The <a
+href="http://www.ee.oulu.fi/research/ouspg/index.html">OUSPG</a> at
+the University of Oulu in Finland had written an SNMPv1 test suite
+that uncovered difficulties in numerous SNMP implementations with
+respect to improperly encoded SNMP PDUs (Protocol Data Units).
+Possible effects of these vulnerabilities included program crashes as
+well as remote exploitabilities.�</p>
+
+<h2> Why <tt>SNMP_Session.pm</tt>/<tt>BER.pm</tt> Users Shouldn't Be
+Too Concerned </h2>
+
+<p> My SNMP support for Perl 5 is written entirely in Perl. When it
+decodes BER-encoded SNMP PDUs, it parses them from left to right and
+splits them into sub-items as it goes, usually using
+<tt>substr()</tt> or <tt>unpack()</tt>. </p>
+
+<hr>
+<address>
+<!-- hhmts start -->
+2002/04/07 21:43:11
+<!-- hhmts end -->
+<a href="http://www.switch.ch/misc/leinen/">
+ Simon Leinen <simon.leinen at switch.ch></A>
+
+</address>
+
+</body>
+</html>
diff --git a/t/00ber.t b/t/00ber.t
new file mode 100644
index 0000000..cf2bf14
--- /dev/null
+++ b/t/00ber.t
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+###
+### Test BER.pm encoding and decoding routines
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+use BER;
+
+### en_decode_test VALUE, ENCODER, TEMPLATE [, ENCODED]
+###
+### Test both encoding and decoding.
+###
+sub en_decode_test ($$$@) {
+ my ($value, $encoder, $template, $encoded) = @_;
+ if (defined $encoded) {
+ is(&$encoder ($value), $encoded);
+ } else {
+ $encoded = &$encoder ($value);
+ }
+ my ($x) = decode_by_template ($encoded, $template);
+ is($x, $value);
+}
+
+### tt PDU, TEMPLATE, EXPECTED, ARGS...
+###
+### Test decoding by template. The PDU is decoded using TEMPLATE and
+### (optionally) ARGS. The resulting values are then compared against
+### EXPECTED, which is a reference to a vector of expected values.
+###
+sub tt ($$$@) {
+ my ($pdu, $template, $desired_result, @args) = @_;
+ my @values = decode_by_template ($pdu, $template, @args);
+ print "# ".join ("; ", @values)."\n";
+ is_deeply (\@values, $desired_result);
+}
+
+en_decode_test ("foo", \&encode_string, "%s", "\x04\x03foo");
+en_decode_test (123, \&encode_int, "%i", "\x02\x01\x7b");
+is (encode_oid (1,3,6,1), "\x06\x03\x2b\x06\x01");
+tt ("\x02\x01\x03", "%i", [3]);
+tt ("\x02\x01\x03", "%u", [3]);
+tt ("\x02\x01\xff", "%i", [-1]);
+tt ("\x30\x03\x02\x01\xff", "%{%i", [-1]);
+tt ("\x30\x0b\x02\x01\x12\x02\x01\x02\x04\x03foo", "%{%i%i%s", [18, 2, "foo"]);
+tt ("\x30\x0b\x02\x01\x12\x02\x01\x02\x04\x03foo", "%{%i%2i%s", [18, "foo"]);
+tt ("\x30\x0b\x02\x01\x12\x02\x01\x02\x04\x03foo", "%{%i%2i%*s", [18], "foo");
+tt ("\x04\x03foo", "%s", ["foo"]);
+tt ("\x38\x03\x02\x01\xff", "%*{%i", [-1], 0x38);
+is (join (":",decode_sequence ("\x30\x05\x02\x00\x02\x01\x01\x30\x05\x02\x00\x02\x01\x01")),
+ "\x02\x00\x02\x01\x01:\x30\x05\x02\x00\x02\x01\x01");
diff --git a/test/asn1-test.pl b/test/asn1-test.pl
new file mode 100755
index 0000000..d632772
--- /dev/null
+++ b/test/asn1-test.pl
@@ -0,0 +1,32 @@
+#!/usr/local/bin/perl -w
+######################################################################
+### Name: asn1-test.pl
+### Date Created: Sat Feb 1 18:45:38 1997
+### Author: Simon Leinen <simon at switch.ch>
+### RCS $Id: asn1-test.pl,v 1.3 1997-08-15 23:55:48 simon Exp $
+######################################################################
+
+require 5.002;
+use strict;
+use ASN_1;
+
+my ($result, $index);
+
+($result, $index) = &ASN_1::BER::decode_length ("\x81\x02", 0);
+die unless $result == (1 << 7) + 2;
+die unless $index == 2;
+($result, $index) = &ASN_1::BER::decode ("\x01\x01\x01", 0);
+die unless ref($result) eq 'ASN_1::Boolean';
+die unless $result->value eq 1;
+die unless $index == 3;
+($result, $index) = &ASN_1::BER::decode ("\x01\x01\x00", 0);
+die unless ref($result) eq 'ASN_1::Boolean';
+die unless $result->value eq 0;
+die unless $index == 3;
+($result, $index) = &ASN_1::BER::decode ("\x10\x03\x01\x01\x00", 0);
+die "$result" unless ref($result) eq 'ASN_1::Sequence';
+die unless length $result->members == 1;
+die unless ref(($result->members)[0]) eq 'ASN_1::Boolean';
+die unless ($result->members)[0]->value eq 0;
+die unless $index == 5;
+1;
diff --git a/test/atm-cfgmaker b/test/atm-cfgmaker
new file mode 100644
index 0000000..7126cad
--- /dev/null
+++ b/test/atm-cfgmaker
@@ -0,0 +1,128 @@
+#!/usr/local/bin/perl -w
+###
+### atm-cfgmaker HOST [COMMUNITY]
+###
+### Generate MRTG configuration for the PVCs and PVPs configured on a
+### Cisco LS1010 ATM switch. Uses the RFC 1213 interfaces group and
+### Cisco's CISCO-ATM-CONN-MIB.
+###
+use strict;
+require 5.002;
+
+use SNMP_Session "0.57";
+use BER;
+
+my $ciscoAtmVclInCells = [1,3,6,1,4,1,9,10,13,1,2,1,1,13];
+my $ciscoAtmVclOutCells = [1,3,6,1,4,1,9,10,13,1,2,1,1,14];
+my $ciscoAtmVclCrossIfIndex = [1,3,6,1,4,1,9,10,13,1,2,1,1,15];
+my $ciscoAtmVclCrossVpi = [1,3,6,1,4,1,9,10,13,1,2,1,1,16];
+my $ciscoAtmVclCrossVci = [1,3,6,1,4,1,9,10,13,1,2,1,1,17];
+
+my $ciscoAtmVplInCells = [1,3,6,1,4,1,9,10,13,1,1,1,1,12];
+my $ciscoAtmVplOutCells = [1,3,6,1,4,1,9,10,13,1,1,1,1,13];
+my $ciscoAtmVplCrossIfIndex = [1,3,6,1,4,1,9,10,13,1,1,1,1,14];
+my $ciscoAtmVplCrossVpi = [1,3,6,1,4,1,9,10,13,1,1,1,1,15];
+
+my $router = shift @ARGV || usage (1);
+my $community = shift @ARGV || 'public';
+
+my $session = SNMP_Session->open ($router, $community, 161)
+ || die "Cannot open SNMP session to $router";
+my $if_table = $session->get_if_table ();
+$session->map_table ([$ciscoAtmVclCrossIfIndex,
+ $ciscoAtmVclCrossVpi,
+ $ciscoAtmVclCrossVci],
+ sub ($$$$) {
+ my ($index, $cross_if_index, $cross_vpi, $cross_vci)
+ = @_;
+ my ($if_index, $vpi, $vci) = split ('\.', $index);
+ grep (defined $_ && ($_=pretty_print $_),
+ ($cross_if_index, $cross_vpi, $cross_vci));
+ out_link ($ciscoAtmVclInCells,
+ $ciscoAtmVclOutCells,
+ $index,
+ $if_index,
+ $cross_if_index,
+ "VPI=$vpi VCI=$vci",
+ "VPI=$cross_vpi VCI=$cross_vpi",
+ $if_table,
+ $router, $community)
+ unless ($cross_vpi == 0 && $cross_vci == 5
+ || $cross_vpi == 0 && $cross_vci == 16);
+ });
+$session->map_table ([$ciscoAtmVplCrossIfIndex,
+ $ciscoAtmVplCrossVpi],
+ sub ($$$$) {
+ my ($index, $cross_if_index, $cross_vpi)
+ = @_;
+ my ($if_index, $vpi) = split ('\.', $index);
+ grep (defined $_ && ($_=pretty_print $_),
+ ($cross_if_index, $cross_vpi));
+ out_link ($ciscoAtmVplInCells,
+ $ciscoAtmVplOutCells,
+ $index,
+ $if_index,
+ $cross_if_index,
+ "VPI=$vpi",
+ "VPI=$cross_vpi",
+ $if_table,
+ $router, $community);
+ });
+$session->close ()
+ || warn "Cannot close SNMP session to $router";
+1;
+
+sub usage ($) {
+ if ($_[0]) {
+ die "Usage: $0 switch-name [community]\n";
+ } else {
+ warn "Usage: $0 switch-name [community]\n";
+ }
+}
+
+sub out_link () {
+ my ($in, $out, $index, $if_index, $cross_if_index, $source_vxi, $dest_vxi, $if_table, $host, $community) = @_;
+ my $source_if_descr = $if_table->{$if_index}->{ifDescr} || $if_index;
+ my $dest_if_descr = $if_table->{$cross_if_index}->{ifDescr} || $cross_if_index;
+ my $source_speed = $if_table->{$if_index}->{ifSpeed};
+ my $dest_speed = $if_table->{$cross_if_index}->{ifSpeed};
+ my $min_speed = $source_speed < $dest_speed ? $source_speed : $dest_speed;
+ my $target_name = $host.'-'.$source_if_descr.'-'.$source_vxi;
+ $target_name =~ s/V[CP]I=//g;
+ $target_name =~ s/-ATM/-/;
+ $target_name =~ s@[-. /]@- at g;
+ my $source_oid = join ('.',@{$in}).".".$index;
+ my $dest_oid = join ('.',@{$out}).".".$index;
+ print STDOUT ("#$host Interface $source_if_descr $source_vxi\n");
+ print STDOUT "Target[$target_name]: $source_oid&$dest_oid:$community\@$host * 53\n";
+ print STDOUT "Options[$target_name]: growright,bits\n";
+ print STDOUT "Title[$target_name]: $host $source_vxi\n";
+ print STDOUT "PageTop[$target_name]: <hr><H3>ATM Traffic on $source_vxi</H3>\n";
+ print STDOUT "YLegend[$target_name]: bits per second\n";
+ print STDOUT "ShortLegend[$target_name]: bps\n";
+ print STDOUT "MaxBytes[$target_name]: ",$min_speed/8,"\n";
+ print STDOUT "AbsMax[$target_name]: ",$min_speed/8,"\n";
+ print STDOUT "\n";
+}
+
+package SNMP_Session;
+
+sub get_if_table ($) {
+ my ($session) = @_;
+
+ my $result = {};
+
+ my $ifDescr = [1,3,6,1,2,1,2,2,1,2];
+ my $ifSpeed = [1,3,6,1,2,1,2,2,1,5];
+ my $locIfDescr = [1,3,6,1,4,1,9,2,2,1,1,28];
+ $session->map_table ([$ifDescr,$locIfDescr,$ifSpeed],
+ sub ($$$) {
+ my ($index, $ifDescr, $locIfDescr, $ifSpeed) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($ifDescr, $locIfDescr, $ifSpeed));
+ $result->{$index} = {'ifDescr' => $ifDescr,
+ 'ifSpeed' => $ifSpeed,
+ 'locIfDescr' => $locIfDescr};
+ });
+ $result;
+}
diff --git a/test/atol-test.c b/test/atol-test.c
new file mode 100644
index 0000000..ab94bec
--- /dev/null
+++ b/test/atol-test.c
@@ -0,0 +1,37 @@
+/*
+ atol-test.c
+
+ Date Created: Sun Jun 22 21:20:45 1997
+ Author: Simon Leinen <simon at switch.ch>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+void atol_test (const char *);
+
+int
+main (int argc, char **argv)
+{
+ unsigned k;
+
+ for (k = 1; k < argc; ++k)
+ {
+ atol_test (argv[k]);
+ }
+ return 0;
+}
+
+void
+atol_test (const char *string)
+{
+ long l, l2;
+ unsigned long ul;
+ long long ll;
+
+ l = atol (string);
+ l2 = strtol (string, 0, 10);
+ ul = strtoul (string, 0, 10);
+ ll = atoll (string);
+ printf ("%s => %ld(atol) %lld(atoll) %ld(strtol) %lu(strtoul)\n", string, l, ll, l2, ul);
+}
diff --git a/test/bad-trap.pl b/test/bad-trap.pl
new file mode 100644
index 0000000..dfebb4c
--- /dev/null
+++ b/test/bad-trap.pl
@@ -0,0 +1,40 @@
+#!/usr/local/bin/perl -w
+use strict;
+use BER;
+use SNMP_Session;
+use Socket;
+
+my $bad_trap
+ = "\x30\x82\x00\x3a"
+ ."\x02\x01\x00\x04\x06\x64\x63\x72\x32\x73\x62\xa4\x2d\x06\x07\x2b"
+ ."\x06\x01\x04\x01\x82\x3e\x40\x04\xce\xaf\x3b\x0e\x02\x01\x04\x02"
+ ."\x01\x00\x43\x04\x36\xfb\x79\x93\x30\x10\x30\x82\x00\x0c\x06\x08"
+ ."\x2b\x06\x01\x02\x01\x01\x01\x00\x05\x00";
+
+my $session = SNMP_Session->open ('localhost', 'public', 1162)
+ || die "open SNMP session: $SNMP_Session::errmsg";
+print_trap ($session, $bad_trap);
+$session->close ()
+ || warn "close SNMP session: $SNMP_Session::errmsg";
+1;
+
+sub print_trap ($$) {
+ my ($this, $trap) = @_;
+ my ($encoded_pair, $oid, $value);
+ my ($community, $ent, $agent, $gen, $spec, $dt, $bindings)
+ = $this->decode_trap_request ($trap);
+ my ($binding, $prefix);
+ print " community: ".$community."\n";
+ print " enterprise: ".BER::pretty_oid ($ent)."\n";
+ print " agent addr: ".inet_ntoa ($agent)."\n";
+ print " generic ID: $gen\n";
+ print " specific ID: $spec\n";
+ print " uptime: ".BER::pretty_uptime_value ($dt)."\n";
+ $prefix = " bindings: ";
+ while ($bindings) {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print $prefix.BER::pretty_oid ($oid)." => ".pretty_print ($value)."\n";
+ $prefix = " ";
+ }
+}
diff --git a/test/bay-atm-test.pl b/test/bay-atm-test.pl
new file mode 100755
index 0000000..306f665
--- /dev/null
+++ b/test/bay-atm-test.pl
@@ -0,0 +1,54 @@
+#!/usr/local/bin/perl -w
+
+require 5;
+
+use SNMP_Session;
+use BER;
+
+$SNMP_Session::suppress_warnings = 1;
+
+$hostname = shift @ARGV || &usage;
+$community = shift @ARGV || 'public';
+&usage if $#ARGV >= 0;
+
+%ugly_oids = qw(u1 1.3.6.1.4.1.930.2.2.2.1.1.1.6.4.2
+ u2 1.3.6.1.4.1.930.2.2.2.1.1.1.8.4.2
+ );
+foreach (keys %ugly_oids) {
+ $ugly_oids{$_} = encode_oid (split (/\./, $ugly_oids{$_}));
+ $pretty_oids{$ugly_oids{$_}} = $_;
+}
+
+srand();
+die "Couldn't open SNMP session to $hostname: $SNMP_Session::errmsg"
+ unless ($session = SNMP_Session->open ($hostname, $community, 161));
+snmp_get ($session, qw(u1 u2));
+$session->close ();
+1;
+
+sub snmp_get
+{
+ my($session, @oids) = @_;
+ my($response, $bindings, $binding, $value, $oid);
+
+ grep ($_ = $ugly_oids{$_}, @oids);
+
+ if ($session->get_request_response (@oids)) {
+ $response = $session->pdu_buffer;
+ ($bindings) = $session->decode_get_response ($response);
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print $pretty_oids{$oid}," => ",
+ pretty_print ($value), "\n";
+ }
+ } else {
+ warn "SNMP problem: $SNMP_Session::errmsg\n";
+ }
+}
+
+sub usage
+{
+ die "usage: $0 hostname [community]";
+}
diff --git a/test/bgpls b/test/bgpls
new file mode 100644
index 0000000..8072ad7
--- /dev/null
+++ b/test/bgpls
@@ -0,0 +1,209 @@
+#!/usr/bin/perl -w
+###
+### bgpls
+###
+### Simon Leinen <simon.leinen at switch.ch>
+###
+### Show the table of the BGP-4 peers of a router.
+###
+use strict;
+
+use SNMP_util;
+
+## Prototypes
+sub bgp_table ($ );
+sub pretty_peer_state ($$);
+sub init_mibs ();
+
+## If $external_only is non-zero, internal BGP peers will be
+## suppressed from output.
+##
+my $external_only = 1;
+
+## if $abnormal_only is non-zero, only peerings where the operational
+## state is inconsistent with the administrative state are printed.
+##
+my $abnormal_only = 1;
+
+init_mibs ();
+foreach my $target (@ARGV) {
+ bgp_table ($target);
+}
+1;
+
+sub bgp_table ($ ) {
+ my ($target) = @_;
+ my ($local_as, $bgp_id) = snmpget ($target, 'bgpLocalAs.0', 'bgpIdentifier.0');
+ snmpmaptable ($target,
+ sub ()
+ {
+ my ($index, $state, $admin_status, $bgp_version, $remote_as,
+ $est_time) = @_;
+ return if $external_only and $remote_as == $local_as;
+ return if $abnormal_only and
+ (($admin_status == 1 && $state == 1)
+ || ($admin_status == 2 && $state == 6));
+
+ printf STDOUT ("%-15s AS%-5d v%1d %s",
+ $index, $remote_as, $bgp_version,
+ pretty_peer_state ($state, $admin_status));
+ if ($state == 6) {
+ printf STDOUT ", %s", pretty_time ($est_time);
+ }
+ print STDOUT "\n";
+ },
+ 'bgpPeerState',
+ 'bgpPeerAdminStatus',
+ 'bgpPeerNegotiatedVersion',
+ 'bgpPeerRemoteAs',
+ 'bgpPeerFsmEstablishedTime',
+ );
+}
+
+sub pretty_peer_state ($$) {
+ my ($state, $admin_status) = @_;
+ my @pretty_peer_state
+ = qw(idle connect active opensent openconfirm established);
+ return $state.'/'.$admin_status if $state < 1;
+ return $state.'/'.$admin_status if $state > @pretty_peer_state;
+ return 'shutdown' if $state == 1 and $admin_status == 1;
+ return $pretty_peer_state[$state-1]
+ .($admin_status == 2 ? "" : "/".($admin_status == 1 ? 'stop' : $admin_status));
+};
+
+sub pretty_time ($ ) {
+ my ($secs) = @_;
+ my $result = '';
+ if ($secs > 86400) {
+ $result = sprintf ("%3dd ", int ($secs/86400)), $secs %= 86400;
+ } else { $result = " "; }
+ if ($secs > 3600) {
+ $result .= sprintf ("%02dh", int ($secs/3600)), $secs %= 3600;
+ } else { $result .= " "; }
+ return $result.sprintf ("%02d:%02d", int ($secs/60), $secs % 60);
+}
+
+sub init_mibs () {
+ snmpmapOID
+ (qw(
+ bgp 1.3.6.1.2.1.15
+ bgpVersion 1.3.6.1.2.1.15.1
+ bgpLocalAs 1.3.6.1.2.1.15.2
+ bgpPeerTable 1.3.6.1.2.1.15.3
+ bgpPeerEntry 1.3.6.1.2.1.15.3.1
+ bgpPeerIdentifier 1.3.6.1.2.1.15.3.1.1
+ bgpPeerState 1.3.6.1.2.1.15.3.1.2
+ bgpPeerAdminStatus 1.3.6.1.2.1.15.3.1.3
+ bgpPeerNegotiatedVersion 1.3.6.1.2.1.15.3.1.4
+ bgpPeerLocalAddr 1.3.6.1.2.1.15.3.1.5
+ bgpPeerLocalPort 1.3.6.1.2.1.15.3.1.6
+ bgpPeerRemoteAddr 1.3.6.1.2.1.15.3.1.7
+ bgpPeerRemotePort 1.3.6.1.2.1.15.3.1.8
+ bgpPeerRemoteAs 1.3.6.1.2.1.15.3.1.9
+ bgpPeerInUpdates 1.3.6.1.2.1.15.3.1.10
+ bgpPeerOutUpdates 1.3.6.1.2.1.15.3.1.11
+ bgpPeerInTotalMessages 1.3.6.1.2.1.15.3.1.12
+ bgpPeerOutTotalMessages 1.3.6.1.2.1.15.3.1.13
+ bgpPeerLastError 1.3.6.1.2.1.15.3.1.14
+ bgpPeerFsmEstablishedTransitions 1.3.6.1.2.1.15.3.1.15
+ bgpPeerFsmEstablishedTime 1.3.6.1.2.1.15.3.1.16
+ bgpPeerConnectRetryInterval 1.3.6.1.2.1.15.3.1.17
+ bgpPeerHoldTime 1.3.6.1.2.1.15.3.1.18
+ bgpPeerKeepAlive 1.3.6.1.2.1.15.3.1.19
+ bgpPeerHoldTimeConfigured 1.3.6.1.2.1.15.3.1.20
+ bgpPeerKeepAliveConfigured 1.3.6.1.2.1.15.3.1.21
+ bgpPeerMinASOriginationInterval 1.3.6.1.2.1.15.3.1.22
+ bgpPeerMinRouteAdvertisementInterval 1.3.6.1.2.1.15.3.1.23
+ bgpPeerInUpdateElapsedTime 1.3.6.1.2.1.15.3.1.24
+ bgpIdentifier 1.3.6.1.2.1.15.4
+ bgpRcvdPathAttrTable 1.3.6.1.2.1.15.5
+ bgpPathAttrEntry 1.3.6.1.2.1.15.5.1
+ bgpPathAttrPeer 1.3.6.1.2.1.15.5.1.1
+ bgpPathAttrDestNetwork 1.3.6.1.2.1.15.5.1.2
+ bgpPathAttrOrigin 1.3.6.1.2.1.15.5.1.3
+ bgpPathAttrASPath 1.3.6.1.2.1.15.5.1.4
+ bgpPathAttrNextHop 1.3.6.1.2.1.15.5.1.5
+ bgpPathAttrInterASMetric 1.3.6.1.2.1.15.5.1.6
+ bgp4PathAttrTable 1.3.6.1.2.1.15.6
+ bgp4PathAttrEntry 1.3.6.1.2.1.15.6.1
+ bgp4PathAttrPeer 1.3.6.1.2.1.15.6.1.1
+ bgp4PathAttrIpAddrPrefixLen 1.3.6.1.2.1.15.6.1.2
+ bgp4PathAttrIpAddrPrefix 1.3.6.1.2.1.15.6.1.3
+ bgp4PathAttrOrigin 1.3.6.1.2.1.15.6.1.4
+ bgp4PathAttrASPathSegment 1.3.6.1.2.1.15.6.1.5
+ bgp4PathAttrNextHop 1.3.6.1.2.1.15.6.1.6
+ bgp4PathAttrMultiExitDisc 1.3.6.1.2.1.15.6.1.7
+ bgp4PathAttrLocalPref 1.3.6.1.2.1.15.6.1.8
+ bgp4PathAttrAtomicAggregate 1.3.6.1.2.1.15.6.1.9
+ bgp4PathAttrAggregatorAS 1.3.6.1.2.1.15.6.1.10
+ bgp4PathAttrAggregatorAddr 1.3.6.1.2.1.15.6.1.11
+ bgp4PathAttrCalcLocalPref 1.3.6.1.2.1.15.6.1.12
+ bgp4PathAttrBest 1.3.6.1.2.1.15.6.1.13
+ bgp4PathAttrUnknown 1.3.6.1.2.1.15.6.1.14
+ bgpTraps 1.3.6.1.2.1.15.7
+
+ ciscoBgp4MIB 1.3.6.1.4.1.9.9.187
+ ciscoBgp4NotifyPrefix 1.3.6.1.4.1.9.9.187.0
+ ciscoBgp4MIBObjects 1.3.6.1.4.1.9.9.187.1
+ cbgpRoute 1.3.6.1.4.1.9.9.187.1.1
+ cbgpRouteTable 1.3.6.1.4.1.9.9.187.1.1.1
+ cbgpRouteEntry 1.3.6.1.4.1.9.9.187.1.1.1.1
+ cbgpRouteAfi 1.3.6.1.4.1.9.9.187.1.1.1.1.1
+ cbgpRouteSafi 1.3.6.1.4.1.9.9.187.1.1.1.1.2
+ cbgpRoutePeerType 1.3.6.1.4.1.9.9.187.1.1.1.1.3
+ cbgpRoutePeer 1.3.6.1.4.1.9.9.187.1.1.1.1.4
+ cbgpRouteAddrPrefix 1.3.6.1.4.1.9.9.187.1.1.1.1.5
+ cbgpRouteAddrPrefixLen 1.3.6.1.4.1.9.9.187.1.1.1.1.6
+ cbgpRouteOrigin 1.3.6.1.4.1.9.9.187.1.1.1.1.7
+ cbgpRouteASPathSegment 1.3.6.1.4.1.9.9.187.1.1.1.1.8
+ cbgpRouteNextHop 1.3.6.1.4.1.9.9.187.1.1.1.1.9
+ cbgpRouteMedPresent 1.3.6.1.4.1.9.9.187.1.1.1.1.10
+ cbgpRouteMultiExitDisc 1.3.6.1.4.1.9.9.187.1.1.1.1.11
+ cbgpRouteLocalPrefPresent 1.3.6.1.4.1.9.9.187.1.1.1.1.12
+ cbgpRouteLocalPref 1.3.6.1.4.1.9.9.187.1.1.1.1.13
+ cbgpRouteAtomicAggregate 1.3.6.1.4.1.9.9.187.1.1.1.1.14
+ cbgpRouteAggregatorAS 1.3.6.1.4.1.9.9.187.1.1.1.1.15
+ cbgpRouteAggregatorAddrType 1.3.6.1.4.1.9.9.187.1.1.1.1.16
+ cbgpRouteAggregatorAddr 1.3.6.1.4.1.9.9.187.1.1.1.1.17
+ cbgpRouteBest 1.3.6.1.4.1.9.9.187.1.1.1.1.18
+ cbgpRouteUnknownAttr 1.3.6.1.4.1.9.9.187.1.1.1.1.19
+ cbgpPeer 1.3.6.1.4.1.9.9.187.1.2
+ cbgpPeerTable 1.3.6.1.4.1.9.9.187.1.2.1
+ cbgpPeerEntry 1.3.6.1.4.1.9.9.187.1.2.1.1
+ cbgpPeerPrefixAccepted 1.3.6.1.4.1.9.9.187.1.2.1.1.1
+ cbgpPeerPrefixDenied 1.3.6.1.4.1.9.9.187.1.2.1.1.2
+ cbgpPeerPrefixLimit 1.3.6.1.4.1.9.9.187.1.2.1.1.3
+ cbgpPeerPrefixAdvertised 1.3.6.1.4.1.9.9.187.1.2.1.1.4
+ cbgpPeerPrefixSuppressed 1.3.6.1.4.1.9.9.187.1.2.1.1.5
+ cbgpPeerPrefixWithdrawn 1.3.6.1.4.1.9.9.187.1.2.1.1.6
+ cbgpPeerLastErrorTxt 1.3.6.1.4.1.9.9.187.1.2.1.1.7
+ cbgpPeerPrevState 1.3.6.1.4.1.9.9.187.1.2.1.1.8
+ cbgpPeerCapsTable 1.3.6.1.4.1.9.9.187.1.2.2
+ cbgpPeerCapsEntry 1.3.6.1.4.1.9.9.187.1.2.2.1
+ cbgpPeerCapCode 1.3.6.1.4.1.9.9.187.1.2.2.1.1
+ cbgpPeerCapIndex 1.3.6.1.4.1.9.9.187.1.2.2.1.2
+ cbgpPeerCapValue 1.3.6.1.4.1.9.9.187.1.2.2.1.3
+ cbgpPeerAddrFamilyTable 1.3.6.1.4.1.9.9.187.1.2.3
+ cbgpPeerAddrFamilyEntry 1.3.6.1.4.1.9.9.187.1.2.3.1
+ cbgpPeerAddrFamilyAfi 1.3.6.1.4.1.9.9.187.1.2.3.1.1
+ cbgpPeerAddrFamilySafi 1.3.6.1.4.1.9.9.187.1.2.3.1.2
+ cbgpPeerAddrFamilyName 1.3.6.1.4.1.9.9.187.1.2.3.1.3
+ cbgpPeerAddrFamilyPrefixTable 1.3.6.1.4.1.9.9.187.1.2.4
+ cbgpPeerAddrFamilyPrefixEntry 1.3.6.1.4.1.9.9.187.1.2.4.1
+ cbgpPeerAcceptedPrefixes 1.3.6.1.4.1.9.9.187.1.2.4.1.1
+ cbgpPeerDeniedPrefixes 1.3.6.1.4.1.9.9.187.1.2.4.1.2
+ cbgpPeerPrefixAdminLimit 1.3.6.1.4.1.9.9.187.1.2.4.1.3
+ cbgpPeerPrefixThreshold 1.3.6.1.4.1.9.9.187.1.2.4.1.4
+ cbgpPeerPrefixClearThreshold 1.3.6.1.4.1.9.9.187.1.2.4.1.5
+ cbgpPeerAdvertisedPrefixes 1.3.6.1.4.1.9.9.187.1.2.4.1.6
+ cbgpPeerSuppressedPrefixes 1.3.6.1.4.1.9.9.187.1.2.4.1.7
+ cbgpPeerWithdrawnPrefixes 1.3.6.1.4.1.9.9.187.1.2.4.1.8
+ ciscoBgp4NotificationPrefix 1.3.6.1.4.1.9.9.187.2
+ ciscoBgp4MIBConformance 1.3.6.1.4.1.9.9.187.3
+ ciscoBgp4MIBCompliances 1.3.6.1.4.1.9.9.187.3.1
+ ciscoBgp4MIBGroups 1.3.6.1.4.1.9.9.187.3.2
+ ciscoBgp4RouteGroup 1.3.6.1.4.1.9.9.187.3.2.1
+ ciscoBgp4PeerGroup 1.3.6.1.4.1.9.9.187.3.2.2
+ ciscoBgp4PeerGroup1 1.3.6.1.4.1.9.9.187.3.2.4
+ ));
+}
diff --git a/test/bridge-list-fdb b/test/bridge-list-fdb
new file mode 100644
index 0000000..8546f27
--- /dev/null
+++ b/test/bridge-list-fdb
@@ -0,0 +1,104 @@
+#!/usr/local/bin/perl -w
+
+## Print dot1dTpFdbTable from RFC 1493
+
+use strict;
+use SNMP_Session;
+use BER;
+
+my $dot1dTpFdbAddress = [1,3,6,1,2,1,17,4,3,1,1];
+my $dot1dTpFdbPort = [1,3,6,1,2,1,17,4,3,1,2];
+my $dot1dTpFdbStatus = [1,3,6,1,2,1,17,4,3,1,3];
+
+my $host = shift @ARGV || die "Usage: $0 host [community]";
+my $community = shift @ARGV || 'public';
+
+my $session = SNMP_Session->open ($host, $community, 161)
+ || die "open SNMP session to $community\@$host: $!";
+$session->map_table ([$dot1dTpFdbPort, $dot1dTpFdbStatus],
+ sub () {
+ my ($addr, $port, $status) = @_;
+ $addr = ether_hex (hex_string_aux (pack ("C6", split ('\.', $addr))));
+ grep (defined $_ && ($_=pretty_print $_),
+ ($port, $status));
+ note_fdb ($addr, $port, $status);
+ });
+$session->close
+ || warn "close SNMP session: $!";
+
+list_fdbs ();
+1;
+
+my %all_fdbs;
+
+sub fdb_addr ($) { defined $_[1] ? $_[0]->{addr} = $_[1] : $_[0]->{addr}; }
+sub fdb_port ($) { defined $_[1] ? $_[0]->{port} = $_[1] : $_[0]->{port}; }
+sub fdb_status ($) { defined $_[1] ? $_[0]->{status} = $_[1] : $_[0]->{status}; }
+
+sub make_fdb ($@) {
+ my ($addr, $port, $status) = @_;
+ {
+ addr => $addr, port => $port, status => $status,
+ };
+}
+
+sub note_fdb ($$@) {
+ my ($addr, @other_args) = @_;
+ my $fdb = make_fdb ($addr, @other_args);
+ $all_fdbs{$addr} = $fdb;
+ $fdb;
+}
+
+sub list_fdbs () {
+ print_fdbs_table_header ();
+ foreach my $fdb (sort { $a->{port} <=> $b->{port} || $a->{addr} cmp $b->{addr} }
+ values %all_fdbs) {
+ my $addr = fdb_addr ($fdb);
+ my $port = fdb_port ($fdb);
+ my $status = fdb_status ($fdb);
+ printf STDOUT ("%4d %-20s %s\n",
+ $port, $addr, pretty_fdb_status ($status));
+ }
+}
+
+sub print_fdbs_table_header () {
+ printf STDOUT ("%-4s %-20s %s\n",
+ "port",
+ "MAC addr.",
+ "status");
+ print STDOUT (("=" x 35),"\n");
+}
+
+sub pretty_fdb_status ($) {
+ my ($status) = @_;
+ if ($status == 1) {
+ return "other";
+ } elsif ($status == 2) {
+ return "invalid";
+ } elsif ($status == 3) {
+ return "learned";
+ } elsif ($status == 4) {
+ return "self";
+ } elsif ($status == 5) {
+ return "mgmt";
+ } else {
+ return "ILLEGAL".$status;
+ }
+}
+
+sub ether_hex ($) {
+ my ($string) = @_;
+ $string =~ s/([0-9a-f][0-9a-f])/$1:/g;
+ $string =~ s/:$//;
+ $string;
+}
+
+sub hex_string_aux ($) {
+ my ($binary_string) = @_;
+ my ($c, $result);
+ $result = '';
+ for $c (unpack "C*", $binary_string) {
+ $result .= sprintf "%02x", $c;
+ }
+ $result;
+}
diff --git a/test/cammer b/test/cammer
new file mode 100755
index 0000000..f0b8d07
--- /dev/null
+++ b/test/cammer
@@ -0,0 +1,245 @@
+#! /usr/sepp/bin/perl
+# -*- mode: Perl -*-
+##################################################################
+# Cammer 1.0
+##################################################################
+# Created by Tobias Oetiker <oetiker at ee.ethz.ch>
+#
+# Cammer needs the address of your local cisco switch and the address
+# of your router. With this it can produce a list of which machine
+# is currently active on which Switch interface
+##################################################################
+# Distributed under the GNU copyleft
+# Copyright 2000 by Tobias Oetiker
+##################################################################
+
+
+require 5.005;
+use strict;
+my $DEBUG = 0;
+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 SNMP_Session "0.78";
+use BER "0.77";
+use SNMP_util "0.77";
+use Getopt::Long;
+use Pod::Usage;
+use Socket;
+
+
+my %OID = ('vlanIndex' => [1,3,6,1,4,1,9,5,1,9,2,1,1],
+ 'vmVlan' => [1,3,6,1,4,1,9,9,68,1,2,2,1,2],
+ 'dot1dTpFdbPort' => [1,3,6,1,2,1,17,4,3,1,2],
+ 'dot1dBasePortIfIndex' => [1,3,6,1,2,1,17,1,4,1,2],
+ 'sysObjectID' => [1,3,6,1,2,1,1,2,0],
+ 'CiscolocIfDescr' => [1,3,6,1,4,1,9,2,2,1,1,28],
+ 'ifAlias' => [1,3,6,1,2,1,31,1,1,1,18],
+ 'ifName' => [1,3,6,1,2,1,31,1,1,1,1],
+ 'ipNetToMediaPhysAddress' => [1,3,6,1,2,1,4,22,1,2],
+ );
+
+
+sub main {
+ my %opt;
+ options(\%opt);
+ # which vlans do exist on the device
+ my @vlans;
+ my $vlani;
+ my %vlan;
+ my $sws = SNMPv2c_Session->open ($opt{sw},$opt{swco},161)
+ || die "Opening SNMP_Session\n";
+
+
+ warn "* Gather VLAN index Table from Switch\n";
+ my $sysdesc = (snmpget($opt{swco}.'@'.$opt{sw},'sysDescr'))[0];
+
+ if ($sysdesc =~ /2900/){
+ warn "* Going into Cisco 2900 Mode\n";
+ $sws->map_table_4 ( [$OID{'vmVlan'}],
+ sub { my($x,$value) = pretty(@_);
+ $vlan{$x} = $value; # catalyst 2900
+ print "if: $x, vlan: $value\n" if $DEBUG;
+ if (not scalar grep {$_ eq $value} @vlans) {
+ push @vlans, $value;
+ print "vlan: $value\n" if $DEBUG;
+ }
+ }
+ ,100);
+ } else {
+ $sws->map_table_4 ([$OID{'vlanIndex'}],
+ sub {
+ my($x,$value) = pretty(@_);
+ push @vlans, $value;
+ print "vlan: $value\n" if $DEBUG;
+ }
+ ,100 );
+ }
+ # which ifNames
+ my %name;
+ warn "* Gather Interface Name Table from Switch\n";
+ $sws->map_table_4 ([$OID{'ifName'}],
+ sub { my($if,$name) = pretty(@_);
+ print "if: $if, name: $name\n" if $DEBUG;
+ $name{$if}=$name;
+ }
+ ,100);
+ $sws->close();
+ # get mac to ip from router
+ my $ros = SNMPv2c_Session->open ($opt{ro},$opt{roco},161)
+ || die "Opening SNMP_Session\n";
+
+ my %ip;
+ warn "* Gather Arp Table from Router\n";
+ $ros->map_table_4 ([$OID{'ipNetToMediaPhysAddress'}],
+ sub {
+ my($ip,$mac) = pretty(@_);
+ $mac = unpack 'H*', pack 'a*',$mac;
+ $mac =~ s/../$&:/g;
+ $mac =~ s/.$//;
+ $ip =~ s/^.+?\.//;
+ push @{$ip{$mac}}, $ip;
+ print "ip: $ip, mac: $mac\n" if $DEBUG;
+ }
+ ,100);
+ $ros->close();
+ # walk CAM table for each VLAN
+ my %if;
+ my %port;
+ warn "* Gather Mac 2 Port and Port 2 Interface table for all VLANS\n";
+ foreach my $vlan (@vlans){
+ # catalist 2900 does not use com at vlan hack
+ my $sws = SNMPv2c_Session->open ($opt{sw},$opt{swco}.'@'.$vlan,161)
+ || die "Opening SNMP_Session\n";
+ $sws->map_table_4 ([$OID{'dot1dTpFdbPort'}],
+ sub {
+ my($mac,$port) = pretty(@_);
+ next if $port == 0;
+ $mac = sprintf "%02x:%02x:%02x:%02x:%02x:%02x", (split /\./, $mac);
+ print "mac: $mac,port: $port\n" if $DEBUG;
+ $port{$vlan}{$mac}=$port;
+ }
+ ,100);
+ $sws->map_table_4 ( [$OID{'dot1dBasePortIfIndex'}],
+ sub { my($port,$if) = pretty(@_);
+ next if $port == 0;
+ print "port: $port, if: $if\n" if $DEBUG;
+ $if{$vlan}{$port} = $if;
+ }
+ ,100);
+ $sws->close();
+ }
+ my %output;
+ foreach my $vlan (@vlans){
+ foreach my $mac (keys %{$port{$vlan}}){
+ my @ip = $ip{$mac} ? @{$ip{$mac}} : ();
+ my @host;
+ foreach my $ip (@ip) {
+ my $host = gethostbyaddr(pack('C4',split(/\./,$ip)),AF_INET);
+ $host =~ s/\.ethz\.ch//;
+ push @host, ($host or $ip);
+ }
+ my $name = $name{$if{$vlan}{$port{$vlan}{$mac}}};
+ my $truevlan = $vlan eq 'none' ? $vlan{$if{$vlan}{$port{$vlan}{$mac}}} : $vlan;
+ my $quest = scalar @ip > 1 ? "(Multi If Host)":"";
+ push @{$output{$name}}, sprintf "%4s %-17s %-15s %s %s",$truevlan,$mac,$ip[0],$host[0],$quest;
+ }
+ }
+ foreach my $name (sort keys %output){
+ foreach my $line (@{$output{$name}}) {
+ printf "%-4s %s\n", $name , $line;
+ }
+ }
+}
+
+main;
+exit 0;
+
+
+sub options () {
+ my $opt = shift;
+ GetOptions( $opt,
+ 'help|?',
+ 'man') or pod2usage(2);
+ pod2usage(-verbose => 1) if $$opt{help} or scalar @ARGV != 2;
+ $opt->{sw} = shift @ARGV;
+ $opt->{ro} = shift @ARGV;
+ pod2usage(-exitstatus => 0, -verbose => 2) if $$opt{man};
+
+ $opt->{sw} =~ /^(.+)@(.+?)$/;
+ $opt->{sw} = $2;
+ $opt->{swco} = $1;
+ $opt->{ro} =~ /^(.+)@(.+?)$/;
+ $opt->{ro} = $2;
+ $opt->{roco} = $1;
+}
+
+sub pretty(@){
+ my $index = shift;
+ my @ret = ($index);
+ foreach my $x (@_){
+ push @ret, pretty_print($x);
+ };
+ return @ret;
+}
+
+__END__
+
+=head1 NAME
+
+cammer - list switch ports with associated IP-addresses
+
+=head1 SYNOPSIS
+
+cammer [--help|--man] community at switch community at router
+
+
+=head1 DESCRIPTION
+
+B<Cammer> is a script which polls a switch and a router in order to produce
+a list of machines attached (and currently online) at each port of the
+switch.
+
+=head1 COPYRIGHT
+
+Copyright (c) 2000 ETH Zurich, All rights reserved.
+
+=head1 LICENSE
+
+This script is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>oetiker at ee.ethz.chE<gt>
+
+=cut
diff --git a/test/cisco-config-history b/test/cisco-config-history
new file mode 100755
index 0000000..1bcb1e2
--- /dev/null
+++ b/test/cisco-config-history
@@ -0,0 +1,170 @@
+#!/usr/local/bin/perl -w
+###
+### Print history of configuration management actions on Cisco router
+### using CISCO-CONFIG-MAN-MIB.my.
+###
+use strict;
+
+use BER "0.72";
+use SNMP_Session "0.67";
+use Time::HiRes;
+
+use Getopt::Long;
+
+my $version = '1';
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+usage (1) if $#ARGV >= $[;
+
+my $ccmHistoryEventTime = [1,3,6,1,4,1,9,9,43,1,1,6,1,2];
+my $ccmHistoryEventCommandSource = [1,3,6,1,4,1,9,9,43,1,1,6,1,3];
+my $ccmHistoryEventConfigSource = [1,3,6,1,4,1,9,9,43,1,1,6,1,4];
+my $ccmHistoryEventConfigDestination = [1,3,6,1,4,1,9,9,43,1,1,6,1,5];
+my $ccmHistoryEventTerminalType = [1,3,6,1,4,1,9,9,43,1,1,6,1,6];
+my $ccmHistoryEventTerminalNumber = [1,3,6,1,4,1,9,9,43,1,1,6,1,7];
+my $ccmHistoryEventTerminalUser = [1,3,6,1,4,1,9,9,43,1,1,6,1,8];
+my $ccmHistoryEventTerminalLocation = [1,3,6,1,4,1,9,9,43,1,1,6,1,9];
+my $ccmHistoryEventCommandSourceAddress = [1,3,6,1,4,1,9,9,43,1,1,6,1,10];
+my $ccmHistoryEventVirtualHostName = [1,3,6,1,4,1,9,9,43,1,1,6,1,11];
+my $ccmHistoryEventServerAddress = [1,3,6,1,4,1,9,9,43,1,1,6,1,12];
+my $ccmHistoryEventFile = [1,3,6,1,4,1,9,9,43,1,1,6,1,13];
+my $ccmHistoryEventRcpUser = [1,3,6,1,4,1,9,9,43,1,1,6,1,14];
+
+my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, 161)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, 161)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+
+my $router_boottime = router_boottime ($session);
+
+$session->map_table ([$ccmHistoryEventTime,
+ $ccmHistoryEventConfigSource,
+ $ccmHistoryEventConfigDestination],
+ sub () {
+ my ($index, $time, $source, $dest) = @_;
+ local $BER::pretty_print_timeticks = 0;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($time, $source, $dest));
+ my ($action);
+
+ if ($source == 3 && $dest == 2) {
+ $action = "show running-config";
+ } elsif ($source == 4 && $dest == 2) {
+ $action = "show configuration";
+ } elsif ($source == 3 && $dest == 4) {
+ $action = "write memory";
+ } elsif ($source == 2 && $dest == 3) {
+ $action = "configure terminal";
+ } elsif ($source == 6 && $dest == 3) {
+ $action = "configure network tftp:";
+ } elsif ($source == 3 && $dest == 6) {
+ $action = "write network tftp:";
+ } else {
+ $source = pretty_HistoryEventMedium ($source);
+ $dest = pretty_HistoryEventMedium ($dest);
+ $action = "$source -> $dest";
+ }
+ my $localtime = localtime ($time * 1e-2+$router_boottime);
+ print $localtime," ",$action,"\n";
+
+ });
+$session->close ()
+ || die "close SNMP session";
+1;
+
+sub pretty_HistoryEventMedium ($) {
+ my ($medium) = @_;
+ if ($medium == 1) {
+ return 'erase';
+ } elsif ($medium == 2) {
+ return 'commandSource';
+ } elsif ($medium == 3) {
+ return 'running';
+ } elsif ($medium == 4) {
+ return 'startup';
+ } elsif ($medium == 5) {
+ return 'local';
+ } elsif ($medium == 6) {
+ return 'networkTftp';
+ } elsif ($medium == 7) {
+ return 'networkRcp';
+ } else {
+ return "$medium???";
+ }
+}
+
+sub usage ($) {
+ warn <<EOM;
+Usage: $0 [-v (1|2c)] router [community]
+ $0 -h
+
+ -h print this usage message and exit.
+
+ -v version can be used to select the SNMP version. The default
+ is SNMPv1, which is what most devices support. If your box
+ supports SNMPv2c, you should enable this by passing "-v 2c"
+ to the script. SNMPv2c is much more efficient for walking
+ tables, which is what this tool does.
+
+ router hostname or IP address of a Cisco IOS device
+
+ community SNMP community string to use. Defaults to "public".
+EOM
+ exit (1) if $_[0];
+}
+
+### router_boottime SESSION
+###
+### Returns the boot time of the SNMP agent reachable through SESSION
+### in Unix seconds
+###
+sub router_boottime ($) {
+ my ($session) = @_;
+
+ my $uptime = uptime_hundredths ($session);
+ my ($seconds,$microseconds) = Time::HiRes::gettimeofday ();
+ return $seconds + $microseconds * 1e-6 - $uptime * 1e-2;
+}
+
+sub uptime_hundredths ($) {
+ my ($session) = @_;
+ local $BER::pretty_print_timeticks = 0;
+ if ($session->get_request_response (encode_oid (1,3,6,1,2,1,1,3,0))) {
+ my $response = $session->pdu_buffer;
+ my ($bindings) = $session->decode_get_response ($response);
+ my $binding;
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ my ($oid,$value) = decode_by_template ($binding, "%O%@");
+ my ($uptime) = pretty_print ($value);
+ return $uptime;
+ }
+ } else {
+ die "cannot get sysUpTime.0 from $host";
+ }
+}
diff --git a/test/cisco-list-cards b/test/cisco-list-cards
new file mode 100755
index 0000000..be0f81d
--- /dev/null
+++ b/test/cisco-list-cards
@@ -0,0 +1,258 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use SNMP_Session;
+use BER;
+
+my %pretty_card_type =
+(
+ 1 => 'unknown', 2 => 'csc1', 3 => 'csc2', 4 => 'csc3',
+ 5 => 'csc4', 6 => 'rp', 7 => 'cpu-igs', 8 => 'cpu-2500',
+ 9 => 'cpu-3000', 10 => 'cpu-3100', 11 => 'cpu-accessPro',
+ 12 => 'cpu-4000', 13 => 'cpu-4000m', 14 => 'cpu-4500',
+ 15 => 'rsp1', 16 => 'rsp2', 17 => 'cpu-4500m', 18 => 'cpu-1003',
+ 19 => 'cpu-4700', 20 => 'csc-m', 21 => 'csc-mt', 22 => 'csc-mc',
+ 23 => 'csc-mcplus', 24 => 'csc-envm', 25 => 'chassisInterface',
+ 26 => 'cpu-4700S', 27 => 'cpu-7200-npe100', 28 => 'rsp7000',
+ 29 => 'chassisInterface7000', 30 => 'rsp4', 31 => 'cpu-3600',
+ 32 => 'cpu-as5200', 33 => 'c7200-io1fe', 34 => 'cpu-4700m',
+ 35 => 'cpu-1600', 36 => 'c7200-io', 37 => 'cpu-1503',
+ 38 => 'cpu-1502', 39 => 'cpu-as5300', 40 => 'csc-16',
+ 41 => 'csc-p', 50 => 'csc-a', 51 => 'csc-e1', 52 => 'csc-e2',
+ 53 => 'csc-y', 54 => 'csc-s', 55 => 'csc-t', 80 => 'csc-r',
+ 81 => 'csc-r16', 82 => 'csc-r16m', 83 => 'csc-1r', 84 => 'csc-2r',
+ 56 => 'sci4s', 57 => 'sci2s2t', 58 => 'sci4t', 59 => 'mci1t',
+ 60 => 'mci2t', 61 => 'mci1s', 62 => 'mci1s1t', 63 => 'mci2s',
+ 64 => 'mci1e', 65 => 'mci1e1t', 66 => 'mci1e2t', 67 => 'mci1e1s',
+ 68 => 'mci1e1s1t', 69 => 'mci1e2s', 70 => 'mci2e',
+ 71 => 'mci2e1t', 72 => 'mci2e2t', 73 => 'mci2e1s',
+ 74 => 'mci2e1s1t', 75 => 'mci2e2s', 100 => 'csc-cctl1',
+ 101 => 'csc-cctl2', 110 => 'csc-mec2', 111 => 'csc-mec4',
+ 112 => 'csc-mec6', 113 => 'csc-fci', 114 => 'csc-fcit',
+ 115 => 'csc-hsci', 116 => 'csc-ctr', 121 => 'cpu-7200-npe150',
+ 122 => 'cpu-7200-npe200', 123 => 'cpu-wsx5302', 124 => 'gsr-rp',
+ 126 => 'cpu-3810', 127 => 'cpu-2600', 150 => 'sp', 151 => 'eip',
+ 152 => 'fip', 153 => 'hip', 154 => 'sip', 155 => 'trip',
+ 156 => 'fsip', 157 => 'aip', 158 => 'mip', 159 => 'ssp',
+ 160 => 'cip', 161 => 'srs-fip', 162 => 'srs-trip', 163 => 'feip',
+ 164 => 'vip', 165 => 'vip2', 166 => 'ssip', 167 => 'smip',
+ 168 => 'posip', 169 => 'feip-tx', 170 => 'feip-fx',
+ 178 => 'cbrt1', 179 => 'cbr120e1', 180 => 'cbr75e',
+ 181 => 'vip2-50', 182 => 'feip2', 183 => 'acip',
+ 200 => 'npm-4000-fddi-sas', 201 => 'npm-4000-fddi-das',
+ 202 => 'npm-4000-1e', 203 => 'npm-4000-1r', 204 => 'npm-4000-2s',
+ 205 => 'npm-4000-2e1', 206 => 'npm-4000-2e',
+ 207 => 'npm-4000-2r1', 208 => 'npm-4000-2r', 209 => 'npm-4000-4t',
+ 210 => 'npm-4000-4b', 211 => 'npm-4000-8b', 212 => 'npm-4000-ct1',
+ 213 => 'npm-4000-ce1', 214 => 'npm-4000-1a',
+ 215 => 'npm-4000-6e-pci', 217 => 'npm-4000-1fe',
+ 218 => 'npm-4000-1hssi', 230 => 'pa-1fe', 231 => 'pa-8e',
+ 232 => 'pa-4e', 233 => 'pa-5e', 234 => 'pa-4t', 235 => 'pa-4r',
+ 236 => 'pa-fddi', 237 => 'sa-encryption', 238 => 'pa-ah1t',
+ 239 => 'pa-ah2t', 241 => 'pa-a8t-v35', 242 => 'pa-1fe-tx-isl',
+ 243 => 'pa-1fe-fx-isl', 244 => 'pa-1fe-tx-nisl',
+ 245 => 'sa-compression', 246 => 'pa-atm-lite-1', 247 => 'pa-ct3',
+ 248 => 'pa-oc3sm-mux-cbrt1', 249 => 'pa-oc3sm-mux-cbr120e1',
+ 254 => 'pa-ds3-mux-cbrt1', 255 => 'pa-e3-mux-cbr120e1',
+ 257 => 'pa-8b-st', 258 => 'pa-4b-u', 259 => 'pa-fddi-fd',
+ 260 => 'pm-cpm-1e2w', 261 => 'pm-cpm-2e2w',
+ 262 => 'pm-cpm-1e1r2w', 263 => 'pm-ct1-csu', 264 => 'pm-2ct1-csu',
+ 265 => 'pm-ct1-dsx1', 266 => 'pm-2ct1-dsx1',
+ 267 => 'pm-ce1-balanced', 268 => 'pm-2ce1-balanced',
+ 269 => 'pm-ce1-unbalanced', 270 => 'pm-2ce1-unbalanced',
+ 271 => 'pm-4b-u', 272 => 'pm-4b-st', 273 => 'pm-8b-u',
+ 274 => 'pm-8b-st', 275 => 'pm-4as', 276 => 'pm-8as',
+ 277 => 'pm-4e', 278 => 'pm-1e', 280 => 'pm-m4t', 281 => 'pm-16a',
+ 282 => 'pm-32a', 283 => 'pm-c3600-1fe-tx',
+ 284 => 'pm-c3600-compression', 285 => 'pm-dmodem',
+ 288 => 'pm-c3600-1fe-fx', 290 => 'as5200-carrier',
+ 291 => 'as5200-2ct1', 292 => 'as5200-2ce1',
+ 310 => 'pm-as5xxx-12m', 330 => 'wm-c2500-5in1',
+ 331 => 'wm-c2500-t1-csudsu', 332 => 'wm-c2500-sw56-2wire-csudsu',
+ 333 => 'wm-c2500-sw56-4wire-csudsu', 334 => 'wm-c2500-bri',
+ 335 => 'wm-c2500-bri-nt1', 360 => 'wic-serial-1t',
+ 364 => 'wic-s-t-3420', 365 => 'wic-s-t-2186', 366 => 'wic-u-3420',
+ 367 => 'wic-u-2091', 368 => 'wic-u-2091-2081', 400 => 'pa-jt2',
+ 401 => 'pa-posdw', 402 => 'pa-4me1-bal', 406 => 'pa-atmdx-ds3',
+ 407 => 'pa-atmdx-e3', 408 => 'pa-atmdx-sml-oc3',
+ 409 => 'pa-atmdx-smi-oc3', 410 => 'pa-atmdx-mm-oc3',
+ 414 => 'pa-a8t-x21', 415 => 'pa-a8t-rs232',
+ 416 => 'pa-4me1-unbal', 417 => 'pa-4r-fdx',
+ 424 => 'pa-1fe-fx-nisl', 435 => 'mc3810-dcm',
+ 436 => 'mc3810-mfm-e1balanced-bri',
+ 437 => 'mc3810-mfm-e1unbalanced-bri',
+ 438 => 'mc3810-mfm-e1-unbalanced', 439 => 'mc3810-mfm-dsx1-bri',
+ 440 => 'mc3810-mfm-dsx1-csu', 441 => 'mc3810-vcm',
+ 442 => 'mc3810-avm', 443 => 'mc3810-avm-fxs',
+ 444 => 'mc3810-avm-fxo', 445 => 'mc3810-avm-em',
+ 480 => 'as5300-4ct1', 481 => 'as5300-4ce1',
+ 482 => 'as5300-carrier', 500 => 'vic-em', 501 => 'vic-fxo',
+ 502 => 'vic-fxs', 503 => 'vpm-2v', 504 => 'vpm-4v',
+ 530 => 'pos-qoc3-mm', 531 => 'pos-qoc3-sm', 532 => 'pos-oc12-mm',
+ 533 => 'pos-oc12-sm', 534 => 'atm-oc12-mm', 535 => 'atm-oc12-sm',
+ 536 => 'pos-oc48-mm-l', 537 => 'pos-oc48-sm-l', 538 => 'gsr-sfc',
+ 539 => 'gsr-csc', 540 => 'gsr-csc4', 541 => 'gsr-csc8',
+ 542 => 'gsr-sfc8', 545 => 'gsr-oc12chds3-mm',
+ 546 => 'gsr-oc12chds3-sm', 605 => 'pm-atm25',
+);
+
+my $ciscoLS1010 = [1,3,6,1,4,1,9,1,107];
+
+my $sysObjectID_0 = [1,3,6,1,2,1,1,2,0];
+
+my $cardType = [1,3,6,1,4,1,9,3,6,11,1,2];
+my $cardDescr = [1,3,6,1,4,1,9,3,6,11,1,3];
+my $cardSerial = [1,3,6,1,4,1,9,3,6,11,1,4];
+my $cardHwVersion = [1,3,6,1,4,1,9,3,6,11,1,5];
+my $cardSwVersion = [1,3,6,1,4,1,9,3,6,11,1,6];
+my $cardSlotNumber = [1,3,6,1,4,1,9,3,6,11,1,7];
+my $cardContainedByIndex = [1,3,6,1,4,1,9,3,6,11,1,8];
+my $cardOperStatus = [1,3,6,1,4,1,9,3,6,11,1,9];
+my $cardSlots = [1,3,6,1,4,1,9,3,6,11,1,10];
+
+my $host = shift @ARGV || die "Usage: $0 host [community]";
+my $community = shift @ARGV || 'public';
+
+my $session = SNMP_Session->open ($host, $community, 161)
+ || die "open SNMP session to $community\@$host: $!";
+$session->map_table ([$cardContainedByIndex,$cardType,$cardDescr,
+ $cardSerial,$cardHwVersion,$cardSwVersion,
+ $cardSlotNumber,$cardOperStatus,$cardSlots],
+ sub () {
+ my ($index, $contained_by,
+ $type, $descr, $serial,
+ $hw_version, $sw_version,
+ $slot_number, $oper_status, $slots) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($contained_by, $type, $descr, $serial,
+ $hw_version, $sw_version,
+ $slot_number, $oper_status, $slots));
+ note_card ($index, $contained_by, $type,
+ $descr, $serial,
+ $hw_version, $sw_version,
+ $slot_number, $oper_status, $slots);
+ });
+$session->close
+ || warn "close SNMP session: $!";
+
+list_cards ();
+1;
+
+my %all_cards;
+my @top_level_cards;
+
+sub card_index ($) { defined $_[1] ? $_[0]->{idx} = $_[1] : $_[0]->{idx}; }
+sub card_type ($) { defined $_[1] ? $_[0]->{type} = $_[1] : $_[0]->{type}; }
+sub card_parent ($) { defined $_[1] ? $_[0]->{parent} = $_[1] : $_[0]->{parent}; }
+sub card_descr ($) { defined $_[1] ? $_[0]->{descr} = $_[1] : $_[0]->{descr}; }
+sub card_children ($) { defined $_[1] ? $_[0]->{children} = $_[1] : $_[0]->{children}; }
+
+sub make_card ($$@) {
+ my ($index, $parent,
+ $type, $descr,
+ $serial, $hw_version,
+ $sw_version, $slot_number,
+ $oper_status, $slots) = @_;
+ {
+ idx => $index,
+ parent => $parent,
+ type => $type,
+ descr => $descr,
+ serial => $serial,
+ hw_version => $hw_version,
+ sw_version => $sw_version,
+ slot_number => $slot_number,
+ oper_status => $oper_status,
+ slots => $slots,
+ children => [],
+ };
+}
+
+sub note_card ($$@) {
+ my ($index, $parent, @other_args) = @_;
+ my $card = make_card ($index, $parent, @other_args);
+ $all_cards{$index} = $card;
+ if ($parent) {
+ my $parent_card = $all_cards{$parent};
+ die "Parent card $parent not found"
+ unless defined $parent_card;
+ push @{(card_children ($parent_card))}, $card;
+ } else {
+ push @top_level_cards, $card;
+ }
+ $card;
+}
+
+sub list_cards () {
+ print_cards_table_header ();
+ list_cards_with_children ('', @top_level_cards);
+}
+
+sub list_cards_with_children ($@) {
+ my ($indent, @cards) = @_;
+ foreach my $card (sort { $a->{slot_number} cmp $b->{slot_number} }
+ @cards) {
+ my $index = card_index ($card);
+ my $type = card_type ($card);
+## my $pretty_type = ($pretty_card_type{$type}
+## || $type);
+ printf STDOUT ("%-48s %-4s %8d %4s %5s %2s %2s\n",
+ $indent.card_descr ($card),
+ pretty_card_oper_status ($card->{oper_status}),
+ $card->{serial},
+ $card->{hw_version},
+ ($card->{sw_version} eq 'not available'
+ ? 'n/a' : $card->{sw_version}),
+ pretty_card_slot_number ($card->{slot_number}),
+ pretty_card_nslots ($card->{slots}));
+ list_cards_with_children ($indent.' ',@{card_children $card});
+ }
+}
+
+sub print_cards_table_header () {
+ printf STDOUT ("%-48s %-4s %8s %4s %5s %2s %2s\n",
+ "description",
+ "stat",
+ "serial",
+ "hw",
+ "sw",
+ "sl",
+ "#s");
+ print STDOUT (("=" x 79),"\n");
+}
+
+sub pretty_card_slot_number ($) {
+ my ($slot_number) = @_;
+ if ($slot_number == -1) {
+ return "";
+ } else {
+ return $slot_number;
+ }
+}
+
+sub pretty_card_nslots ($) {
+ my ($nslots) = @_;
+ if ($nslots == -1) {
+ return "?";
+ } elsif ($nslots == 0) {
+ return "";
+ } else {
+ return $nslots;
+ }
+}
+
+sub pretty_card_oper_status ($) {
+ my ($oper_status) = @_;
+ if ($oper_status == 1) {
+ return "-";
+ } elsif ($oper_status == 2) {
+ return "up";
+ } elsif ($oper_status == 3) {
+ return "down";
+ } elsif ($oper_status == 4) {
+ return "stby";
+ } else {
+ return "ILLEGAL".$oper_status;
+ }
+}
diff --git a/test/cisco-tftp.pl b/test/cisco-tftp.pl
new file mode 100755
index 0000000..64c9b1b
--- /dev/null
+++ b/test/cisco-tftp.pl
@@ -0,0 +1,50 @@
+#!/usr/local/bin/perl -w
+
+require 5.002;
+use strict;
+use SNMP_Session;
+use BER;
+
+my %OIDS = (
+ 'netConfigSet' => '1.3.6.1.4.1.9.2.1.50',
+ 'WriteNet' => '1.3.6.1.4.1.9.2.1.55',
+ 'WriteMem' => '1.3.6.1.4.1.9.2.1.54.0',
+ );
+
+my $key;
+foreach $key (keys %OIDS) {
+ my @oid;
+
+ @oid = split (/\./,$OIDS{$key});
+ $OIDS{$key} = \@oid;
+}
+
+my ($router,$community) = ($ARGV[0] || 'popo', $ARGV[1] || "asdjkfhagk");
+
+my $tftphost = "130.59.1.30";
+my $filename = "snmp-test";
+
+sub write_net ($ $ $ ) {
+ my ($session, $tftphost, $filename) = @_;
+
+ my $write_net_oid = encode_oid (@{$OIDS{WriteNet}}, split (/\./,$tftphost));
+ my @enoid = ([$write_net_oid, encode_string ($filename)]);
+
+ #print (join(".",$write_net_oid), "\n");
+ if ($session->set_request_response(@enoid)) {
+ my $response = $session->pdu_buffer;
+ my ($bindings) = $session->decode_get_response ($response);
+ $session->close ();
+ while ($bindings) {
+ my ($binding, $oid, $value);
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ }
+ } else {
+ return (-1,-1);
+ }
+}
+
+my $session = SNMP_Session->open ($router , $community, 161);
+&write_net ($session, $tftphost, $filename);
+1;
diff --git a/test/counter64-test.pl b/test/counter64-test.pl
new file mode 100644
index 0000000..955c3ef
--- /dev/null
+++ b/test/counter64-test.pl
@@ -0,0 +1,201 @@
+#!/usr/local/bin/perl -w
+###
+### Author: Simon Leinen <simon at switch.ch>
+### Date Created: 03-Mar-1999
+###
+### Try to work with Counter64 values
+###
+require 5.003;
+
+use strict;
+
+### Forward declarations
+sub usage ($);
+
+use BER;
+use SNMP_Session "0.67"; # requires map_table_4
+use POSIX; # for exact time
+use Curses;
+use Math::BigInt;
+
+my $version = '1';
+
+my $desired_interval = 5.0;
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-t/) {
+ if ($ARGV[0] eq '-t') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+(\.[0-9]+)?$/) {
+ $desired_interval = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+usage (1) if $#ARGV >= $[;
+
+my $ifDescr = [1,3,6,1,2,1,2,2,1,2];
+my $ifAdminStatus = [1,3,6,1,2,1,2,2,1,7];
+my $ifOperStatus = [1,3,6,1,2,1,2,2,1,8];
+my $ifInOctets = [1,3,6,1,2,1,2,2,1,10];
+my $ifOutOctets = [1,3,6,1,2,1,2,2,1,16];
+my $ifHCInOctets = [1,3,6,1,2,1,31,1,1,1,6];
+my $ifHCOutOctets = [1,3,6,1,2,1,31,1,1,1,10];
+my $ifInUcastPkts = [1,3,6,1,2,1,2,2,1,11];
+my $ifOutUcastPkts = [1,3,6,1,2,1,2,2,1,17];
+
+my $clock_ticks = POSIX::sysconf( &POSIX::_SC_CLK_TCK );
+
+my $win = new Curses;
+
+my %old;
+my $sleep_interval = $desired_interval + 0.0;
+my $interval;
+my $linecount;
+
+sub out_interface {
+ my ($index, $descr, $admin, $oper, $in, $out) = @_;
+ my ($clock) = POSIX::times();
+ my $alarm = 0;
+
+ grep (defined $_ && ($_=pretty_print $_),
+ ($descr, $admin, $oper, $in, $out));
+ $win->clrtoeol ();
+ return unless defined $oper && $oper == 1; # up
+ return unless defined $in && defined $out;
+ if (!defined $old{$index}) {
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %-24s %10s %10s\n",
+ $index,
+ defined $descr ? $descr : '',
+ defined $in ? $in : '-',
+ defined $out ? $out : '-'));
+ } else {
+ my $old = $old{$index};
+
+ $interval = ($clock-$old->{'clock'}) * 1.0 / $clock_ticks;
+ my $d_in = $in ? ("".$in-$old->{'in'})*8000
+ /int ($interval*1000)
+ : 0;
+ my $d_out = $out ? ("".$out-$old->{'out'})*8000
+ /int ($interval*1000)
+ : 0;
+ warn "in: $in out: $out d_in: $d_in d_out: $d_out old->{in}: ",$old->{in}," old->{out}: ",$old->{out};
+ $alarm = ($d_out > 0 && $d_in == 0);
+ print STDERR "\007" if $alarm && !$old->{'alarm'};
+ print STDERR "\007" if !$alarm && $old->{'alarm'};
+ $win->standout() if $alarm;
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %-24s %10.1f %10.1f\n",
+ $index,
+ defined $descr ? $descr : '',
+ defined $in ? $d_in : 0,
+ defined $out ? $d_out : 0));
+ $win->standend() if $alarm;
+ }
+ $old{$index} = {'in' => $in,
+ 'out' => $out,
+ 'clock' => $clock,
+ 'alarm' => $alarm};
+ ++$linecount;
+ $win->refresh ();
+}
+
+$win->erase ();
+my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, 161)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, 161)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+
+### max_repetitions:
+###
+### We try to be smart about the value of $max_repetitions. Starting
+### with the session default, we use the number of rows in the table
+### (returned from map_table_4) to compute the next value. It should
+### be one more than the number of rows in the table, because
+### map_table needs an extra set of bindings to detect the end of the
+### table.
+###
+my $max_repetitions = $session->default_max_repetitions;
+while (1) {
+ $win->addstr (0, 0, sprintf ("%-20s interval %4.1fs %d reps",
+ $host,
+ $interval || $desired_interval,
+ $max_repetitions));
+ $win->standout();
+ $win->addstr (1, 0,
+ sprintf ("%2s %-24s %10s %10s\n",
+ "ix", "name",
+ "bits/s", "bits/s"));
+ $win->addstr (2, 0,
+ sprintf ("%2s %-24s %10s %10s\n",
+ "", "",
+ "in", "out"));
+ $win->clrtoeol ();
+ $win->standend();
+ $linecount = 3;
+ my $calls = $session->map_table_4
+ ([$ifDescr,
+ $ifAdminStatus,
+ $ifOperStatus,
+ $version ne '1' ? $ifHCInOctets : $ifInOctets,
+ $version ne '1' ? $ifHCOutOctets : $ifOutOctets],
+ \&out_interface,
+ $max_repetitions);
+ $max_repetitions = $calls + 1
+ if $calls > 0;
+ $sleep_interval -= ($interval - $desired_interval)
+ if defined $interval;
+ select (undef, undef, undef, $sleep_interval);
+}
+1;
+
+sub usage ($) {
+ warn <<EOM;
+Usage: $0 [-t secs] [-v (1|2c)] switch [community]
+ $0 -h
+
+ -h print this usage message and exit.
+
+ -t secs specifies the sampling interval. Defaults to 5 seconds.
+
+ -v version can be used to select the SNMP version. The default
+ is SNMPv1, which is what most devices support. If your box
+ supports SNMPv2c, you should enable this by passing "-v 2c"
+ to the script. SNMPv2c is much more efficient for walking
+ tables, which is what this tool does.
+
+ switch hostname or IP address of an LS1010 switch
+
+ community SNMP community string to use. Defaults to "public".
+EOM
+ exit (1) if $_[0];
+}
diff --git a/test/cricket-genconf-sensor b/test/cricket-genconf-sensor
new file mode 100755
index 0000000..a3d226d
--- /dev/null
+++ b/test/cricket-genconf-sensor
@@ -0,0 +1,232 @@
+#!/usr/local/bin/perl -w
+##
+## cricket-genconf-sensor
+##
+## Generate Cricket configuration for sensor monitoring
+##
+## Author: Simon Leinen <simon at limmat.switch.ch>
+## Date created: 21-Dec-2006
+##
+## This script generates Cricket configuration files for
+## SNMP-monitorable sensors in a set of routers. It does this on the
+## basis of a RANCID configuration file repository. For each router
+## in that directory that seems to have monitorable sensors, the
+## script calls the `entls' script to generate Cricket configuration.
+##
+## The script puts newly generated configuration files into a
+## temporary directory, and then installs some "safe" configuration
+## changes by itself. For other configuration changes, the user is
+## presented with "diff" output and has to decide how to apply them.
+
+use strict;
+use warnings;
+
+use Cisco::Abbrev;
+
+my $testing = 0;
+
+### Prototypes
+sub read_router_configurations ($ );
+sub has_sensors_p ($ );
+sub postprocess_router_config ($$);
+sub maybe_install_new_configuration ($ );
+sub install_new_configuration ($ );
+
+my $rancid_directory = '/usr/local/rancid/backbone/configs';
+
+my @routers = read_router_configurations ($rancid_directory);
+
+my $cricket_config_dir = '/home/cricket/cricket-config';
+my $old_config_dir = $cricket_config_dir.'/'.'transceiver-monitoring';
+
+-d $old_config_dir or die "cannot find existing configuration $old_config_dir";
+
+my $new_config_dir = '/tmp'.'/foo/';
+-d $new_config_dir
+ or mkdir $new_config_dir
+ or die "Cannot create $new_config_dir: $!";
+
+my (@unchanged, @installed, @unresolved);
+foreach my $router (@routers) {
+ my $routername = $router->{name};
+ ## For testing, only look at one router
+ next if $testing and $routername ne 'swiix2';
+ next unless has_sensors_p ($router);
+ my $rdir = $new_config_dir.'/'.$routername;
+ -d $rdir or mkdir $rdir or die "cannot create directory $rdir: $!";
+ my $retval = system ('perl -Ilib test/entls -t hctiws@'
+ .$routername.':::::2:v4only > '
+ .$rdir.'/'.$routername);
+ if ($retval) {
+ warn "failed to generate configuration for $routername";
+ } else {
+ postprocess_router_config ($rdir.'/'.$routername, $router);
+ maybe_install_new_configuration ($router);
+ }
+}
+print "Unchanged: ",join (", ", map { $_->{name} } @unchanged),"\n";
+print "Installed: ",join (", ", map { $_->{name} } @installed),"\n";
+print "Unresolved: ",join (", ", map { $_->{name} } @unresolved),"\n";
+1;
+
+sub postprocess_router_config ($$) {
+ my ($file, $router) = @_;
+ my ($pre, $sd, $post, $long, $desc);
+ open IN, $file or die "Cannot open configuration file $file: $!";
+ open OUT, ">$file.post" or die "Cannot open configuration file $file.post: $!";
+ while (<IN>) {
+ if (/^(\s*display-name\s*=\s*")(.*)("\s*)$/) {
+ } elsif (/^(\s*long-desc\s*=\s*")(.*)("\s*)$/) {
+ } elsif (/^(\s*short-desc\s*=\s*")(.*)("\s*)$/) {
+ ($pre, $sd, $post) = ($1, $2, $3);
+ $sd =~ s/^transceiver //i;
+ $long = cisco_long_int ($sd);
+ $sd = $long if defined $long;
+ if (defined $long) {
+ $desc = $long;
+ $sd = $desc = $router->{ifdesc}->{$long}
+ if exists $router->{ifdesc}->{$long};
+ } else {
+ $long = $desc = $sd;
+ }
+ chomp $post;
+ print OUT "$pre$sd$post\n";
+ print OUT "\tlong-desc\t = \"<h3>$long - $desc</h3>\"\n";
+ print OUT "\tdisplay-name\t = \"%router% - $long\"\n";
+ } else {
+ print OUT $_;
+ }
+ }
+ close IN or die "Cannot close configuration file $file: $!";
+ close OUT or die "Cannot close configuration file $file.post: $!";
+ rename $file, "$file.pre" or unlink $file;
+ rename "$file.post", $file or rename "$file.pre", $file;
+ unlink "$file.pre";
+ return 1;
+}
+
+sub read_router_configurations ($ ) {
+ my ($dir) = @_;
+ my @routers = ();
+ opendir CONFIG, $dir
+ or die "open directory $dir: $!";
+ foreach my $file (readdir CONFIG) {
+ next if $testing and $file ne 'swiix2';
+ next unless -f $dir.'/'.$file;
+ push @routers, { name => $file };
+ }
+ closedir CONFIG
+ or die "close directory $dir: $!";
+ @routers;
+}
+
+sub has_sensors_p ($ ) {
+ my ($router) = @_;
+ my $routername = $router->{name};
+ my $have_sensor_p = 0;
+ my ($ifname, %ifdesc);
+ open (CONFIG, $rancid_directory.'/'.$routername)
+ or die "open configuration file for $routername: $!";
+ while (<CONFIG>) {
+ if (/Receive Power Sensor/) {
+ $have_sensor_p = 1;
+ } elsif (/^interface (.*)/) {
+ $ifname = $1;
+ } elsif (/^ description (.*)$/
+ and defined $ifname) {
+ $ifdesc{$ifname} = $1;
+ } elsif (/^ /) {
+ } else {
+ $ifname = undef;
+ }
+ }
+ close CONFIG or die "close configuration file for $routername: $!";
+ ## foreach my $ifname (sort keys %ifdesc) {
+ ## printf "%-20s %s\n", $ifname, $ifdesc{$ifname};
+ ## }
+ $router->{ifdesc} = \%ifdesc;
+ return $have_sensor_p;
+}
+
+## maybe_install_new_configuration ROUTER
+##
+## Check whether the newly generated Cricket configuration file for
+## router ROUTER has to/can be installed.
+##
+## If the file doesn't exist in the current configuration, we can
+## safely install the new one.
+##
+## If the newly generated file is identical to the old one, we don't
+## have to do anything.
+##
+## The the newly generated file differs from the old one, we output
+## the diff and don't install anything.
+##
+## TODO: Apply the diffs. This is not totally trivial, however. As
+## long as the diff consists in only added lines, the new file can be
+## safely installed over the current one. But when configuration is
+## lost (i.e. sensors are removed), we should deactivate the lost
+## targets using "collect = 0", rather than removing them entirely, to
+## make sure that history is kept.
+##
+## There is an additional case, namely that a file exists in the
+## current configuration that was not generated in the new run. We
+## don't handle this situation currently.
+##
+## TODO: Check the existing configuration for files that were lost in
+## the new generation run, and deactivate collection in these files.
+##
+## Actual installation of configurations is performed by
+## install_new_configuration().
+##
+sub maybe_install_new_configuration ($ ) {
+ my ($router) = @_;
+ my $routername = $router->{name};
+ my $old_file = $old_config_dir.'/'.$routername.'/'.$routername;
+ my $new_file = $new_config_dir.'/'.$routername.'/'.$routername;
+
+ if (! -f $old_file) {
+ print "NEW: $routername\n";
+ if (install_new_configuration ($router)) {
+ push @installed, $router;
+ } else {
+ push @unresolved, $router;
+ }
+ } else {
+ my $retval = system ("diff", "-uw", $old_file, $new_file);
+ if ($retval) {
+ ##warn "TESTING:\n";
+ ##$retval = system "diff -w $old_file $new_file | egrep -v '\^>'";
+ ##warn "TESTING END: $retval\n";
+ print "DIFFER: $routername\n";
+ push @unresolved, $router;
+ } else {
+ push @unchanged, $router;
+ unlink $new_file;
+ }
+ }
+}
+
+## install_new_configuration ROUTER
+##
+## Install the newly generated configuration file for router ROUTER in
+## the active directory tree. The containing directory is created if
+## needed.
+##
+sub install_new_configuration ($ ) {
+ my ($router) = @_;
+ my $routername = $router->{name};
+ my $old_file = $old_config_dir.'/'.$routername.'/'.$routername;
+ my $new_file = $new_config_dir.'/'.$routername.'/'.$routername;
+ my $old_dir = $old_config_dir.'/'.$routername;
+
+ unless (-d $old_dir || mkdir $old_dir) {
+ warn "Cannot create $old_dir: $!";
+ return undef;
+ }
+ if (system ("mv", $new_file, $old_file)) {
+ warn "Failed to move $new_file to $old_file";
+ return undef;
+ }
+ return 1;
+}
diff --git a/test/d.pl b/test/d.pl
new file mode 100755
index 0000000..aaab3a5
--- /dev/null
+++ b/test/d.pl
@@ -0,0 +1,62 @@
+#!/usr/local/bin/perl -w
+# Minimal useful application of the SNMP package.
+# Author: Simon Leinen <simon at lia.di.epfl.ch>
+# RCS $Header: /home/leinen/CVS/SNMP_Session/test/d.pl,v 1.1.1.1 2003-09-02 20:12:36 leinen Exp $
+######################################################################
+# This application sends a get request for three fixed MIB-2 variable
+# instances (sysDescr.0, sysContact.0 and ipForwarding.0) to a given
+# host. The hostname and community string can be given as
+# command-line arguments.
+######################################################################
+
+require 5;
+
+use SNMP_SessionD;
+use BERD;
+
+$hostname = shift @ARGV || &usage;
+$community = shift @ARGV || 'public';
+&usage if $#ARGV >= 0;
+
+%ugly_oids = qw(sysDescr.0 1.3.6.1.2.1.1.1.0
+ sysContact.0 1.3.6.1.2.1.1.4.0
+ ipForwarding.0 1.3.6.1.2.1.4.1.0
+ );
+foreach (keys %ugly_oids) {
+ $ugly_oids{$_} = encode_oid (split (/\./, $ugly_oids{$_}));
+ $pretty_oids{$ugly_oids{$_}} = $_;
+}
+
+srand();
+die "Couldn't open SNMP session to $hostname"
+ unless ($session = SNMP_SessionD->open ($hostname, $community, 161));
+snmp_get ($session, qw(sysDescr.0 sysContact.0 ipForwarding.0));
+$session->close ();
+1;
+
+sub snmp_get
+{
+ my($session, @oids) = @_;
+ my($response, $bindings, $binding, $value, $oid);
+
+ grep ($_ = $ugly_oids{$_}, @oids);
+
+ if ($session->get_request_response (@oids)) {
+ $response = $session->pdu_buffer;
+ ($bindings) = $session->decode_get_response ($response);
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print $pretty_oids{$oid}," => ",
+ pretty_print ($value), "\n";
+ }
+ } else {
+ warn "Response not received.\n";
+ }
+}
+
+sub usage
+{
+ die "usage: $0 hostname [community]";
+}
diff --git a/test/digital-bug b/test/digital-bug
new file mode 100755
index 0000000..9cca4f1
--- /dev/null
+++ b/test/digital-bug
@@ -0,0 +1,32 @@
+#!/usr/local/bin/perl
+
+#Check for SNMP Values
+
+use BER;
+require 'SNMP_Session.pm';
+
+#Variables
+$host=@ARGV[0] || die "usage";
+$community=@ARGV[1] || 'public';
+$port='161';
+
+$session = SNMP_Session->open ($host, $community, $port)
+ || die "Couldn't open SNMP session to $host";
+
+
+#Oid's
+$oid1=encode_oid (1,3,6,1,2,1,1,5,0);
+
+if ($session->get_request_response ($oid1)){
+ ($bindings) = $session->decode_get_response
+($session->{pdu_buffer});
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ print $pretty_oids{$oid}," => ",
+ &pretty_print ($value), "\n";
+ }
+} else {
+ die "No response from agent on $host";
+}
diff --git a/test/entls b/test/entls
new file mode 100644
index 0000000..fbdf673
--- /dev/null
+++ b/test/entls
@@ -0,0 +1,269 @@
+#!/usr/bin/perl -w
+###
+### entls - list ENTITY-MIB
+###
+### Author: Simon Leinen <simon at switch.ch>
+### Date created: 03-May-2005
+
+use strict;
+use SNMP_util;
+use SNMP_Table;
+
+## Prototypes
+sub init_mibs ();
+sub collect_entity_information ($ );
+sub print_physical ($$);
+sub print_phys_tree ($$);
+sub print_phys_tree_1 ($$@);
+sub get_physical ($ );
+sub decode_truth_value ($ );
+sub snmp_decode_value ($@);
+sub node_is_transceiver_sensor ($ );
+sub router_pretty_name ($ );
+sub router_pretty_name_1 ($ );
+sub usage ();
+
+my @targets = ();
+
+my $phys_tree;
+
+## Set to 1 if you want entPhysicalVendorType to be printed.
+##
+my $print_vendor_type = 0;
+
+## Set to 1 if you want the entPhysical index to be printed.
+##
+my $print_ent_physical_index = 0;
+
+## Set to 1 if you want transceiver sensors listed.
+##
+my $transceiver_sensors = 0;
+
+## Directory in which to find router configurations.
+## Currently, this is only used to find the "canonical" names
+## of routers, using the `hostname' command from the router
+## configuration file.
+##
+my $rancid_base = '/usr/local/rancid/backbone/configs';
+
+my $print_relative_index = 1;
+my $print_hierarchy_indentation = 1;
+
+my $current_router;
+my $current_transceiver;
+
+while (@ARGV) {
+ if ($ARGV[0] eq '-V') {
+ $print_vendor_type = 1;
+ } elsif ($ARGV[0] eq '-i') {
+ $print_ent_physical_index = 1;
+ } elsif ($ARGV[0] eq '-t') {
+ $transceiver_sensors = 1;
+ $print_ent_physical_index = 1;
+ $print_relative_index = 0;
+ $print_hierarchy_indentation = 0;
+ } elsif ($ARGV[0] eq '-h') {
+ usage ();
+ exit (0);
+ } elsif ($ARGV[0] =~ /^-/) {
+ warn ("Unknown option ",$ARGV[0]);
+ usage ();
+ exit (1);
+ } else {
+ push @targets, $ARGV[0];
+ }
+ shift @ARGV;
+}
+usage (), exit 1 unless @targets;
+
+init_mibs ();
+foreach my $target (@targets) {
+ $current_router = $target;
+ $current_router =~ s/.*@//;
+ $current_router =~ s/:.*//;
+ collect_entity_information ($target);
+ print_physical ($current_router, $phys_tree);
+}
+1;
+
+sub collect_entity_information ($ ) {
+ my ($target) = @_;
+ $phys_tree = get_physical ($target);
+}
+
+sub print_physical ($$) {
+ my ($current_router, $phys_tree) = @_;
+ if ($transceiver_sensors) {
+ print "target --default--\n\trouter\t= ",
+ router_pretty_name ($current_router), "\n";
+ print "\tdirectory-desc\t= \"Router %router% transceiver monitoring\"\n";
+ print "\tdisplay-name\t= \"%router% - %short-desc%\"\n";
+ print "\tlong-desc\t= \"<H3>%short-desc%</H3>\"\n";
+ } else {
+ print "Physical Entities\n\n";
+ }
+ print_phys_tree ($phys_tree, "");
+}
+
+sub print_phys_tree ($$) {
+ print_phys_tree_1 ($_[0], $_[1], ());
+}
+
+sub print_phys_tree_1 ($$@) {
+ my ($node, $prefix, @stack) = @_;
+ $node->{parent_stack} = \@stack;
+ printf STDOUT ("%s%s\n", $prefix, $node->tostring ())
+ if !$transceiver_sensors or node_is_transceiver_sensor ($node);
+ foreach my $class (sort keys %{$node->{_children}}) {
+ my $class_children = $node->{_children}->{$class};
+ foreach my $child_index (sort { $a <=> $b } keys %{$class_children}) {
+ print_phys_tree_1 ($class_children->{$child_index},
+ ($print_hierarchy_indentation
+ ? (' 'x length $prefix)
+ : '')
+ .($print_relative_index
+ ? sprintf ("%d. ", $child_index)
+ : ''),
+ ($node, @stack));
+ }
+ }
+}
+
+sub node_is_transceiver_sensor ($ ) {
+ my ($node) = @_;
+ ($node->{vendorType} =~ /1\.3\.6\.1\.4\.1\.9\.12\.3\.1\.8\.(4[6789]|50)$/)
+ ? 1 : 0;
+}
+### get_if_entries TARGET
+###
+### Read the MIB-II interface table, and construct a hash mapping the
+### interface indices to hashes containing important slots.
+### Currently, only ifDescr and ifAlias are recorded.
+###
+sub get_if_entries ($ ) {
+ my ($target) = @_;
+ return snmp_rows_to_objects
+ ($target, 'MIBII::Interface', 'if', qw(descr alias));
+}
+
+sub get_physical ($ ) {
+ my ($target) = @_;
+ my ($phys, $root);
+ $phys = snmp_rows_to_objects
+ ($target, 'Entity::PhysicalEntry', 'entPhysical',
+ qw(descr vendorType containedIn class parentRelPos name
+ hardwareRev firmwareRev softwareRev serialNum mfgName
+ modelName alias assetID isFRU));
+ foreach my $i1 (keys %{$phys}) {
+ my $e1 = $phys->{$i1};
+ if ($e1->{containedIn} == 0) {
+ die "multiple roots" if defined $root;
+ $root = $e1;
+ }
+ foreach my $i2 (keys %{$phys}) {
+ my $e2 = $phys->{$i2};
+ next if $e2->{_visited};
+ if ($i1 == $e2->{containedIn}) {
+ $e1->{_children}->{$e2->{class}}->{$e2->{parentRelPos}} = $e2;
+ $e2->{parent} = $e1;
+ }
+ }
+ }
+ return $root;
+}
+
+sub decode_truth_value ($ ) {return snmp_decode_value ($_[0], qw(1 0));}
+
+sub snmp_decode_value ($@) {
+ my ($index, @mapvec) = @_;
+ return $index if $index < 1 or $index > $#mapvec+1;
+ return $mapvec[$index-1];
+}
+
+sub init_mibs () {
+ snmpmapOID
+ (qw(
+entPhysicalDescr 1.3.6.1.2.1.47.1.1.1.1.2
+entPhysicalVendorType 1.3.6.1.2.1.47.1.1.1.1.3
+entPhysicalContainedIn 1.3.6.1.2.1.47.1.1.1.1.4
+entPhysicalClass 1.3.6.1.2.1.47.1.1.1.1.5
+entPhysicalParentRelPos 1.3.6.1.2.1.47.1.1.1.1.6
+entPhysicalName 1.3.6.1.2.1.47.1.1.1.1.7
+entPhysicalHardwareRev 1.3.6.1.2.1.47.1.1.1.1.8
+entPhysicalFirmwareRev 1.3.6.1.2.1.47.1.1.1.1.9
+entPhysicalSoftwareRev 1.3.6.1.2.1.47.1.1.1.1.10
+entPhysicalSerialNum 1.3.6.1.2.1.47.1.1.1.1.11
+entPhysicalMfgName 1.3.6.1.2.1.47.1.1.1.1.12
+entPhysicalModelName 1.3.6.1.2.1.47.1.1.1.1.13
+entPhysicalAlias 1.3.6.1.2.1.47.1.1.1.1.14
+entPhysicalAssetID 1.3.6.1.2.1.47.1.1.1.1.15
+entPhysicalIsFRU 1.3.6.1.2.1.47.1.1.1.1.16
+));
+}
+
+sub usage () {
+ print "Usage: $0 -h\n";
+ print "Usage: $0 [-V] [-i] [-t] TARGET...\n";
+ print " -V print entities' vendorTypes\n";
+ print " -i print indexes from entPhysicalTable\n";
+ print " -t list transceiver sensors\n";
+}
+
+my %router_pretty_name = ();
+
+sub router_pretty_name ($ ) {
+ my ($router_name) = @_;
+ return $router_pretty_name{$router_name}
+ if exists $router_pretty_name{$router_name};
+ return ($router_pretty_name{$router_name}
+ = router_pretty_name_1 ($router_name));
+}
+
+sub router_pretty_name_1 ($ ) {
+ my ($router_name) = @_;
+ open (CONFIG, $rancid_base.'/'.$router_name)
+ or return $router_name;
+ while (<CONFIG>) {
+ return $1 if /^hostname (.*)$/;
+ }
+ close CONFIG;
+}
+
+package MIBII::Interface;
+package Entity::PhysicalEntry;
+
+my $last_transceiver;
+
+sub tostring ($ ) {
+ my ($node) = @_;
+ my $result = '';
+ my $parent = $node->{parent_stack}->[0];
+ my $current_transceiver = $parent->{name};
+ if (defined $current_transceiver
+ && (!defined $last_transceiver || $current_transceiver ne $last_transceiver)) {
+ my $target = "$current_transceiver";
+ $target = lc $target;
+ $target =~ s/^transceiver //;
+ $target =~ s/[ \/.]/_/g;
+ $target =~ s/^gi/gigabitethernet/g;
+ $target =~ s/^te/tengigabitethernet/g;
+ print "\ntarget $target\n";
+ print "\tshort-desc\t= \"",$current_transceiver,"\"\n";
+ print "\ttarget-type\t= \"monitored-transceiver\"\n";
+ $last_transceiver = $current_transceiver;
+ }
+ if ($transceiver_sensors) {
+ my ($interface, $sensor_type) = split (/ /, $node->{name}, 2);
+ $sensor_type =~ s/[ \/]/-/g;
+ $result = "\t".lc $sensor_type;
+ $result .= sprintf (" = \"%d\"", $node->{index});
+ } else {
+ $result .= sprintf ("%d:", $node->{index})
+ if $print_ent_physical_index;
+ $result .= sprintf ("%s", $node->{name});
+ $result .= " (".$node->{alias}.")" if $node->{alias};
+ $result .= " (".$node->{vendorType}.")"
+ if $print_vendor_type and $node->{vendorType};
+ }
+ return $result;
+}
diff --git a/test/fore-test.pl b/test/fore-test.pl
new file mode 100755
index 0000000..95bcf09
--- /dev/null
+++ b/test/fore-test.pl
@@ -0,0 +1,50 @@
+#!/usr/local/bin/perl -w
+###
+### Small test program that uses GetNext requests to walk a table.
+###
+
+use strict;
+use BER;
+use SNMP_Session;
+
+my $hostname = $ARGV[0] || '193.246.0.134';
+my $community = $ARGV[1] || 'public';
+
+my $session;
+
+die unless ($session = SNMP_Session->open ($hostname, $community, 161));
+
+my @nsapTopoLinkDestPort = split ('\.', '1.3.6.1.4.1.326.2.2.2.1.9.3.1.7');
+
+my $destPortIndex = encode_oid (@nsapTopoLinkDestPort);
+
+my @oids = ($destPortIndex);
+my @next_oids;
+
+my $oid;
+my $i;
+for (;;) {
+ if ($session->getnext_request_response (@oids)) {
+ my $response = $session->pdu_buffer;
+ my ($bindings, $binding, $oid, $value);
+
+ ($bindings) = $session->decode_get_response ($response);
+ @next_oids = ();
+
+ ## IP address
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ last
+ unless BER::encoded_oid_prefix_p ($destPortIndex, $oid);
+ push @next_oids, $oid;
+ print pretty_print ($value), ' [',pretty_print ($oid), "]\n";
+
+ } else {
+ die "No response received.\n";
+ }
+ @oids = @next_oids;
+}
+
+$session->close ();
+
+1;
diff --git a/test/hex-test.pl b/test/hex-test.pl
new file mode 100755
index 0000000..254181e
--- /dev/null
+++ b/test/hex-test.pl
@@ -0,0 +1,61 @@
+#!/usr/local/bin/perl -w
+######################################################################
+### Name: hex-test.pl
+### Date Created: Mon Sep 22 21:15:06 1997
+### Author: Simon Leinen <simon at switch.ch>
+### RCS $Id: hex-test.pl,v 1.1 1997-09-22 19:25:19 simon Exp $
+######################################################################
+### Test the new `hex_string' subroutine.
+######################################################################
+
+require 5;
+
+require 'SNMP_Session.pm';
+use BER;
+
+$hostname = shift @ARGV || &usage;
+$community = shift @ARGV || 'public';
+&usage if $#ARGV >= 0;
+
+%ugly_oids = qw(sysDescr.0 1.3.6.1.2.1.1.1.0
+ sysContact.0 1.3.6.1.2.1.1.4.0
+ ipForwarding.0 1.3.6.1.2.1.4.1.0
+ );
+foreach (keys %ugly_oids) {
+ $ugly_oids{$_} = encode_oid (split (/\./, $ugly_oids{$_}));
+ $pretty_oids{$ugly_oids{$_}} = $_;
+}
+
+srand();
+die "Couldn't open SNMP session to $hostname"
+ unless ($session = SNMP_Session->open ($hostname, $community, 161));
+snmp_get ($session, qw(sysDescr.0 sysContact.0 ipForwarding.0));
+$session->close ();
+1;
+
+sub snmp_get
+{
+ my($session, @oids) = @_;
+ my($response, $bindings, $binding, $value, $oid);
+
+ grep ($_ = $ugly_oids{$_}, @oids);
+
+ if ($session->get_request_response (@oids)) {
+ $response = $session->pdu_buffer;
+ ($bindings) = $session->decode_get_response ($response);
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print $pretty_oids{$oid}," => ",
+ &hex_string ($value), "\n";
+ }
+ } else {
+ warn "Response not received.\n";
+ }
+}
+
+sub usage
+{
+ die "usage: $0 hostname [community]";
+}
diff --git a/test/inexist.pl b/test/inexist.pl
new file mode 100755
index 0000000..5e26f1d
--- /dev/null
+++ b/test/inexist.pl
@@ -0,0 +1,62 @@
+#!/usr/local/bin/perl -w
+# Minimal useful application of the SNMP package.
+# Author: Simon Leinen <simon at lia.di.epfl.ch>
+# RCS $Header: /home/leinen/CVS/SNMP_Session/test/inexist.pl,v 1.1.1.1 2003-09-02 20:12:36 leinen Exp $
+######################################################################
+# This application sends a get request for three fixed MIB-2 variable
+# instances (sysDescr.0, sysContact.0 and ipForwarding.0) to a given
+# host. The hostname and community string can be given as
+# command-line arguments.
+######################################################################
+
+require 5;
+
+require 'SNMP_Session.pm';
+use BER;
+
+$SNMP_Session::suppress_warnings = 1;
+
+$hostname = shift @ARGV || &usage;
+$community = shift @ARGV || 'public';
+&usage if $#ARGV >= 0;
+
+%ugly_oids = qw(foo.0 1.3.6.1.2.1.1.0.0
+ );
+foreach (keys %ugly_oids) {
+ $ugly_oids{$_} = encode_oid (split (/\./, $ugly_oids{$_}));
+ $pretty_oids{$ugly_oids{$_}} = $_;
+}
+
+srand();
+die "Couldn't open SNMP session to $hostname"
+ unless ($session = SNMP_Session->open ($hostname, $community, 161));
+snmp_get ($session, qw(foo.0));
+$session->close ();
+1;
+
+sub snmp_get
+{
+ my($session, @oids) = @_;
+ my($response, $bindings, $binding, $value, $oid);
+
+ grep ($_ = $ugly_oids{$_}, @oids);
+
+ if ($session->get_request_response (@oids)) {
+ $response = $session->pdu_buffer;
+ ($bindings) = $session->decode_get_response ($response);
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ print $pretty_oids{$oid}," => ",
+ pretty_print ($value), "\n";
+ }
+ } else {
+ warn "SNMP problem: $SNMP_Session::errmsg\n";
+ }
+}
+
+sub usage
+{
+ die "usage: $0 hostname [community]";
+}
diff --git a/test/ip-addr-to-if-index b/test/ip-addr-to-if-index
new file mode 100755
index 0000000..59c984c
--- /dev/null
+++ b/test/ip-addr-to-if-index
@@ -0,0 +1,141 @@
+#!/usr/local/bin/perl -w
+## Name: test/ip-addr-to-if-index
+## Author: Simon Leinen <simon at switch.ch>
+## Description: Find ifIndex for given IP address
+######################################################################
+## Usage: test/ip-addr-to-if-index [-q] community at hostname ipaddr...
+##
+## This script finds the interface indices corresponding to the given
+## IP addresses on the node.
+##
+## Example:
+##
+## $ ./test/ip-addr-to-if-index public at babar 127.0.0.1 130.59.4.2
+## 127.0.0.1 lo0 1
+## 130.59.4.2 hme0 2
+##
+## The `-q' form suppresses the IP addresses and interface
+## description:
+##
+## $ ./test/ip-addr-to-if-index -q public at babar 127.0.0.1 130.59.4.2
+## 1
+## 2
+######################################################################
+
+require 5.002;
+use strict;
+use SNMP_Session;
+use BER;
+
+sub usage();
+sub ether_hex($ );
+sub ifDescr($ $ );
+
+my ($hostname,$community,$quiet);
+$quiet = 0;
+while ($ARGV[0] =~ /^-/) {
+ if ($ARGV[0] eq '-q') {
+ $quiet = 1;
+ shift @ARGV;
+ } else {
+ usage ();
+ }
+}
+$hostname = shift @ARGV || usage();
+($community,$hostname) = ($hostname =~ /^(.*)@(.*)$/);
+
+my $session;
+
+die "Couldn't open SNMP session to $hostname"
+ unless ($session = SNMP_Session->open ($hostname, $community, 161));
+
+my $ip_address;
+while ($ip_address = shift @ARGV) {
+ my ($ip1,$ip2,$ip3,$ip4);
+ usage()
+ unless (($ip1,$ip2,$ip3,$ip4)
+ = $ip_address =~ m/^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/);
+
+ my @ifIndex_OID = qw(1 3 6 1 2 1 4 20 1 2);
+ push @ifIndex_OID, $ip1;
+ push @ifIndex_OID, $ip2;
+ push @ifIndex_OID, $ip3;
+ push @ifIndex_OID, $ip4;
+
+ my @oids = (encode_oid (@ifIndex_OID));
+
+ if ($session->get_request_response (@oids)) {
+ my $response = $session->pdu_buffer;
+ my ($bindings, $binding, $oid, $value);
+ my ($ifIndex);
+
+ ($bindings) = $session->decode_get_response ($response);
+
+ ## IfIndex
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ $ifIndex = pretty_print ($value);
+
+ if (!$quiet) {
+ printf STDOUT ("%-15s %-10s ", $ip_address,
+ &ifDescr ($ifIndex, $session));
+ }
+ printf STDOUT ("%d\n", $ifIndex);
+
+ } else {
+ die "No response received.\n";
+ }
+}
+
+$session->close ();
+
+1;
+
+sub usage ()
+{
+ die "usage: $0 community\@hostname ipaddr...";
+}
+
+## ether_hex (HEX_STRING)
+##
+## Converts a raw hex representation into the common form used in
+## Ethernet addresses, e.g. "080020830069" becomes
+## "08:00:20:83:00:69".
+##
+sub ether_hex ($ )
+{
+ my ($string) = @_;
+ $string =~ s/([0-9a-f][0-9a-f])/$1:/g;
+ $string =~ s/:$//;
+ $string;
+}
+
+my %ifDescrCache;
+
+## ifDescr (IFINDEX, SESSION)
+##
+## Return the interface description associated with the given
+## IFINDEX. Uses SESSION as the destination for SNMP request.
+## Results are cached in %ifDescrCache to avoid sending the same SNMP
+## request more than once.
+##
+sub ifDescr($ $ )
+{
+ my @ifDescr = split ('\.','1.3.6.1.2.1.2.2.1.2');
+ my ($ifIndex, $session) = @_;
+
+ return $ifDescrCache{$ifIndex,$session}
+ if defined ($ifDescrCache{$ifIndex,$session});
+ push @ifDescr,$ifIndex;
+ if ($session->get_request_response (encode_oid (@ifDescr))) {
+ my $response = $session->pdu_buffer;
+ my ($bindings, $binding, $oid, $value);
+
+ ($bindings) = $session->decode_get_response ($response);
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ return $ifDescrCache{$ifIndex,$session} = pretty_print ($value);
+ } else {
+ return "if#".$ifIndex;
+ }
+}
diff --git a/test/lambda-monitor.pl b/test/lambda-monitor.pl
new file mode 100644
index 0000000..90d5778
--- /dev/null
+++ b/test/lambda-monitor.pl
@@ -0,0 +1,169 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use SNMP_Session;
+use SNMP_util;
+
+sub show_light_trail (@);
+sub show_bitmap ($$);
+sub print_html_header ();
+sub print_html_trailer ();
+
+my $html_file = "/opt/www/htdocs/lan/switchlambda/status.html";
+
+my $inverse_video = 0;
+
+snmpmapOID (qw(amplifierOperStatus 1.3.6.1.4.1.2522.1.5.1.1.0
+ amplifierLastChange 1.3.6.1.4.1.2522.1.5.1.2.0
+ amplifierGain-dB 1.3.6.1.4.1.2522.1.5.1.3.0
+ inputPowerStatus 1.3.6.1.4.1.2522.1.5.1.4.0
+ inputPowerLevel 1.3.6.1.4.1.2522.1.5.1.5.0
+ outputPowerStatus 1.3.6.1.4.1.2522.1.5.1.6.0
+ outputPowerLevel 1.3.6.1.4.1.2522.1.5.1.7.0));
+
+my @eastbound_amps = qw(public at muxCE1-A8
+ public at muxLS1-A1
+ public at muxLS1-A8
+ public at muxBE1-A1
+ public at muxBE1-A8
+ public at muxBA1-A1
+ public at muxBA1-A8
+ public at muxEZ1-A1);
+
+
+my @westbound_amps = qw(
+ public at muxEZ1-A2
+ public at muxBA1-A7
+ public at muxBA1-A2
+ public at muxBE1-A7
+ public at muxBE1-A2
+ public at muxLS1-A7
+ public at muxLS1-A2
+ );
+
+my @amps = (@eastbound_amps, @westbound_amps);
+
+for (;;) {
+ open (HTML, ">$html_file.new");
+ print_html_header ();
+ my $localtime = localtime();
+ print "",$localtime,"\n";
+
+ print HTML "<p> Last updated: $localtime </p>\n";
+
+ print "\nEastbound:\n";
+ print HTML "<table width=\"100%\"><tr><td align=\"left\" valign=\"top\" width=\"45%\"><h2> Eastbound </h2>\n";
+ show_light_trail (@eastbound_amps);
+
+ print HTML "</td><td align=\"right\" valign=\"top\" width=\"45%\"><h2> Westbound </h2>";
+ print "\nWestbound:\n";
+ show_light_trail (@westbound_amps);
+
+ print HTML "</td></tr></table>\n";
+ print "-"x 75,"\n";
+ print_html_trailer ();
+ close (HTML);
+ rename ($html_file.".new",$html_file);
+ sleep (292);
+}
+1;
+
+sub show_light_trail (@) {
+ my @amps = @_;
+ printf "%-16s%-8s%-8s\n", "node", " in pwr", " out pwr";
+ printf "%-16s%-8s%-8s\n", "name", " dBm", " dBm";
+
+ print HTML "<table>\n <tr>\n <th>Amplifier</th>\n <th>Input<br>Power<br>(dBm)</th>\n <th>Output<br>Power<br>(dBm)</th>\n </tr>\n";
+ foreach my $amp (@amps) {
+ my ($community,$nodename) = split (/@/,$amp);
+ my ($amp_status,
+ $in_status, $in,
+ $out_status, $out)
+ = snmpget ($amp, qw(amplifierOperStatus
+ inputPowerStatus inputPowerLevel
+ outputPowerStatus outputPowerLevel));
+ printf "%-16s%8.2f%8.2f\t%-8s%-8s%-8s\n",
+ $nodename, $in, $out,
+ show_bitmap ($amp_status, 5),
+ pretty_power_status ($in_status),
+ pretty_power_status ($out_status);
+ my ($amp_class, $in_class, $out_class)
+ = (class_for_amp_status ($amp_status),
+ class_for_level_status ($in_status),
+ class_for_level_status ($out_status));
+ print HTML "<tr><td class=\"$amp_class\">$nodename</td><td class=\"$in_class\">$in</td><td class=\"$out_class\">$out</td></tr>\n";
+ }
+ print HTML "</table>\n";
+}
+
+sub show_bitmap ($$) {
+ my ($bits,$n) = @_;
+ my ($k,$result);
+ for ($k = 0, $result = ''; $k < $n; ++$k) {
+ $result .= (($bits & (1<<$k)) ? '*' : ' ');
+ }
+ return $result;
+}
+
+sub pretty_power_status ($) {
+ return (qw(???? ---- minr majr CRIT))[$_[0]];
+}
+
+sub class_for_level_status ($) {
+ return (qw(weird normal minor major critical))[$_[0]];
+}
+
+sub class_for_amp_status ($) {
+ return 'failed' if $_[0] & 1<<4;
+ return 'critical' if $_[0] & 1<<3;
+ return 'major' if $_[0] & 1<<2;
+ return 'minor' if $_[0] & 1<<1;
+ return 'normal' if $_[0] & 1<<0;
+ return 'normal';
+}
+
+sub print_html_header () {
+ my $expires = time + 300;
+ my $expire_string = http_date_string ($expires);
+ my ($c_bg, $c_normal, $c_minor, $c_major, $c_critical, $c_failed)
+ = $inverse_video
+ ? qw(#000000 #ffffff #ff8080 #ff4040 #ff0000 #ff8000)
+ : qw(#ffffff #000000 #804040 #ff4040 #ff0000 #ff8000);
+ print HTML <<EOM;
+<html><head>
+<title>SWITCHlambda Amplifier Status</title>
+<meta http-equiv="Refresh" content="300">
+<meta http-equiv="Expires" content="$expire_string">
+<style type="text/css">
+.normal {color: $c_normal}
+.minor {color: $c_minor}
+.major {color: $c_major}
+.critical {color: $c_critical; font-weight: bold}
+.failed {color: $c_failed; font-weight: bold; font-style: italic}
+</style>
+</head><body bgcolor="$c_bg" text="$c_normal">\n<h1>SWITCHlambda Amplifier Status</h1>
+EOM
+}
+
+sub print_html_trailer () {
+ print HTML <<EOM;
+<h3> Color Legend </h3>
+<p><span class="normal">normal</span>
+- <span class="minor">minor</span>
+- <span class="major">major</span>
+- <span class="critical">critical</span>
+- <span class="failed">failed</span></p>
+</body></html>
+EOM
+}
+
+sub http_date_string ($) {
+ my ($time) = @_;
+ my @gmtime = gmtime $time;
+ my ($wday) = (qw(Sun Mon Tue Wed Thu Fri Sat))[$gmtime[6]];
+ my ($month) = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$gmtime[4]];
+ my ($mday, $year, $hour, $min, $sec) = @gmtime[3,5,2,1,0];
+ return sprintf ("%s, %02d %s %04d %02d:%02d:%02d GMT",
+ $wday, $mday, $month, $year+1900, $hour, $min, $sec);
+}
diff --git a/test/lambda-webmon.pl b/test/lambda-webmon.pl
new file mode 100755
index 0000000..d9cd0fe
--- /dev/null
+++ b/test/lambda-webmon.pl
@@ -0,0 +1,208 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use SNMP_Session;
+use SNMP_util;
+
+sub make_amp_html_page ($$);
+sub show_light_trail ($@);
+sub print_html_header ();
+sub print_html_trailer ();
+
+my $html_file = "/opt/www/htdocs/lan/switchlambda/status-new.html";
+my $html_file_iv = "/opt/www/htdocs/lan/switchlambda/status-iv.html";
+
+my $inverse_video = 0;
+
+snmpmapOID (qw(amplifierOperStatus 1.3.6.1.4.1.2522.1.5.1.1.0
+ amplifierLastChange 1.3.6.1.4.1.2522.1.5.1.2.0
+ amplifierGain-dB 1.3.6.1.4.1.2522.1.5.1.3.0
+ inputPowerStatus 1.3.6.1.4.1.2522.1.5.1.4.0
+ inputPowerLevel 1.3.6.1.4.1.2522.1.5.1.5.0
+ outputPowerStatus 1.3.6.1.4.1.2522.1.5.1.6.0
+ outputPowerLevel 1.3.6.1.4.1.2522.1.5.1.7.0
+
+ aType 1.3.6.1.4.1.2522.1.2.1.1.0
+ aTypeValue 1.3.6.1.4.1.2522.1.2.1.2.0
+ aChannel 1.3.6.1.4.1.2522.1.2.1.3.0
+ aTxLaserOn 1.3.6.1.4.1.2522.1.2.1.4.0
+ aTxLaserOutput 1.3.6.1.4.1.2522.1.2.1.5.0
+ aTxLaserTemp 1.3.6.1.4.1.2522.1.2.1.6.0
+ aTxLossOfSignal 1.3.6.1.4.1.2522.1.2.1.7.0
+ aRxOpticalPower 1.3.6.1.4.1.2522.1.2.1.8.0
+ aRxLossOfSignal 1.3.6.1.4.1.2522.1.2.1.9.0
+ aRxAPDBias 1.3.6.1.4.1.2522.1.2.1.10.0
+ bType 1.3.6.1.4.1.2522.1.2.2.1.0
+ bTypeValue 1.3.6.1.4.1.2522.1.2.2.2.0
+ bChannel 1.3.6.1.4.1.2522.1.2.2.3.0
+ bTxLaserOn 1.3.6.1.4.1.2522.1.2.2.4.0
+ bTxLaserOutput 1.3.6.1.4.1.2522.1.2.2.5.0
+ bTxLaserTemp 1.3.6.1.4.1.2522.1.2.2.6.0
+ bTxLossOfSignal 1.3.6.1.4.1.2522.1.2.2.7.0
+ bRxOpticalPower 1.3.6.1.4.1.2522.1.2.2.8.0
+ bRxLossOfSignal 1.3.6.1.4.1.2522.1.2.2.9.0
+ bRxAPDBias 1.3.6.1.4.1.2522.1.2.2.10.0
+));
+
+my @eastbound_amps = qw(public at mCE11-A8
+ public at mLS11-A1
+ public at mLS11-A8
+ public at mBE11-A1
+ public at mBE11-A8
+ public at mBA11-A1
+ public at mBA11-A8
+ public at mEZ11-A1);
+
+my @westbound_amps = qw(public at mEZ11-A2
+ public at mBA11-A7
+ public at mBA11-A2
+ public at mBE11-A7
+ public at mBE11-A2
+ public at mLS11-A7
+ public at mLS11-A2);
+
+my @amps = (@eastbound_amps, @westbound_amps);
+
+for (;;) {
+ my $amp_status = get_amp_status (@amps);
+ $inverse_video = 0;
+ make_amp_html_page ($html_file, $amp_status);
+ $inverse_video = 1;
+ make_amp_html_page ($html_file_iv, $amp_status);
+ sleep (292);
+}
+1;
+
+sub get_amp_status (@) {
+ my (@amps) = @_;
+ my %status = ();
+ foreach my $amp (@amps) {
+ my ($amp_status,
+ $in_status, $in,
+ $out_status, $out)
+ = snmpget ($amp, qw(amplifierOperStatus
+ inputPowerStatus inputPowerLevel
+ outputPowerStatus outputPowerLevel));
+ $status{$amp} = {amp_status => $amp_status,
+ in_status => $in_status,
+ in => $in,
+ out_status => $out_status,
+ out => $out};
+ }
+ return \%status;
+}
+
+sub make_amp_html_page ($$) {
+ my ($out_file, $status) = @_;
+ open (HTML, ">$out_file.new");
+ print_html_header ();
+ my $localtime = localtime();
+
+ print HTML "<table width=\"100%\"><tr><td align=\"left\" valign=\"top\" width=\"45%\"><h2> Eastbound </h2>\n";
+ show_light_trail ($status, @eastbound_amps);
+
+ print HTML "</td><td align=\"right\" valign=\"top\" width=\"45%\"><h2> Westbound </h2>";
+ show_light_trail ($status, @westbound_amps);
+
+ print HTML "</td></tr></table>\n";
+ print HTML "<p> Last updated: $localtime </p>\n";
+ print_html_trailer ();
+ close (HTML);
+ rename ($out_file.".new",$out_file);
+}
+
+sub show_light_trail ($@) {
+ my ($status, @amps) = @_;
+
+ print HTML "<table width=\"90%\">\n <tr>\n <th>Amplifier</th>\n <th>Input<br>Power<br>(dBm)</th>\n <th>Output<br>Power<br>(dBm)</th>\n </tr>\n";
+ foreach my $amp (@amps) {
+ my ($community,$nodename) = split (/@/,$amp);
+ my $values = $status->{$amp};
+ my ($amp_status,
+ $in_status, $in,
+ $out_status, $out)
+ = ($values->{amp_status},
+ $values->{in_status},$values->{in},
+ $values->{out_status},$values->{out});
+ my ($amp_class, $in_class, $out_class)
+ = (class_for_amp_status ($amp_status),
+ class_for_level_status ($in_status),
+ class_for_level_status ($out_status));
+ print HTML "<tr><td class=\"$amp_class\">$nodename</td><td class=\"$in_class\">$in</td><td class=\"$out_class\">$out</td></tr>\n";
+ }
+ print HTML "</table>\n";
+}
+
+sub class_for_level_status ($) {
+ return (qw(weird normal minor major critical))[$_[0]];
+}
+
+sub class_for_amp_status ($) {
+ return 'failed' if $_[0] & 1<<4;
+ return 'critical' if $_[0] & 1<<3;
+ return 'major' if $_[0] & 1<<2;
+ return 'minor' if $_[0] & 1<<1;
+ return 'normal' if $_[0] & 1<<0;
+ return 'offline';
+}
+
+sub print_html_header () {
+ my $expires = time + 300;
+ my $expire_string = http_date_string ($expires);
+ my ($c_bg, $c_normal, $c_offline, $c_minor, $c_major, $c_critical, $c_failed)
+ = $inverse_video
+ ? ('#000000', '#ffffff', '#606060', '#ff7070', '#ff3030', '#ff0000', '#ff8000')
+ : ('#ffffff', '#000000', '#a0a0a0', '#c06060', '#ff4040', '#ff0000', '#ff8000');
+ print HTML <<EOM;
+<html><head>
+<title>SWITCHlambda Amplifier Status</title>
+<meta http-equiv="Refresh" content="300">
+<meta http-equiv="Expires" content="$expire_string">
+<style type="text/css">
+p {font-family: helvetica,arial}
+h1 {font-family: helvetica,arial}
+h2 {font-family: helvetica,arial}
+th {font-family: helvetica,arial}
+td {font-family: lucidatypewriter,courier,helvetica,arial}
+.normal {color: $c_normal; }
+.offline {color: $c_offline; }
+.minor {color: $c_minor; }
+.major {color: $c_major; }
+.critical {color: $c_critical; font-weight: bold; }
+.failed {color: $c_failed; font-weight: bold; font-style: italic; }
+.trailer {font-size: 70%; }
+</style>
+</head><body bgcolor="$c_bg" text="$c_normal">\n<h1>SWITCHlambda Amplifier Status</h1>
+EOM
+}
+
+sub print_html_trailer () {
+ print HTML <<EOM;
+
+<p> <b>Color Legend:</b>
+ <span class="normal">normal</span>
+- <span class="offline">offline</span>
+- <span class="minor">minor</span>/<span class="major">major</span>/<span class="critical">critical
+alarm condition</span>
+- <span class="failed">failed</span></p>
+
+<p class="trailer"> Generated by a script using the <a
+href="http://www.switch.ch/misc/leinen/snmp/perl/">SNMP_Session.pl</a>.<br>
+
+Script by <a href="http://www.switch.ch/misc/leinen/">Simon
+Leinen</a>, <a href="http://www.switch.ch/">SWITCH</a>, 2001. </p>
+
+</body></html>
+EOM
+}
+
+sub http_date_string ($) {
+ my ($time) = @_;
+ my @gmtime = gmtime $time;
+ my ($wday) = (qw(Sun Mon Tue Wed Thu Fri Sat))[$gmtime[6]];
+ my ($month) = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec))[$gmtime[4]];
+ my ($mday, $year, $hour, $min, $sec) = @gmtime[3,5,2,1,0];
+ return sprintf ("%s, %02d %s %04d %02d:%02d:%02d GMT",
+ $wday, $mday, $month, $year+1900, $hour, $min, $sec);
+}
diff --git a/test/list-bgp4-neighbors b/test/list-bgp4-neighbors
new file mode 100755
index 0000000..e677699
--- /dev/null
+++ b/test/list-bgp4-neighbors
@@ -0,0 +1,53 @@
+#!/usr/local/bin/perl -w
+
+require 5.002;
+use strict;
+use SNMP_Session "0.59";
+use BER;
+use Socket;
+
+sub usage ();
+
+my $bgpPeerState = [1,3,6,1,2,1,15,3,1,2];
+my $bgpPeerRemoteAs = [1,3,6,1,2,1,15,3,1,9];
+
+my $hostname = $ARGV[0] || usage ();
+my $community = $ARGV[1] || "public";
+
+my $session;
+
+die "Couldn't open SNMP session to $hostname"
+ unless ($session = SNMP_Session->open ($hostname, $community, 161));
+$session->map_table ([$bgpPeerState, $bgpPeerRemoteAs],
+ sub () {
+ my ($index, $state, $as) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($state, $as));
+ my $pretty_state = (qw(? idle connect active
+ opensent openconfirm
+ established))[$state];
+ printf "%-15s %-12s %32s AS%-5s\n",
+ $index, $pretty_state,
+ hostname ($index), $as;
+ });
+$session->close ();
+
+1;
+
+sub pretty_addr ($ ) {
+ my ($addr) = @_;
+ my ($hostname,$aliases,$addrtype,$length, at addrs)
+ = gethostbyaddr (inet_aton ($addr), AF_INET);
+ $hostname ? $hostname." [".$addr."]" : $addr;
+}
+
+sub hostname ($ ) {
+ my ($addr) = @_;
+ my ($hostname,$aliases,$addrtype,$length, at addrs)
+ = gethostbyaddr (inet_aton ($addr), AF_INET);
+ $hostname || "[".$addr."]";
+}
+
+sub usage () {
+ die "usage: $0 host [community]";
+}
diff --git a/test/list-bgp4-table b/test/list-bgp4-table
new file mode 100644
index 0000000..8dc5d14
--- /dev/null
+++ b/test/list-bgp4-table
@@ -0,0 +1,84 @@
+#!/usr/local/bin/perl -w
+
+require 5.002;
+use strict;
+use SNMP_Session "0.59";
+use BER;
+use Socket;
+use Getopt::Long;
+
+sub usage ();
+
+my $snmp_version = '2';
+
+GetOptions ("version=i" => \$snmp_version);
+
+my $bgp4PathAttrASPathSegment = [1,3,6,1,2,1,15,6,1,5];
+
+my $hostname = $ARGV[0] || usage ();
+my $community = $ARGV[1] || "public";
+
+my $session;
+
+die "Couldn't open SNMP session to $hostname"
+ unless ($session =
+ ($snmp_version eq '1' ? SNMP_Session->open ($hostname, $community, 161)
+ : SNMPv2c_Session->open ($hostname, $community, 161)));
+$session->map_table ([$bgp4PathAttrASPathSegment],
+ sub () {
+ my ($index, $as_path_segment) = @_;
+ my ($dest_net, $preflen, $peer)
+ = ($index =~ /([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\.([0-9]+)\.([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/);
+ grep (defined $_ && ($_=pretty_print $_),
+ ($as_path_segment));
+ printf "%-18s %-15s %s\n",
+ $dest_net."/".$preflen, $peer, pretty_as_path ($as_path_segment);
+ });
+$session->close ();
+
+1;
+
+sub pretty_addr ($ ) {
+ my ($addr) = @_;
+ my ($hostname,$aliases,$addrtype,$length, at addrs)
+ = gethostbyaddr (inet_aton ($addr), AF_INET);
+ $hostname ? $hostname." [".$addr."]" : $addr;
+}
+
+sub hostname ($ ) {
+ my ($addr) = @_;
+ my ($hostname,$aliases,$addrtype,$length, at addrs)
+ = gethostbyaddr (inet_aton ($addr), AF_INET);
+ $hostname || "[".$addr."]";
+}
+
+sub pretty_as_path ($ ) {
+ my ($aps) = @_;
+ my $start = 0;
+ my $result = '';
+ while (length ($aps) > $start) {
+ my ($type,$length) = unpack ("CC", substr ($aps, $start, 2));
+ $start += 2;
+ my ($pretty_ases, $next_start) = pretty_ases ($length, $aps, $start);
+ $result .= ($type == 1 ? "SET " : $type == 2 ? "" : "type $type??")
+ .$pretty_ases;
+ $start = $next_start;
+ }
+ $result;
+}
+
+sub pretty_ases ($$$ ) {
+ my ($length, $aps, $start) = @_;
+ my $result = undef;
+ return ('',0) if $length == 0;
+ while ($length-- > 0) {
+ my $as = unpack ("S", substr ($aps, $start, 2));
+ $start += 2;
+ $result = defined $result ? $result." ".$as : $as;
+ }
+ ($result, $start);
+}
+
+sub usage () {
+ die "usage: $0 host [community]";
+}
diff --git a/test/list-ospf-neighbors b/test/list-ospf-neighbors
new file mode 100755
index 0000000..6637704
--- /dev/null
+++ b/test/list-ospf-neighbors
@@ -0,0 +1,47 @@
+#!/usr/local/bin/perl -w
+
+require 5.002;
+use strict;
+use SNMP_Session "0.59";
+use BER;
+use Socket;
+
+sub usage ();
+
+my $ospfNbrIpAddr = [1,3,6,1,2,1,14,10,1,1];
+my $ospfNbrRtrId = [1,3,6,1,2,1,14,10,1,3];
+my $ospfNbrState = [1,3,6,1,2,1,14,10,1,6];
+
+my $hostname = $ARGV[0] || usage ();
+my $community = $ARGV[1] || "public";
+
+my $session;
+
+die "Couldn't open SNMP session to $hostname"
+ unless ($session = SNMP_Session->open ($hostname, $community, 161));
+$session->map_table ([$ospfNbrIpAddr, $ospfNbrRtrId, $ospfNbrState],
+ sub () {
+ my ($index, $addr, $router_id, $state) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($addr, $router_id, $state));
+ my $pretty_state = (qw(? down attempt init twoWay
+ exchangeStart exchange
+ loading full))[$state];
+ print STDOUT (pretty_addr ($addr)," ",
+ pretty_addr ($router_id),
+ " (",$pretty_state,")\n");
+ });
+$session->close ();
+
+1;
+
+sub pretty_addr ($ ) {
+ my ($addr) = @_;
+ my ($hostname,$aliases,$addrtype,$length, at addrs)
+ = gethostbyaddr (inet_aton ($addr), AF_INET);
+ $hostname ? $hostname." [".$addr."]" : $addr;
+}
+
+sub usage () {
+ die "usage: $0 host [community]";
+}
diff --git a/test/look-at-counters.pl b/test/look-at-counters.pl
new file mode 100755
index 0000000..2cf04eb
--- /dev/null
+++ b/test/look-at-counters.pl
@@ -0,0 +1,105 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use SNMP_Session;
+use BER;
+use Time::HiRes qw(gettimeofday tv_interval);
+
+sub usage();
+
+$SNMP_Session::suppress_warnings = 1;
+
+my ($host, $community, $interval, $port, $factor, @OIDS);
+
+$interval = 5;
+$port = 161;
+$factor = 1;
+
+while ($#ARGV >= 0) {
+ $_ = shift @ARGV;
+ if (/^-t$/) {
+ $interval = shift @ARGV;
+ } elsif (/^-p$/) {
+ $port = shift @ARGV;
+ } elsif (/^-b$/) {
+ $factor = 8;
+ } elsif (! defined ($host)) {
+ $host = $_;
+ } elsif (! defined ($community)) {
+ $community = $_;
+ } else {
+ push @OIDS, $_;
+ }
+}
+usage() if !defined $host || !defined $community || !@OIDS;
+
+my @encoded_oids = @OIDS;
+
+grep (($_ = encode_oid (split ('\.',$_)) || die "cannot encode $_"),
+ @encoded_oids);
+
+my $session = SNMP_Session->open ($host, $community, $port)
+ || die "couldn't open SNMP session to $host";
+
+my %last_values;
+my $last_time;
+
+get_initial_values ($session, @encoded_oids)
+ || die "Couldn't get initial values";
+while (1) {
+ sleep $interval;
+ print_value_changes ($session, @encoded_oids);
+}
+$session->close ();
+1;
+
+sub usage() {
+ die "Usage: $0 [-t interval] [-p port] host community OID...";
+}
+
+sub get_initial_values ($@) {
+ my ($session, @encoded_oids) = @_;
+
+ if (!$session->get_request_response (@encoded_oids)) {
+ print STDERR "Request to $host failed: $SNMP_Session::errmsg\n";
+ } else {
+ my $response = $session->pdu_buffer;
+ my ($bindings) = $session->decode_get_response ($response);
+
+ $last_time = [gettimeofday()];
+ while ($bindings ne '') {
+ my $binding;
+ ($binding,$bindings) = decode_sequence ($bindings);
+ my ($oid,$value) = decode_by_template ($binding, "%O%@");
+ grep ($_=pretty_print $_, $oid, $value);
+ $last_values{$oid} = $value;
+ }
+ }
+ 1;
+}
+
+sub print_value_changes ($@) {
+ my ($session, @encoded_oids) = @_;
+ if (!$session->get_request_response (@encoded_oids)) {
+ print STDERR "Request to $host failed: $SNMP_Session::errmsg\n";
+ } else {
+ my $this_time = [gettimeofday()];
+ my $response = $session->pdu_buffer;
+ my ($bindings) = $session->decode_get_response ($response);
+ my $real_interval = tv_interval ($last_time, $this_time);
+ $last_time = $this_time;
+
+ while ($bindings ne '') {
+ my $binding;
+ ($binding,$bindings) = decode_sequence ($bindings);
+ my ($oid,$value) = decode_by_template ($binding, "%O%@");
+ grep ($_=pretty_print $_, $oid, $value);
+ my $diff = $value - $last_values{$oid};
+ printf "%12.2f",$diff/$real_interval*$factor,"\n";
+ $last_values{$oid} = $value;
+ }
+ print "\n";
+ }
+ 1;
+}
diff --git a/test/ls1010-list-vcls b/test/ls1010-list-vcls
new file mode 100755
index 0000000..197b207
--- /dev/null
+++ b/test/ls1010-list-vcls
@@ -0,0 +1,258 @@
+#!/usr/local/bin/perl -w
+###
+### ls1010-list-vcls
+###
+### Author: Simon Leinen <simon at switch.ch>
+### Date Created: 24-Feb-1999
+###
+### Real-time full-screen display of the cell counts on VCLs (Virtual
+### Channel Links) and VPLs (Virtual Path Links) on a Cisco LS1010 ATM
+### switch.
+###
+### Description:
+###
+### Call this script with "-h" to learn about command usage.
+###
+### The script will poll an LS1010's ciscoAtmVplTable and
+### ciscoAtmVclTable (from CISCO-ATM-CONN-MIB) at specified intervals
+### (default is every five seconds).
+###
+### For each VCL except for the standard signaling and ILMI VCs (0/5
+### and 0/16, respectively), a line is written to the terminal which
+### lists the VCL's connection info, as well as the input and output
+### transfer rates, as computed from the deltas of the respective cell
+### counts since the last sample.
+###
+### When a VCL is found to have had only output, but no input traffic
+### in the last sampling interval, it is shown in inverse video. In
+### addition, when a VCL changes state (from normal to inverse or vice
+### versa), a bell character is sent to the terminal.
+###
+### Note that on the very first display, the actual SNMP counter
+### values are displayed. THOSE ABSOLUTE COUNTER VALUES HAVE NO
+### DEFINED SEMANTICS WHATSOEVER. However, in some versions of
+### Cisco's software, the values seem to correspond to the total
+### number of counted items since system boot (modulo 2^32). This can
+### be useful for certain kinds of slowly advancing counters (such as
+### CRC errors, hopefully).
+###
+### The topmost screen line shows the name of the managed node, as
+### well as a few hard-to-explain items I found useful while debugging
+### the script.
+###
+### Please send any patches and suggestions for improvement to the
+### author (see e-mail address above). Hope you find this useful!
+###
+require 5.003;
+
+use strict;
+
+use BER;
+use SNMP_Session "0.67"; # requires map_table_4
+use POSIX; # for exact time
+use Curses;
+
+my $version = '1';
+
+my $desired_interval = 5.0;
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-t/) {
+ if ($ARGV[0] eq '-t') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+(\.[0-9]+)?$/) {
+ $desired_interval = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+usage (1) if $#ARGV >= $[;
+
+my $ciscoAtmVclInCells = [1,3,6,1,4,1,9,10,13,1,2,1,1,13];
+my $ciscoAtmVclOutCells = [1,3,6,1,4,1,9,10,13,1,2,1,1,14];
+my $ciscoAtmVclCrossIfIndex = [1,3,6,1,4,1,9,10,13,1,2,1,1,15];
+my $ciscoAtmVclCrossVpi = [1,3,6,1,4,1,9,10,13,1,2,1,1,16];
+my $ciscoAtmVclCrossVci = [1,3,6,1,4,1,9,10,13,1,2,1,1,17];
+
+my $ciscoAtmVplInCells = [1,3,6,1,4,1,9,10,13,1,1,1,1,12];
+my $ciscoAtmVplOutCells = [1,3,6,1,4,1,9,10,13,1,1,1,1,13];
+my $ciscoAtmVplCrossIfIndex = [1,3,6,1,4,1,9,10,13,1,1,1,1,14];
+my $ciscoAtmVplCrossVpi = [1,3,6,1,4,1,9,10,13,1,1,1,1,15];
+
+my $clock_ticks = POSIX::sysconf( &POSIX::_SC_CLK_TCK );
+
+my $win = new Curses;
+
+my %old;
+my $sleep_interval = $desired_interval + 0.0;
+my $interval;
+my $linecount;
+
+sub out_vpl {
+ my ($index, $xif, $xvpi, $incells, $outcells) = @_;
+ my ($if, $vpi) = split (/\./, $index);
+ grep (defined $_ && ($_=pretty_print $_),
+ ($xif, $xvpi, $incells, $outcells));
+ return out_vxl ($if, $vpi, undef,
+ $xif, $xvpi, undef,
+ $incells, $outcells);
+}
+sub out_vcl {
+ my ($index, $xif, $xvpi, $xvci, $incells, $outcells) = @_;
+ my ($if, $vpi, $vci) = split (/\./, $index);
+ grep (defined $_ && ($_=pretty_print $_),
+ ($xif, $xvpi, $xvci, $incells, $outcells));
+ return out_vxl ($if, $vpi, $vci,
+ $xif, $xvpi, $xvci,
+ $incells, $outcells);
+}
+
+sub out_vxl {
+ my ($if, $vpi, $vci, $xif, $xvpi, $xvci, $incells, $outcells) = @_;
+ my $index = join ('.', $if, $vpi, defined $vci ? $vci : '-');
+
+ my ($clock) = POSIX::times();
+ my $alarm = 0;
+ $win->clrtoeol ();
+ ## Ignore signaling VCs
+ return if defined $vpi && $vpi == 0 && defined $vci && ($vci == 5 || $vci == 16);
+ return if defined $xvpi && $xvpi == 0 && defined $xvci && ($xvci == 5 || $xvci == 16);
+
+ return unless defined $incells && defined $outcells;
+ if (!defined $old{$index}) {
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %3d/%-3s %2d %3d/%-3s %10s %10s\n",
+ $if, $vpi, defined $vci ? $vci : '-',
+ $xif, $xvpi, defined $xvci ? $xvci : '-',
+ $incells, $outcells));
+ } else {
+ my $old = $old{$index};
+
+ $interval = ($clock-$old->{'clock'}) * 1.0 / $clock_ticks;
+ my $d_in = ($incells-$old->{'incells'})*8*53/$interval;
+ my $d_out = ($outcells-$old->{'outcells'})*8*53/$interval;
+ $alarm = (($d_out > 0) && ($d_in == 0));
+ print STDERR "\007" if $alarm && !$old->{'alarm'};
+ print STDERR "\007" if !$alarm && $old->{'alarm'};
+ $win->standout() if $alarm;
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %3d/%-3s %2d %3d/%-3s %10.0f %10.0f\n\n",
+ $if, $vpi, defined $vci ? $vci : '-',
+ $xif, $xvpi, defined $xvci ? $xvci : '-',
+ $d_in, $d_out));
+ $win->standend() if $alarm;
+ }
+ $old{$index} = {'incells' => $incells,
+ 'outcells' => $outcells,
+ 'clock' => $clock,
+ 'alarm' => $alarm};
+ ++$linecount;
+ $win->refresh ();
+}
+
+$win->erase ();
+my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, 161)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, 161)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+
+### max_vcl_repetitions, max_vpl_repetitions:
+###
+### We try to be smart about the value of maxRepetitions. Starting
+### with the session default, we use the number of rows in the table
+### (returned from map_table_4) to compute the next value. It should
+### be one more than the number of rows in the table, because
+### map_table needs an extra set of bindings to detect the end of the
+### table.
+###
+my $max_vcl_repetitions = $session->default_max_repetitions;
+my $max_vpl_repetitions = $session->default_max_repetitions;
+while (1) {
+ $win->addstr (0, 0, sprintf ("%-20s interval %4.1fs %d VCLs %d VPLs",
+ $host,
+ $interval || $desired_interval,
+ $max_vcl_repetitions,
+ $max_vpl_repetitions));
+ $win->standout();
+ $win->addstr (1, 0,
+ sprintf ("%2s %3s/%-3s %2s %3s/%-3s %10s %10s",
+ "if", "VPI", "VCI",
+ "if", "VPI", "VCI",
+ "bits/s", "bits/s"));
+ $win->addstr (2, 0,
+ sprintf ("%2s %3s %-3s %2s %3s %-3s %10s %10s\n",
+ "", "", "", "", "", "",
+ "in", "out",
+ ""));
+ $win->clrtoeol ();
+ $win->standend();
+ $linecount = 3;
+ my $vcls = $session->map_table_4
+ ([$ciscoAtmVclCrossIfIndex, $ciscoAtmVclCrossVpi, $ciscoAtmVclCrossVci,
+ $ciscoAtmVclInCells, $ciscoAtmVclOutCells],
+ \&out_vcl,
+ $max_vcl_repetitions);
+ $max_vcl_repetitions = $vcls + 1
+ if $vcls > 0;
+ my $vpls = $session->map_table_4
+ ([$ciscoAtmVplCrossIfIndex, $ciscoAtmVplCrossVpi,
+ $ciscoAtmVplInCells, $ciscoAtmVplOutCells],
+ \&out_vpl,
+ $max_vpl_repetitions);
+ $max_vpl_repetitions = $vpls + 1
+ if $vpls > 0;
+ $sleep_interval -= ($interval - $desired_interval)
+ if defined $interval;
+ select (undef, undef, undef, $sleep_interval);
+}
+1;
+
+sub usage ($) {
+ warn <<EOM;
+Usage: $0 [-t secs] [-v (1|2c)] switch [community]
+ $0 -h
+
+ -h print this usage message and exit.
+
+ -t secs specifies the sampling interval. Defaults to 5 seconds.
+
+ -v version can be used to select the SNMP version. The default
+ is SNMPv1, which is what most devices support. If your box
+ supports SNMPv2c, you should enable this by passing "-v 2c"
+ to the script. SNMPv2c is much more efficient for walking
+ tables, which is what this tool does.
+
+ switch hostname or IP address of an LS1010 switch
+
+ community SNMP community string to use. Defaults to "public".
+EOM
+ exit (1) if $_[0];
+}
diff --git a/test/make-cisco-products.pl b/test/make-cisco-products.pl
new file mode 100644
index 0000000..d4aee16
--- /dev/null
+++ b/test/make-cisco-products.pl
@@ -0,0 +1,21 @@
+#!/usr/local/bin/perl -w
+### Script to convert CISCO-PRODUCTS-MIB.my to a Perl variable
+### definition for use in e.g. etna:/home/noc/bin/get-cisco-versions.
+### Should be run from time to time when a new MIB arrives on
+### ftp.cisco.com/pub/mibs/v2.
+###
+use strict;
+
+my $source = "/home/leinen/snmp/mibs/cisco/v2/CISCO-PRODUCTS-MIB.my";
+
+open (SRC, $source) || die "open $source: $!";
+print "my %cisco_product_name = (\n";
+while (<SRC>) {
+ my ($product, $octet);
+ next unless ($product, $octet)
+ = /^(.*)\s+OBJECT\s+IDENTIFIER\s*::=\s*{\s*ciscoProducts\s*([0-9]+)\s*}/;
+ print " $octet => \"$product\",\n";
+}
+close (SRC) || warn "close $source: $!";
+print ");\n";
+1;
diff --git a/test/max-list-sessions b/test/max-list-sessions
new file mode 100755
index 0000000..12fe5cf
--- /dev/null
+++ b/test/max-list-sessions
@@ -0,0 +1,130 @@
+#!/usr/local/bin/perl -w
+#
+# max-list-sessions
+#
+# Use ASCEND-SESSION-MIB to print a list of active connections on an
+# Ascend dialin router such as a MAX.
+#
+# Date Created: 1998/06/29
+# Author: Simon Leinen <simon at switch.ch>
+# RCS $Header: /home/leinen/CVS/SNMP_Session/test/max-list-sessions,v 1.5 2000-09-24 18:54:03 leinen Exp $
+#
+use strict;
+
+use SNMP_Session "0.59";
+use BER;
+use Socket;
+
+my $host = shift @ARGV || die;
+my $community = shift @ARGV || die;
+
+my $ssnStatusIndex = [1,3,6,1,4,1,529,12,2,1,1];
+my $ssnStatusValidFlag = [1,3,6,1,4,1,529,12,2,1,2];
+my $ssnStatusUserName = [1,3,6,1,4,1,529,12,2,1,3];
+my $ssnStatusUserIPAddress = [1,3,6,1,4,1,529,12,2,1,4];
+my $ssnStatusUserSubnetMask = [1,3,6,1,4,1,529,12,2,1,5];
+my $ssnStatusCurrentService = [1,3,6,1,4,1,529,12,2,1,6];
+my $ssnStatusCallReferenceNum = [1,3,6,1,4,1,529,12,2,1,7];
+
+my $callStatusIndex = [1,3,6,1,4,1,529,11,2,1,1];
+my $callStatusValidFlag = [1,3,6,1,4,1,529,11,2,1,2];
+my $callStatusStartingTimeStamp = [1,3,6,1,4,1,529,11,2,1,3];
+my $callStatusCallReferenceNum = [1,3,6,1,4,1,529,11,2,1,4];
+my $callStatusDataRate = [1,3,6,1,4,1,529,11,2,1,5];
+my $callStatusSlotNumber = [1,3,6,1,4,1,529,11,2,1,6];
+my $callStatusSlotLineNumber = [1,3,6,1,4,1,529,11,2,1,7];
+my $callStatusSlotChannelNumber = [1,3,6,1,4,1,529,11,2,1,8];
+my $callStatusModemSlotNumber = [1,3,6,1,4,1,529,11,2,1,9];
+my $callStatusModemOnSlot = [1,3,6,1,4,1,529,11,2,1,10];
+my $callStatusIfIndex = [1,3,6,1,4,1,529,11,2,1,11];
+my $callSessionIndex = [1,3,6,1,4,1,529,11,2,1,12];
+my $callStatusType = [1,3,6,1,4,1,529,11,2,1,13];
+my $callStatusXmitRate = [1,3,6,1,4,1,529,11,2,1,14];
+my $callStatusPortType = [1,3,6,1,4,1,529,11,2,1,15];
+
+my %call_to_session = ();
+
+my $session = SNMP_Session->open ($host, $community, 161)
+ || die "Opening SNMP_Session";
+read_sessions ($session);
+read_calls ($session);
+foreach my $call_ref (sort keys %call_to_session) {
+ pretty_session ($call_to_session{$call_ref});
+}
+1;
+
+sub read_calls ($) {
+ my ($session) = @_;
+ $session->map_table ([$callStatusValidFlag,
+ $callStatusCallReferenceNum,
+ $callStatusDataRate],
+ sub {
+ my ($index, $valid, $refno, $rate) = @_;
+ map { defined ($_) && ($_=pretty_print $_) }
+ ($valid, $refno, $rate);
+ return if $valid == 1;
+ my $session = $call_to_session{$refno};
+ return unless defined $session;
+ $session->{'data_rate'} = $rate;
+ });
+}
+
+sub read_sessions ($) {
+ my ($session) = @_;
+ $session->map_table ([$ssnStatusValidFlag,
+ $ssnStatusUserName,
+ $ssnStatusUserIPAddress, $ssnStatusUserSubnetMask,
+ $ssnStatusCurrentService, $ssnStatusCallReferenceNum],
+ sub {
+ my ($index, $valid, $user, $addr, $mask,
+ $service, $call_ref) = @_;
+
+ map (defined $_ && ($_=pretty_print $_),
+ ($valid, $user, $addr, $mask,
+ $service, $call_ref));
+ return if $valid == 1; # invalid
+ $call_to_session{$call_ref}
+ = { 'index' => $index,
+ 'user' => $user,
+ 'addr' => $addr,
+ 'mask' => $mask,
+ 'service' => $service,
+ 'call_ref' => $call_ref,
+ };
+ });
+}
+
+sub pretty_session ($ ) {
+ my ($session) = @_;
+ printf STDOUT ("%2d %-24s %-15s %-15s %6d %s\n",
+ $session->{'index'},
+ defined $session->{user} ? $session->{user} : '',
+ inet_ntoa (pack ("C4",split ('\.',$session->{addr}))),
+ inet_ntoa (pack ("C4",split ('\.',$session->{mask}))),
+ $session->{'data_rate'},
+ pretty_service ($session->{service}));
+}
+
+sub pretty_service ($) {
+ my ($service) = @_;
+ return
+ $service == 1 ? 'none'
+ : $service == 2 ? 'other'
+ : $service == 3 ? 'ppp'
+ : $service == 4 ? 'slip'
+ : $service == 5 ? 'mpp'
+ : $service == 6 ? 'x25'
+ : $service == 7 ? 'combinet'
+ : $service == 8 ? 'frameRelay'
+ : $service == 9 ? 'euraw'
+ : $service == 10 ? 'euui'
+ : $service == 11 ? 'telnet'
+ : $service == 12 ? 'telnetBinary'
+ : $service == 13 ? 'rawTcp'
+ : $service == 14 ? 'terminalServer'
+ : $service == 15 ? 'mp'
+ : $service == 16 ? 'virtualConnect'
+ : $service == 17 ? 'dchannelX25'
+ : $service == 18 ? 'dtpt'
+ : 'unknown('.$service.')';
+}
diff --git a/test/mcount.pl b/test/mcount.pl
new file mode 100755
index 0000000..7c63f11
--- /dev/null
+++ b/test/mcount.pl
@@ -0,0 +1,218 @@
+#!/usr/local/bin/perl -w
+###
+### Author: Simon Leinen <simon at switch.ch>
+### Date Created: 28-Jul-1999
+###
+### Track ipMRouteInterfaceInMcastOctets and
+### ipMRouteInterfaceOutMcastOctets for all multicast interfaces of a
+### router. The router has to support IPMROUTE-MIB according to
+### draft-ietf-idmr-multicast-routmib-10.txt (or an earlier version,
+### but not all versions may include these counters.
+###
+require 5.003;
+
+use strict;
+
+use BER;
+use SNMP_Session "0.67"; # requires map_table_4
+use POSIX; # for exact time
+use Curses;
+
+my $version = '1';
+
+my $desired_interval = 5.0;
+
+my $all_p = 0;
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-t/) {
+ if ($ARGV[0] eq '-t') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+(\.[0-9]+)?$/) {
+ $desired_interval = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-a') {
+ $all_p = 1;
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+usage (1) if $#ARGV >= $[;
+
+my $ifDescr = [1,3,6,1,2,1,2,2,1,2];
+my $ifAdminStatus = [1,3,6,1,2,1,2,2,1,7];
+my $ifOperStatus = [1,3,6,1,2,1,2,2,1,8];
+my $ipMRouteInterfaceTtl = [1,3,6,1,3,60,1,1,4,1,2];
+my $ipMRouteProtocol = [1,3,6,1,3,60,1,1,4,1,3];
+my $ipMRouteRateLimit = [1,3,6,1,3,60,1,1,4,1,4];
+my $ipMRouteInMcastOctets = [1,3,6,1,3,60,1,1,4,1,5];
+my $ipMRouteOutMcastOctets = [1,3,6,1,3,60,1,1,4,1,6];
+my $ipMRouteHCInMcastOctets = [1,3,6,1,3,60,1,1,4,1,7];
+my $ipMRouteHCOutMcastOctets = [1,3,6,1,3,60,1,1,4,1,8];
+
+my $locIfDescr = [1,3,6,1,4,1,9,2,2,1,1,28];
+
+my $clock_ticks = POSIX::sysconf( &POSIX::_SC_CLK_TCK );
+
+my $win = new Curses;
+
+my %old;
+my $sleep_interval = $desired_interval + 0.0;
+my $interval;
+my $linecount;
+
+sub out_multicast_interface {
+ my ($index, $descr, $admin, $oper, $in, $out, $comment) = @_;
+ my ($clock) = POSIX::times();
+ my $alarm = 0;
+
+ grep (defined $_ && ($_=pretty_print $_),
+ ($descr, $admin, $oper, $in, $out, $comment));
+ $win->clrtoeol ();
+ return unless $all_p || defined $oper && $oper == 1; # up
+ return unless defined $in && defined $out;
+
+ if (!defined $old{$index}) {
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %-24s %10s %10s %s\n",
+ $index,
+ defined $descr ? $descr : '',
+ defined $in ? $in : '-',
+ defined $out ? $out : '-',
+ defined $comment ? $comment : ''));
+ } else {
+ my $old = $old{$index};
+
+ $interval = ($clock-$old->{'clock'}) * 1.0 / $clock_ticks;
+ my $d_in = $in ? ($in-$old->{'in'})*8/$interval : 0;
+ my $d_out = $out ? ($out-$old->{'out'})*8/$interval : 0;
+ print STDERR "\007" if $alarm && !$old->{'alarm'};
+ print STDERR "\007" if !$alarm && $old->{'alarm'};
+ $win->standout() if $alarm;
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %-24s %s %s %s\n",
+ $index,
+ defined $descr ? $descr : '',
+ pretty_bps ($in, $d_in),
+ pretty_bps ($out, $d_out),
+ defined $comment ? $comment : ''));
+ $win->standend() if $alarm;
+ }
+ $old{$index} = {'in' => $in,
+ 'out' => $out,
+ 'clock' => $clock,
+ 'alarm' => $alarm};
+ ++$linecount;
+ $win->refresh ();
+}
+
+sub pretty_bps ($$) {
+ my ($count, $bps) = @_;
+ if (! defined $count) {
+ return ' - ';
+ } elsif ($bps > 1000000) {
+ return sprintf ("%8.4f M", $bps/1000000);
+ } elsif ($bps > 1000) {
+ return sprintf ("%9.1fk", $bps/1000);
+ } else {
+ return sprintf ("%10.0f", $bps);
+ }
+}
+
+$win->erase ();
+my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, 161)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, 161)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+
+### max_repetitions:
+###
+### We try to be smart about the value of $max_repetitions. Starting
+### with the session default, we use the number of rows in the table
+### (returned from map_table_4) to compute the next value. It should
+### be one more than the number of rows in the table, because
+### map_table needs an extra set of bindings to detect the end of the
+### table.
+###
+my $max_repetitions = $session->default_max_repetitions;
+while (1) {
+ $win->addstr (0, 0, sprintf ("%-20s interval %4.1fs %d reps",
+ $host,
+ $interval || $desired_interval,
+ $max_repetitions));
+ $win->standout();
+ $win->addstr (1, 0,
+ sprintf ("%2s %-24s %10s %10s %10s %s\n",
+ "ix", "name",
+ "bits/s", "bits/s",
+ "description"));
+ $win->addstr (2, 0,
+ sprintf ("%2s %-24s %10s %10s %10s %s\n",
+ "", "",
+ "in", "out",
+ ""));
+ $win->clrtoeol ();
+ $win->standend();
+ $linecount = 3;
+ my $calls = $session->map_table_4
+ ([$ifDescr,$ifAdminStatus,$ifOperStatus,
+ $ipMRouteInMcastOctets, $ipMRouteOutMcastOctets,
+ $locIfDescr],
+ \&out_multicast_interface,
+ $max_repetitions);
+ $max_repetitions = $calls + 1
+ if $calls > 0;
+ $sleep_interval -= ($interval - $desired_interval)
+ if defined $interval;
+ select (undef, undef, undef, $sleep_interval);
+}
+1;
+
+sub usage ($) {
+ warn <<EOM;
+Usage: $0 [-t secs] [-v (1|2c)] switch [community]
+ $0 -h
+
+ -h print this usage message and exit.
+
+ -t secs specifies the sampling interval. Defaults to 5 seconds.
+
+ -v version can be used to select the SNMP version. The default
+ is SNMPv1, which is what most devices support. If your box
+ supports SNMPv2c, you should enable this by passing "-v 2c"
+ to the script. SNMPv2c is much more efficient for walking
+ tables, which is what this tool does.
+
+ switch hostname or IP address of an LS1010 switch
+
+ community SNMP community string to use. Defaults to "public".
+EOM
+ exit (1) if $_[0];
+}
diff --git a/test/mdebug b/test/mdebug
new file mode 100755
index 0000000..6a6a08d
--- /dev/null
+++ b/test/mdebug
@@ -0,0 +1,142 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use SNMP_Session;
+use BER;
+use Socket;
+
+sub print_mroutes ($$);
+sub pretty_next_hop_state ($);
+
+my $version = '2c';
+my $port = 161;
+my $debug = 0;
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-p/) {
+ if ($ARGV[0] eq '-p') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+$/) {
+ $port = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+my ($group_name, $group, $group_quad);
+my ($source_name, $source, $source_quad);
+$group_name = shift @ARGV || die ("no source/group given");
+if ($#ARGV >= $[) {
+ $source_name = $group_name;
+ $group_name = shift @ARGV;
+}
+usage (1) if $#ARGV >= $[;
+if (! defined ($group = inet_aton ($group_name))) {
+ die ("Cannot parse group $group_name");
+}
+$group_quad = inet_ntoa ($group);
+if (! defined ($source = inet_aton ($source_name))) {
+ die ("Cannot parse source $source_name");
+}
+$source_quad = inet_ntoa ($source);
+
+my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, $port)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, $port)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+print_mroutes ($session, $group);
+1;
+
+sub print_mroutes ($$) {
+ my $ipMRouteUpstreamNeighbor = [1,3,6,1,3,60,1,1,2,1,4];
+ my $ipMRouteInIfIndex = [1,3,6,1,3,60,1,1,2,1,5];
+ my $ipMRouteUpTime = [1,3,6,1,3,60,1,1,2,1,6];
+ my $ipMRouteExpiryTime = [1,3,6,1,3,60,1,1,2,1,7];
+ my $ipMRoutePkts = [1,3,6,1,3,60,1,1,2,1,8];
+ my $ipMRouteDifferentInIfPackets = [1,3,6,1,3,60,1,1,2,1,9];
+ my $ipMRouteOctets = [1,3,6,1,3,60,1,1,2,1,10];
+ my $ipMRouteProtocol = [1,3,6,1,3,60,1,1,2,1,11];
+
+ my $ipMRouteNextHopState = [1,3,6,1,3,60,1,1,3,1,6];
+ my $ipMRouteNextHopProtocol = [1,3,6,1,3,60,1,1,3,1,10];
+ my ($session, $group) = @_;
+ my @group_subids = split (/\./, inet_ntoa ($group), 4);
+ my @oids = ([@{$ipMRouteUpstreamNeighbor}, at group_subids],
+ [@{$ipMRouteProtocol}, at group_subids]);
+ $session->map_table
+ (\@oids,
+ sub () {
+ my ($index, $nbr, $protocol) = @_;
+ my ($source, $mask) =
+ ($index =~ m/^(\d+\.\d+\.\d+\.\d+)\.(\d+\.\d+\.\d+\.\d+)$/);
+ map { $_ = pretty_print $_ if defined $_ }
+ ($nbr, $protocol);
+ print STDOUT ("$source ($mask) ",inet_ntoa ($group),": ",
+ pretty_next_hop_protocol ($protocol),
+ "\n");
+ });
+
+ @oids = ([@{$ipMRouteNextHopState}, at group_subids],
+ [@{$ipMRouteNextHopProtocol}, at group_subids]);
+ $session->map_table
+ (\@oids,
+ sub () {
+ my ($index, $state, $protocol) = @_;
+ my ($source, $mask, $if_index, $nexthop) =
+ ($index =~ m/^(\d+\.\d+\.\d+\.\d+)\.(\d+\.\d+\.\d+\.\d+)\.(\d+)\.(\d+\.\d+\.\d+\.\d+)$/);
+ map { $_ = pretty_print $_ if defined $_ }
+ ($state, $protocol);
+ print STDOUT ("$source ($mask) ",inet_ntoa ($group),": ",
+ pretty_next_hop_state ($state)," ",
+ pretty_next_hop_protocol ($protocol),
+ "\n");
+ });
+}
+
+sub pretty_next_hop_state ($ ) {
+ return "forwarding(1)" if $_[0] eq 1;
+ return "pruned(2)" if $_[0] eq 2;
+ return "???($_[0])";
+}
+
+sub pretty_next_hop_protocol ($ ) {
+ return "other(1)" if $_[0] eq 1;
+ return "local(2)" if $_[0] eq 2;
+ return "netmgmt(3)" if $_[0] eq 3;
+ return "dvmrp(4)" if $_[0] eq 4;
+ return "mospf(5)" if $_[0] eq 5;
+ return "pimSparseDense(6)" if $_[0] eq 6;
+ return "cbt(7)" if $_[0] eq 7;
+ return "pimSparseMode(8)" if $_[0] eq 8;
+ return "pimDenseMode(9)" if $_[0] eq 9;
+ return "igmpOnly(10)" if $_[0] eq 10;
+ return "???($_[0])";
+}
diff --git a/test/mrouted-genconf b/test/mrouted-genconf
new file mode 100755
index 0000000..1981d20
--- /dev/null
+++ b/test/mrouted-genconf
@@ -0,0 +1,114 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+use SNMP_Session "0.58";
+use BER;
+use Socket;
+
+my $mrouters =
+[
+ 'public at it-ws.ten-34.net:9161',
+ 'public at de-ws.ten-34.net',
+ 'public at ch-ws.ten-34.net',
+ 'public at at-ws.ten-34.net',
+ 'public at uk-ws.ten-34.net',
+ ];
+
+## Define this if you want WorkDir set in the generated configuration
+## file.
+##
+#my $work_dir = '/var/tmp/mrtg';
+
+## Define this if you want IconDir set in the generated configuration
+## file.
+##
+#my $icon_dir = '/lan/stat';
+
+## Define this if you want Directory[] set in each target.
+##
+#my $directory = 'ten-34/ipmcast';
+
+## An absolute maximum for traffic rates over tunnels, in Bytes per
+## second. You probably don't need to change this.
+##
+my $abs_max = '100000000';
+
+my $dvmrpInterfaceType = [1,3,6,1,3,62,1,1,3,1,2];
+my $dvmrpInterfaceRemoteAddress = [1,3,6,1,3,62,1,1,3,1,5];
+my $dvmrpInterfaceMetric = [1,3,6,1,3,62,1,1,3,1,7];
+my $dvmrpInterfaceRateLimit = [1,3,6,1,3,62,1,1,3,1,8];
+my $dvmrpInterfaceInOctets = [1,3,6,1,3,62,1,1,3,1,11];
+my $dvmrpInterfaceOutOctets = [1,3,6,1,3,62,1,1,3,1,12];
+
+## Print head of configuration file
+print "WorkDir: $work_dir\n" if defined $work_dir;
+print "IconDir: $icon_dir\n" if defined $icon_dir;
+print "WriteExpires: Yes\nWeekformat[^]: V\nWithPeak[_]: wmy\n";
+
+foreach my $target (@{$mrouters}) {
+ my $session;
+ my $abs_max_bytes = $abs_max >> 3;
+ my ($community, $mrouter, $port);
+
+ if ($target =~ /^(.*)@(.*):([0-9]+)$/) {
+ $community = $1; $mrouter = $2; $port = $3;
+ } elsif ($target =~ /^(.*)@(.*)$/) {
+ $community = $1; $mrouter = $2; $port = 161;
+ } else {
+ warn "Malformed target $target\n";
+ next;
+ }
+ $session = SNMP_Session->open ($mrouter, $community, $port)
+ || (warn ("Opening SNMP session to $mrouter\n"), next);
+
+ my $snmp_target = $community.'@'.$mrouter;
+ $snmp_target .= ":".$port
+ unless $port == 161;
+ print "\n\nTarget[$mrouter-mroutes]:
+ 1.3.6.1.3.62.1.1.9.0&1.3.6.1.3.62.1.1.10.0:$snmp_target\n";
+ print "Directory[$mrouter-mroutes]: $directory\n"
+ if defined $directory;
+ print <<EOM
+MaxBytes[$mrouter-mroutes]: 8000
+AbsMax[$mrouter-mroutes]: 100000
+Options[$mrouter-mroutes]: growright,gauge,nopercent
+ShortLegend[$mrouter-mroutes]: routes
+YLegend[$mrouter-mroutes]: # of routes
+Title[$mrouter-mroutes]: Multicast routes on $mrouter
+PageTop[$mrouter-mroutes]: <hr><H3>Multicast routes on $mrouter</H3>
+Legend1[$mrouter-mroutes]: # reachable DVMRP routes
+Legend2[$mrouter-mroutes]: # DVMRP routing table entries
+Legend3[$mrouter-mroutes]: max # reachable routes
+Legend4[$mrouter-mroutes]: max # routing table entries
+LegendI[$mrouter-mroutes]: reachable
+LegendO[$mrouter-mroutes]: total
+EOM
+ eval {
+ $session->map_table
+ ([$dvmrpInterfaceType, $dvmrpInterfaceRemoteAddress,
+ $dvmrpInterfaceMetric, $dvmrpInterfaceRateLimit], sub
+ {
+ my ($index, $type, $peer_addr, $metric, $rate_limit) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($type, $peer_addr, $metric, $rate_limit));
+ my $rate_limit_bytes = $rate_limit * 1000 >> 3;
+ ## ignore interfaces other than tunnels for now
+ return unless $type == 1;
+ my $peer_name = gethostbyaddr(pack ("C4",split ('\.',$peer_addr)),
+ AF_INET)
+ || $peer_addr;
+ my $graph_name = $mrouter.'-'.$peer_name;
+ print <<EOM;
+
+Target[$graph_name]: 1.3.6.1.3.62.1.1.3.1.11.$index&1.3.6.1.3.62.1.1.3.1.12.$index:$snmp_target
+PageTop[$graph_name]: <hr><H3>Tunnel $mrouter -> $peer_name (metric $metric)</H3>
+Options[$graph_name]: growright,bits
+MaxBytes[$graph_name]: $rate_limit_bytes
+AbsMax[$graph_name]: $abs_max_bytes
+Title[$graph_name]: Mbone Tunnel from $mrouter to $peer_name
+EOM
+ print "Directory[$graph_name]: $directory\n"
+ if $directory;
+ }) };
+ $session->close ();
+}
diff --git a/test/mrtg-ipmcast b/test/mrtg-ipmcast
new file mode 100644
index 0000000..1e79d33
--- /dev/null
+++ b/test/mrtg-ipmcast
@@ -0,0 +1,319 @@
+#!/usr/local/bin/perl -w
+##############################################################################
+### File Name: mrtg-ipmcast
+### Description: Generate MRTG configuration for multicast statistics
+### Author: Simon Leinen <simon at switch.ch>
+### Date Created: 20-Jun-2000
+### RCS $Header: /home/leinen/CVS/SNMP_Session/test/mrtg-ipmcast,v 1.6 2001-03-07 15:38:11 leinen Exp $
+##############################################################################
+### This script can be used to generate a piece of MRTG[1]
+### configuration file for plotting per-interface multicast traffic
+### statistics using IPMROUTE-MIB[2].
+###
+### Usage: mrtg-ipmcast [-d DIR] [-w WORKDIR] [-i ICONDIR]
+### [-c COMMUNITY] [-v ( 1 | 2c )] [-p port] ROUTER1 ROUTER2 ...
+###
+### This will contact all ROUTERs under the specified COMMUNITY and
+### look at some columns of the ipMRouteInterfaceTable. For each rows
+### for which those columns have defined values, an MRTG target
+### definition will be written. Such a target definition might look
+### like this:
+###
+### Target[swice1-multicast-atm4-0-0.6]: 1.3.6.1.3.60.1.1.4.1.5.66&1.3.6.1.3.60.1.1.4.1.6.66:secret at swiCE1.switch.ch
+### MaxBytes[swice1-multicast-atm4-0-0.6]: 19375000
+### AbsMax[swice1-multicast-atm4-0-0.6]: 19375000
+### Options[swice1-multicast-atm4-0-0.6]: growright,bits
+### Title[swice1-multicast-atm4-0-0.6]: Multicast Traffic on swiCE1.switch.ch:ATM4/0/0.6-aal5 layer (ATM PVC to swiZHX)
+### PageTop[swice1-multicast-atm4-0-0.6]: <hr><H3>Multicast Traffic on swiCE1.switch.ch:ATM4/0/0.6-aal5 layer (ATM PVC to swiZHX)</H3>
+### Directory[swice1-multicast-atm4-0-0.6]: multicast
+###
+### The OIDs are ipMRouteInterfaceInMcastOctets and
+### ipMRouteInterfaceOutMcastOctets indexed for the interface. In the
+### example, the interface has index 66, and the ifName is
+### "ATM4/0/0.6".
+###
+### "AbsMax" is taken from the ifSpeed value for the interface.
+###
+### "MaxBytes" is set to the multicast rate limit as per
+### ipMRouteInterfaceRateLimit. If no rate-limit is specified, the
+### same value as for AbsMax will be used.
+###
+### "Directory" is only defined if the "-d DIR" option has been passed
+### to the script. In the example, the script has been called with
+### "-d multicast".
+###
+### The "-w" and "-i" options can be used to cause WorkDir and IconDir
+### definitions to be generated, respectively.
+##############################################################################
+
+use strict;
+use SNMP_Session "0.58";
+use BER;
+use Socket;
+
+## Forward declarations
+sub usage ($ );
+
+### If set, a Directory[] attribute pointing to this directory will be
+### included for every target in the generated configuration.
+my $directory;
+
+## Define this if you want WorkDir set in the generated configuration
+## file.
+##
+my $work_dir;
+
+## Define this if you want IconDir set in the generated configuration
+## file.
+##
+my $icon_dir;
+
+## An absolute maximum for traffic rates over tunnels, in Bytes per
+## second. You probably don't need to change this.
+##
+my $abs_max = '100000000';
+
+my $mrouters = [];
+
+my $version = '1';
+
+my $community = 'public';
+
+my $port = 161;
+
+while (@ARGV) {
+ if ($ARGV[0] =~ /^-h/) {
+ usage (0);
+ exit (0);
+ } elsif ($ARGV[0] =~ /^-p/) {
+ if ($ARGV[0] eq '-p') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+$/) {
+ $port = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ $version = '2c' if ($ARGV[0] eq '2');
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } elsif ($ARGV[0] eq '2') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-c') {
+ shift @ARGV;
+ usage (1) unless @ARGV;
+ $community = $ARGV[0];
+ } elsif ($ARGV[0] eq '-d') {
+ shift @ARGV;
+ usage (1) unless @ARGV;
+ $directory = $ARGV[0];
+ } elsif ($ARGV[0] eq '-w') {
+ shift @ARGV;
+ usage (1) unless @ARGV;
+ $work_dir = $ARGV[0];
+ } elsif ($ARGV[0] eq '-i') {
+ shift @ARGV;
+ usage (1) unless @ARGV;
+ $icon_dir = $ARGV[0];
+ } else {
+ push @{$mrouters}, "$community\@$ARGV[0]:$port";
+ }
+ shift @ARGV;
+}
+
+my %pretty_protocol_name =
+(
+ 1 => "other",
+ 2 => "local",
+ 3 => "netmgmt",
+ 4 => "dvmrp",
+ 5 => "mospf",
+ 6 => "pimSparseDense",
+ 7 => "cbt",
+ 8 => "pimSparseMode",
+ 9 => "pimDenseMode",
+ 10 => "igmpOnly",
+ 11 => "bgmp",
+ 12 => "msdp",
+);
+
+my $ipMRouteInterfaceTtl = [1,3,6,1,3,60,1,1,4,1,2];
+my $ipMRouteInterfaceProtocol = [1,3,6,1,3,60,1,1,4,1,3];
+my $ipMRouteInterfaceRateLimit = [1,3,6,1,3,60,1,1,4,1,4];
+my $ipMRouteInterfaceInMcastOctets = [1,3,6,1,3,60,1,1,4,1,5];
+my $ipMRouteInterfaceOutMcastOctets = [1,3,6,1,3,60,1,1,4,1,6];
+
+## Print head of configuration file
+print "WorkDir: $work_dir\n" if defined $work_dir;
+print "IconDir: $icon_dir\n" if defined $icon_dir;
+print "WriteExpires: Yes\nWeekformat[^]: V\nWithPeak[_]: wmy\n";
+
+foreach my $target (@{$mrouters}) {
+ my $session;
+ my ($community, $mrouter, $port);
+
+ if ($target =~ /^(.*)@(.*):([0-9]+)$/) {
+ $community = $1; $mrouter = $2; $port = $3;
+ } elsif ($target =~ /^(.*)@(.*)$/) {
+ $community = $1; $mrouter = $2; $port = 161;
+ } else {
+ warn "Malformed target $target\n";
+ next;
+ }
+ $session =
+ ($version eq '1' ? SNMPv1_Session->open ($mrouter, $community, $port)
+ : $version eq '2c' ? SNMPv2c_Session->open ($mrouter, $community, $port)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session to router $mrouter";
+
+ my $if_table = $session->get_if_table;
+
+ my $snmp_target = $community.'@'.$mrouter;
+ $snmp_target .= ":".$port
+ unless $port == 161;
+## eval
+ {
+ $session->map_table
+ ([$ipMRouteInterfaceTtl,
+ $ipMRouteInterfaceProtocol,
+ $ipMRouteInterfaceRateLimit], sub
+ {
+ my ($index, $ttl, $protocol, $rate_limit) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($protocol, $ttl, $rate_limit));
+ my ($if_entry, $abs_max_bytes, $rate_limit_bytes,
+ $interface, $mr, $graph_name);
+ die unless defined ($if_entry = $if_table->{$index});
+ if (defined $if_entry->{ifSpeed}) {
+ if ($rate_limit == 0 || $if_entry->{ifSpeed} < $rate_limit) {
+ $rate_limit = $if_entry->{ifSpeed} / 1000;
+ }
+ $abs_max_bytes = $if_entry->{ifSpeed} >> 3
+ if defined $if_entry->{ifSpeed};
+ } else {
+ }
+ $abs_max_bytes = $abs_max >> 3
+ unless defined $abs_max_bytes;
+ $rate_limit_bytes = $rate_limit * 1000 >> 3;
+
+ $protocol = $pretty_protocol_name{$protocol}
+ if exists $pretty_protocol_name{$protocol};
+## my $peer_name = gethostbyaddr(pack ("C4",split ('\.',$peer_addr)),
+## AF_INET)
+## || $peer_addr;
+ my $peer_name = "?";
+ $interface = $index;
+ if (defined ($if_entry->{ifDescr})) {
+ $interface = $if_entry->{ifDescr};
+ }
+ print STDERR "IF $interface TTL $ttl $protocol\n";
+ $mr = $mrouter;
+ $mr =~ s/\..*//;
+ $graph_name = lc ($mr.'-multicast-'.cleanup ($interface));
+ if (defined ($if_entry->{ifAlias}) && $if_entry->{ifAlias} ne '') {
+ $interface .= " (".$if_entry->{ifAlias}.")";
+ } elsif (defined ($if_entry->{locIfDescr}) && $if_entry->{locIfDescr} ne '') {
+ $interface .= " (".$if_entry->{locIfDescr}.")";
+ }
+ print <<EOM;
+
+Target[$graph_name]: 1.3.6.1.3.60.1.1.4.1.5.$index&1.3.6.1.3.60.1.1.4.1.6.$index:$snmp_target
+MaxBytes[$graph_name]: $rate_limit_bytes
+AbsMax[$graph_name]: $abs_max_bytes
+Options[$graph_name]: growright,bits
+Title[$graph_name]: Multicast Traffic on $mrouter:$interface
+PageTop[$graph_name]: <hr><H3>Multicast Traffic on $mrouter:$interface</H3>
+EOM
+ print "Directory[$graph_name]: $directory\n"
+ if $directory;
+ })
+};
+ $session->close ();
+}
+
+sub cleanup ($ ) {
+ local ($_) = @_;
+ s@/@- at g;
+ s at -(aal5|cef) layer$@@;
+ $_;
+}
+
+sub usage ($) {
+ warn <<EOM;
+Usage: $0 [-w workdir] [-i icondir] [-v (1|2c)] [-p port] [-c community] hostname...
+ $0 -h
+
+ -h print this usage message and exit.
+
+ -w workdir specifies the WorkDir parameter for the generated MRTG
+ configuration file.
+
+ -i icondir specifies the IconDir parameter for the generated MRTG
+ configuration file.
+
+ -v version can be used to select the SNMP version. The default
+ is SNMPv1, which is what most devices support. If your box
+ supports SNMPv2c, you should enable this by passing "-v 2c"
+ to the script. SNMPv2c is much more efficient for walking
+ tables, which is what this tool does.
+
+ -p port can be used to specify a non-standard UDP port of the SNMP
+ agent (the default is UDP port 161).
+
+ -c community SNMP community string to use. Defaults to "public".
+
+ hostname... hostnames or IP addresses of multicast routers
+EOM
+ exit (1) if $_[0];
+}
+
+package SNMP_Session;
+
+sub get_if_table ($) {
+ my ($session) = @_;
+
+ my $result = {};
+
+ my $ifDescr = [1,3,6,1,2,1,2,2,1,2];
+ my $ifSpeed = [1,3,6,1,2,1,2,2,1,5];
+ my $locIfDescr = [1,3,6,1,4,1,9,2,2,1,1,28];
+ my $ifAlias = [1,3,6,1,2,1,31,1,1,1,18];
+ $session->map_table ([$ifDescr,$ifSpeed],
+ sub ($$$) {
+ my ($index, $ifDescr, $ifSpeed) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($ifDescr, $ifSpeed));
+ $result->{$index} = {'ifDescr' => $ifDescr,
+ 'ifSpeed' => $ifSpeed};
+ });
+ $session->map_table ([$locIfDescr],
+ sub ($$$) {
+ my ($index, $locIfDescr) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($locIfDescr));
+ $result->{$index}->{'locIfDescr'} = $locIfDescr;
+ });
+ $session->map_table ([$ifAlias],
+ sub ($$$) {
+ my ($index, $ifAlias) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($ifAlias));
+ $result->{$index}->{'ifAlias'} = $ifAlias;
+ });
+ $result;
+}
diff --git a/test/msdpls b/test/msdpls
new file mode 100755
index 0000000..ac5f338
--- /dev/null
+++ b/test/msdpls
@@ -0,0 +1,303 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use SNMP_Session;
+use BER;
+use Socket;
+
+my $version = '1';
+my $port = 161;
+my $debug = 0;
+my $group;
+my $numericp = 0;
+
+### Prototypes
+sub msdp_list_duplicate_sas ($ );
+sub msdp_collect_sas ($ );
+sub msdp_fill_in_duplicates ($$);
+sub msdp_report_duplicate_sas ($$$$);
+sub msdp_duplicate_report_header ($$$$);
+sub msdp_duplicate_report_trailer ($ );
+sub msdp_map_group ($$$$);
+sub msdp_map_sg ($$$$$ );
+sub msdp_list_group ($$);
+sub pretty_ip_html ($ );
+sub pretty_ip ($ );
+sub usage ($ );
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-p/) {
+ if ($ARGV[0] eq '-p') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+$/) {
+ $port = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-d') {
+ ++$debug;
+ } elsif ($ARGV[0] eq '-n') {
+ ++$numericp;
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } elsif ($ARGV[0] eq '-g') {
+ shift @ARGV;
+ $group = $ARGV[0] or usage (1);
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+usage (1) if $#ARGV >= $[;
+my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, $port)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, $port)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+$session->debug (1) if $debug;
+$session->{max_repetitions} = 100;
+
+my $msdpSACachePeerLearnedFrom = [1,3,6,1,3,92,1,1,6,1,4];
+my $msdpSACacheRPFPeer = [1,3,6,1,3,92,1,1,6,1,5];
+my $msdpSACacheInSAs = [1,3,6,1,3,92,1,1,6,1,6];
+my $msdpSACacheInDataPackets = [1,3,6,1,3,92,1,1,6,1,7];
+my $msdpSACacheUpTime = [1,3,6,1,3,92,1,1,6,1,8];
+my $msdpSACacheExpiryTime = [1,3,6,1,3,92,1,1,6,1,9];
+my $msdpSACacheStatus = [1,3,6,1,3,92,1,1,6,1,10];
+
+if (defined $group) {
+ msdp_list_group ($session, inet_aton ($group));
+} else {
+ msdp_list_duplicate_sas ($session);
+}
+1;
+
+sub msdp_list_duplicate_sas ($ ) {
+ my ($session) = @_;
+ my ($announcements, $nsas, $nsgs, $ndups);
+ ($announcements, $nsas) = msdp_collect_sas ($session);
+ $nsgs = keys %{$announcements};
+ ($announcements, $ndups) = msdp_fill_in_duplicates ($session, $announcements);
+ msdp_report_duplicate_sas ($announcements, $nsas, $nsgs, $ndups);
+}
+
+sub msdp_collect_sas ($ ) {
+ my ($session) = @_;
+ my @oids = ($msdpSACacheStatus);
+ my $nsa = 0;
+ my %announcements;
+ $session->map_table
+ (\@oids,
+ sub () {
+ my ($index, $sa_status) = @_;
+ die "index: $index"
+ unless $index =~ /^(\d+\.\d+\.\d+\.\d+)\.(\d+\.\d+\.\d+\.\d+)\.(\d+\.\d+\.\d+\.\d+)$/;
+ my ($group, $source, $rp) = ($1, $2, $3);
+ warn ("S/G/RP entry (status): $group $source $rp ("
+ .pretty_print ($sa_status).")\n")
+ if $debug;
+ return 0 unless pretty_print ($sa_status) == 1; # active(1)
+ ++$nsa;
+ push @{$announcements{$source,$group}}, {rp => $rp};
+ });
+ (\%announcements, $nsa);
+}
+
+sub msdp_fill_in_duplicates ($$) {
+ my ($session, $announcements) = @_;
+ my %result = ();
+ my ($oldreps, $key, $anns, $dupannouncements, $nrps);
+ $oldreps = $session->{max_repetitions};
+ $session->{max_repetitions} = 5;
+ $dupannouncements = 0;
+ foreach $key (keys %{$announcements}) {
+ my ($source, $group) = split ($;,$key);
+ $anns = $announcements->{$key};
+ if ($#{$anns} > 0) {
+ $nrps = 0;
+ my @newanns = ();
+ msdp_map_sg ($session, $group, $source,
+ [$msdpSACachePeerLearnedFrom,
+ $msdpSACacheRPFPeer,
+ $msdpSACacheInSAs,
+ $msdpSACacheInDataPackets,
+ $msdpSACacheUpTime,
+ $msdpSACacheExpiryTime,
+ $msdpSACacheStatus],
+ sub () {
+ my ($rp,
+ $peer_learned_from,$rpf_peer,
+ $in_sas,$in_data_packets,
+ $up_time,$expiry_time,$status) = @_;
+ return 1 unless $status == 1; # active(1)
+ push @newanns, {rp => $rp,
+ ## peer_learned_from => $peer_learned_from,
+ ## rpf_peer => $rpf_peer,
+ in_sas => $in_sas,
+ in_data_packets => $in_data_packets,
+ up_time => $up_time,
+ expiry_time => $expiry_time,
+ };
+ ++$nrps;
+ });
+ if ($nrps > 1) {
+ ++$dupannouncements;
+ $result{$key} = \@newanns;
+ }
+ }
+ }
+ $session->{max_repetitions} = $oldreps;
+ (\%result, $dupannouncements);
+}
+
+sub msdp_report_duplicate_sas ($$$$) {
+ my ($announcements, $nsas, $nsgs, $ndups) = @_;
+ msdp_duplicate_report_header ($announcements, $nsas, $nsgs, $ndups);
+ foreach my $key (sort keys %{$announcements}) {
+ my ($source, $group) = split ($;,$key);
+ my $announcements = $announcements->{$key};
+ if ($#{$announcements} > 0) {
+ printf STDOUT ("<tr><th colspan=\"3\">(%s,%s)</th></tr>\n",
+ pretty_ip_html ($source),
+ pretty_ip_html ($group));
+ foreach my $announcement (@{$announcements}) {
+ printf STDOUT ("<tr><td>%s</td><td align=\"right\">%d</td><td align=\"right\">%d</td></tr>\n",
+ pretty_ip_html ($announcement->{rp}),
+ $announcement->{in_sas},
+ $announcement->{in_data_packets});
+ }
+ }
+ }
+ msdp_duplicate_report_trailer ($announcements);
+}
+
+sub msdp_duplicate_report_header ($$$$) {
+ my ($announcements, $nsas, $nsgs, $ndups) = @_;
+ print STDOUT ("<html><head><title>MSDP Duplicate SA Report</title></head>\n");
+ print STDOUT <<EOM;
+<style type="text/css"> <!--
+ body{background-color:#ffffff; color:black; font-family:helvetica }
+ tt{font-family:courier,lucidatypewriter }
+ th{font-family:helvetica,arial }
+ td{font-family:helvetica,arial }
+ pre{font-family:courier,lucidatypewriter,monaco,monospaced }
+ -->
+</style>
+EOM
+ print STDOUT ("<body><h1>MSDP Duplicate SA Report</h1>\n");
+ printf STDOUT ("<p> %d (S,G) pairs found in %d SAs in <tt>%s</tt>'s cache. \n",
+ $nsgs, $nsas, $host);
+ printf STDOUT ("Total number of (S,G) pairs with multiple RPs: %d </p>\n",
+ $ndups);
+ printf STDOUT ("<table border=\"0\">\n<tr><th>RP</th><th>#SAs</th><th>#data pkts</th></tr>\n");
+}
+
+sub msdp_duplicate_report_trailer ($ ) {
+ my ($announcements) = @_;
+ print STDOUT "</table>\n</body></html>\n";
+}
+
+sub msdp_map_group ($$$$) {
+ my ($session, $group, $cols, $mapfn) = @_;
+ my @group_subids = split (/\./, inet_ntoa ($group), 4);
+ my @oids = map { $_ = [@{$_}, at group_subids] } @{$cols};
+ $session->map_table
+ (\@oids,
+ sub () {
+ my ($index, @colvals) = @_;
+ map { $_ = pretty_print $_ if defined $_ } (@colvals);
+ my ($source,$rp);
+ (($source,$rp) = ($index =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\.([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/))
+ || die "?";
+ &$mapfn ($source,$rp, at colvals);
+});
+}
+
+sub msdp_map_sg ($$$$$ ) {
+ my ($session, $group, $source, $cols, $mapfn) = @_;
+ my @group_subids = split (/\./, $group, 4);
+ my @source_subids = split (/\./, $source, 4);
+ my @oids = map { $_ = [@{$_}, at group_subids, at source_subids] } @{$cols};
+ $session->map_table
+ (\@oids,
+ sub () {
+ my ($index, @colvals) = @_;
+ map { $_ = pretty_print $_ if defined $_ } (@colvals);
+ my ($rp);
+ (($rp) = ($index =~ /^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$/))
+ || die "?";
+ &$mapfn ($rp, at colvals);
+});
+}
+
+sub msdp_list_group ($$) {
+ my ($session, $group) = @_;
+ msdp_map_group ($session,$group,
+ [$msdpSACachePeerLearnedFrom,
+ $msdpSACacheRPFPeer,
+ $msdpSACacheInSAs,
+ $msdpSACacheInDataPackets,
+ $msdpSACacheUpTime,
+ $msdpSACacheExpiryTime,
+ $msdpSACacheStatus],
+ sub () {
+ my ($source,$rp,
+ $peer_learned_from,$rpf_peer,
+ $in_sas,$in_data_packets,
+ $up_time,$expiry_time,$status) = @_;
+ #return unless $in_data_packets;
+ print " ",pretty_ip ($source)," $in_data_packets pkts\n";
+ print " $peer_learned_from (learned-from) != $rpf_peer (RPF peer)\n"
+ if $peer_learned_from ne $rpf_peer;
+ });
+}
+
+sub pretty_ip_html ($ ) {
+ return "<tt>".pretty_ip ($_[0])."</tt>";
+}
+
+sub pretty_ip ($ ) {
+ my ($ip) = @_;
+ my ($name);
+
+ !$numericp && ($name = gethostbyaddr (inet_aton ($ip),AF_INET))
+ ? "$name [$ip]"
+ : "$ip";
+}
+
+sub usage ($ ) {
+ print STDERR
+ ("Usage: $0 [OPTIONS...] ROUTER [COMMUNITY]\n\n",
+ " OPTIONS:\n\n",
+ " -v 1|2c select SNMPv1 or SNMPv2c (community-based SNMPv2)\n",
+ " -p PORT specify an alternate UDP port to contact SNMP agent\n",
+ " -g GROUP list sources for a specific group\n",
+ " -d print debugging output\n",
+ " -n don't resolve hostnames\n",
+ "\n",
+ " ROUTER which agent to contact - must implement the MSDP MIB\n",
+ " COMMUNITY specifies the SNMP community string, defaults to \"public\"\n");
+ exit $_[0];
+}
diff --git a/test/mtf.pl b/test/mtf.pl
new file mode 100755
index 0000000..2b4574f
--- /dev/null
+++ b/test/mtf.pl
@@ -0,0 +1,51 @@
+#!/usr/local/bin/perl -w
+#
+# @@@ Modified to use an illegal OID
+# @@@ (to check whether a correct error message is generated)
+#
+# Demonstration code for table walking
+#
+# This script should serve as an example of how to "correctly"
+# traverse the rows of a table. This functionality is implemented in
+# the map_table() subroutine. The example script displays a few
+# columns of the RFC 1213 interface table and Cisco's locIfTable. The
+# tables share the same index, so they can be handled by a single
+# invocation of map_table().
+
+require 5.003;
+
+use strict;
+
+use BER;
+use SNMP_Session;
+
+my $host = shift @ARGV || die;
+my $community = shift @ARGV || die;
+
+my $ifDescr = [1,3,6,1,2,1,2,2,1,2];
+my $ifInOctets = [1,3,6,1,2,1,2,2,1,10];
+my $ifOutOctets = [1,3,6,1,2,1,2,2,1,16];
+my $locIfInBitsSec = [1,3,6,1,4,1,9,2,2,1,1,6];
+# @@@
+my $locIfOutBitsSec = [9,2,2,1,1,8];
+# @@@
+my $locIfDescr = [1,3,6,1,4,1,9,2,2,1,1,28];
+
+sub out_interface {
+ my ($index, $descr, $in, $out, $comment) = @_;
+
+ grep (defined $_ && ($_=pretty_print $_),
+ ($descr, $in, $out, $comment));
+ printf "%2d %-24s %10s %10s %s\n",
+ $index,
+ defined $descr ? $descr : '',
+ defined $in ? $in/1000.0 : '-',
+ defined $out ? $out/1000.0 : '-',
+ defined $comment ? $comment : '';
+}
+
+my $session = SNMP_Session->open ($host, $community, 161)
+ || die "Opening SNMP_Session";
+$session->map_table ([$ifDescr,$locIfInBitsSec,$locIfOutBitsSec,$locIfDescr],
+ \&out_interface);
+1;
diff --git a/test/negative-counter.pl b/test/negative-counter.pl
new file mode 100644
index 0000000..79eb8b4
--- /dev/null
+++ b/test/negative-counter.pl
@@ -0,0 +1,8 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use BER;
+
+print pretty_print ("\x41\x01\xff"),"\n";
+1;
diff --git a/test/party-test.pl b/test/party-test.pl
new file mode 100755
index 0000000..0b782fe
--- /dev/null
+++ b/test/party-test.pl
@@ -0,0 +1,15 @@
+#!/usr/local/bin/perl
+######################################################################
+# Check that we can read the CMU SNMPv2 party definition file in
+# /etc/party.conf. Describe the party named "zeusmd5". This is
+# basically intended as a regression test for the party-parsing code.
+######################################################################
+
+require 5;
+
+require 'Party.pm';
+
+Party::read_cmu_party_database('/etc/party.conf');
+Party->find ('zeusmsmd5')->describe (STDERR);
+
+1;
diff --git a/test/pnni-find-ilmi-neighbors.pl b/test/pnni-find-ilmi-neighbors.pl
new file mode 100755
index 0000000..9141908
--- /dev/null
+++ b/test/pnni-find-ilmi-neighbors.pl
@@ -0,0 +1,41 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use BER;
+use SNMP_Session "0.57";
+
+sub usage();
+
+my $host = shift @ARGV || usage();
+my $community = shift @ARGV || 'public';
+
+my $pnniRouteAddrProto = [1,3,6,1,4,1,353,5,4,1,1,19,4,1,8];
+
+my $ilmionly = 1;
+my $hostonly = 1;
+
+my $session = SNMP_Session->open ($host, $community, 161)
+ || die "couldn't open SNMP session";
+$session->map_table ([$pnniRouteAddrProto],
+ sub {
+ my ($index, $proto) = @_;
+ grep (defined $_ && ($_=pretty_print $_),
+ ($proto));
+ ## we are only interested in routes whose proto is local(2).
+ return if $ilmionly && $proto != 2;
+ my @index = split ('\.',$index);
+ my $nsap = join (".", grep ($_=sprintf ("%02x",$_), at index[1..19]));
+ my $prefix_length = $index[20];
+
+ return if $hostonly && $prefix_length != 152;
+ print $nsap;
+ print "/",$prefix_length unless $hostonly;
+ print "\n";
+ });
+$session->close;
+1;
+
+sub usage () {
+ die "Usage: $0 host [community]";
+}
diff --git a/test/qosls b/test/qosls
new file mode 100755
index 0000000..c9c96cd
--- /dev/null
+++ b/test/qosls
@@ -0,0 +1,553 @@
+#!/usr/bin/perl -w
+###
+### qosls - list QoS configuration on a Cisco router
+###
+### Author: Simon Leinen <simon at switch.ch>
+### Date created: 26-Mar-2005
+###
+### This script reads QoS configuration information from the
+### CISCO-CLASS-BASED-QOS-MIB, and constructs an internal
+### representation for it.
+
+use strict;
+use SNMP_util;
+
+## Prototypes
+sub init_mibs ();
+sub collect_qos_information ($ );
+sub print_service_policies ($$);
+sub print_qos_objects ($ );
+sub print_qos_config ($ );
+sub get_if_entries ($ );
+sub get_service_policies ($ );
+sub get_qos_objects ($ );
+sub fixup_parents ($ );
+sub get_qos_object_configs ($ );
+sub get_qos_config ($$$$@);
+sub pretty_traffic_direction ($ );
+sub pretty_interface_type ($ );
+sub pretty_config_type ($ );
+sub pretty_class_info ($ );
+sub pretty_match_info ($ );
+sub pretty_queueing_bandwidth_units ($ );
+sub pretty_queueing_unit_type ($ );
+sub pretty_red_mechanism ($ );
+sub pretty_police_action ($ );
+sub pretty_traffic_shaping_limit ($ );
+sub get_police_action_configs ($$);
+sub decode_truth_value ($ );
+sub snmp_decode_value ($@);
+sub snmp_rows_to_objects ($$$@ );
+sub snmp_map_row_objects ($$$$@ );
+
+my $target = shift @ARGV || die "Usage: $0 target\n";
+
+init_mibs ();
+collect_qos_information ($target);
+1;
+
+sub collect_qos_information ($ ) {
+ my $if_entry = get_if_entries ($target);
+ my $service_policies = get_service_policies ($target);
+ my $qos_objects = get_qos_objects ($target);
+ my $configs = get_qos_object_configs ($target);
+
+ print_service_policies ($service_policies, $if_entry);
+ print_qos_objects ($qos_objects);
+ print_qos_config ($configs);
+}
+
+sub print_service_policies ($$) {
+ my ($service_policies, $if_entry) = @_;
+ print "Service Policies\n\n";
+ foreach my $installed_index (sort keys %{$service_policies}) {
+ my $service_policy = $service_policies->{$installed_index};
+ printf STDOUT
+ ("%4d %-10s %s\n",
+ $installed_index,
+ $if_entry->{$service_policy->{ifIndex}}->{descr},
+ pretty_traffic_direction ($service_policy->{policyDirection}));
+ }
+ print "\n";
+}
+
+sub print_qos_objects ($ ) {
+ my ($qos_objects) = @_;
+ print "QoS objects\n\n";
+ foreach my $policy_index (sort keys %{$qos_objects}) {
+ foreach my $object_index (sort keys %{$qos_objects->{$policy_index}}) {
+ my $qos_object = $qos_objects->{$policy_index}->{$object_index};
+ printf STDOUT
+ ("%4d %4d [%4d] %d %-10s\n",
+ $policy_index,
+ $object_index,
+ $qos_object->{parentObjectsIndex},
+ $qos_object->{configIndex},
+ pretty_config_type $qos_object->{objectsType});
+ }
+ }
+ print "\n";
+}
+
+sub print_qos_config ($ ) {
+ my ($configs) = @_;
+ print "QoS Configuration\n\n";
+ foreach my $config_index (sort keys %{$configs}) {
+ my $config = $configs->{$config_index};
+ printf STDOUT
+ ("%4d %-14s %s\n",
+ $config_index,
+ ref $config,
+ $config->tostring ());
+ }
+ print "\n";
+}
+
+### get_if_entries TARGET
+###
+### Read the MIB-II interface table, and construct a hash mapping the
+### interface indices to hashes containing important slots.
+### Currently, only ifDescr and ifAlias are recorded.
+###
+sub get_if_entries ($ ) {
+ my ($target) = @_;
+ return snmp_rows_to_objects
+ ($target, 'MIBII::Interface', 'if', qw(descr alias));
+}
+
+sub get_service_policies ($ ) {
+ return snmp_rows_to_objects
+ ($target, 'CBQM::ServicePolicy',
+ 'cbQos', qw(ifType policyDirection ifIndex frDLCI atmVCI));
+}
+
+sub get_qos_objects ($ ) {
+ my ($target) = @_;
+ my $qos_objects = {};
+ snmp_map_row_objects
+ ($target, 'CBQM::QosObject',
+ sub () {
+ my ($index, $object) = @_;
+ my ($policy_index, $object_index) = split ('\.', $index);
+ $qos_objects->{$policy_index}->{$object_index} = $object;
+ },
+ 'cbQos',
+ qw(configIndex objectsType parentObjectsIndex));
+ fixup_parents ($qos_objects);
+ return $qos_objects;
+}
+
+sub fixup_parents ($ ) {
+ my ($qos_objects) = @_;
+ foreach my $policy_index (keys %{$qos_objects}) {
+ my $policy = $qos_objects->{$policy_index};
+ foreach my $object_index (keys %$policy) {
+ my $object = $policy->{$object_index};
+ my $parent_index = $object->{'parentObjectsIndex'};
+ if ($parent_index != 0) {
+ die ("missing parent ",$parent_index)
+ unless $policy->{$parent_index};
+ $object->{'parent'} = $policy->{$parent_index};
+ push @{$policy->{$parent_index}->{'children'}}, $object;
+ }
+ }
+ }
+}
+
+sub get_qos_object_configs ($ ) {
+ my ($target) = @_;
+ my $configs = {};
+ get_qos_config ($target, 'CBQM::PolicyMapCfg', $configs,
+ 'cbQosPolicyMap', qw(name desc));
+ get_qos_config ($target, 'CBQM::ClassMapCfg', $configs,
+ 'cbQosCM', qw(name desc info));
+ get_qos_config ($target, 'CBQM::MatchStmtCfg', $configs,
+ 'cbQosMatchStmt', qw(name info));
+ get_qos_config ($target, 'CBQM::QueueingCfg', $configs,
+ 'cbQosQueueingCfg',
+ qw(bandwidth bandwidthUnits flowEnabled priorityEnabled
+ aggregateQSize individualQSize dynamicQNumber
+ prioBurstSize qLimitUnits aggregateQLimit));
+ get_qos_config ($target, 'CBQM::REDCfg', $configs,
+ 'cbQosREDCfg',
+ qw(exponWeight meanQsize dscpPrec eCNEnabled));
+ get_qos_config ($target, 'CBQM::REDClassCfg', $configs,
+ 'cbQosRED',
+ qw(cfgPktDropProb classCfgThresholdUnit
+ classCfgMinThreshold classCfgMaxThreshold));
+ get_qos_config ($target, 'CBQM::PoliceCfg', $configs,
+ 'cbQosPoliceCfg',
+ qw(rate burstSize extBurstSize
+ conformAction conformSetValue
+ exceedAction exceedSetValue
+ violateAction violateSetValue
+ pir rate64));
+ get_qos_config ($target, 'CBQM::TrafficShaperCfg', $configs,
+ 'cbQosTSCfg',
+ qw(rate burstSize extBurstSize
+ adaptiveEnabled adaptiveRate limitType));
+ get_qos_config ($target, 'CBQM::SetCfg', $configs,
+ 'cbQosSetCfg',
+ qw(feature ipDSCPValue ipPrecedenceValue qosGroupValue
+ l2CosValue mplsExpValue discardClassValue));
+ get_police_action_configs ($target, $configs);
+ return $configs;
+}
+
+sub get_qos_config ($$$$@) {
+ my ($target, $class, $configs, $prefix, @cols) = @_;
+ snmp_map_row_objects
+ ($target, $class,
+ sub () { my ($index, $object) = @_;
+ $configs->{$index} = $object; },
+ $prefix, @cols);
+ return $configs;
+}
+
+sub get_police_action_configs ($$) {
+ my ($target, $configs) = @_;
+ snmp_map_row_objects
+ ($target, 'CBQM::PoliceActionCfg',
+ sub () {
+ my ($index, $object) = @_;
+ my ($config_index, $action_index)
+ = split ('\.', $index);
+ $configs->{$config_index}->{'police_action'}->{$action_index}
+ = $object;
+ },
+ 'cbQosPoliceActionCfg',
+ qw(conform conformSetValue exceed exceedSetValue
+ violate violateSetValue));
+ return $configs;
+}
+
+sub pretty_traffic_direction ($ ) {
+ return snmp_decode_value ($_[0], qw(input output));}
+sub pretty_interface_type ($ ) {
+ return snmp_decode_value
+ ($_[0], qw(mainInterface subInterface frDLCI atmPVC));}
+sub pretty_config_type ($ ) {
+ return snmp_decode_value
+ ($_[0], qw(policymap classmap matchStatement queueing
+ randomDetect trafficShaping police set));}
+sub pretty_class_info ($ ) {
+ return snmp_decode_value ($_[0], qw(none matchAll matchAny));}
+sub pretty_match_info ($ ) {
+ return snmp_decode_value ($_[0], qw(none matchNot));}
+sub pretty_queueing_bandwidth_units ($ ) {
+ return snmp_decode_value ($_[0], qw(kbps percentage percentageRemaining));}
+sub pretty_queueing_unit_type ($ ) {
+ return snmp_decode_value ($_[0], qw(packets cells bytes));}
+sub pretty_red_mechanism ($ ) {
+ return snmp_decode_value ($_[0], qw(precedence dscp));}
+sub pretty_police_action ($ ) {
+ return snmp_decode_value
+ ($_[0], qw(transmit setIpDSCP setIpPrecedence setQosGroup
+ drop setMplsExp setAtmClp setFrDe setL2Cos setDiscardClass));}
+sub pretty_traffic_shaping_limit ($ ) {
+ return snmp_decode_value ($_[0], qw(average peak));}
+sub pretty_set_feature_type ($ ) {
+ return snmp_decode_value
+ ($_[0], qw(ipDscp ipPrecedence qosGroupNumber
+ frDeBit atmClpBit l2Cos mplsExp discardClass));}
+
+sub decode_truth_value ($ ) {return snmp_decode_value ($_[0], qw(1 0));}
+
+sub snmp_decode_value ($@) {
+ my ($index, @mapvec) = @_;
+ return $index if $index < 1 or $index > $#mapvec+1;
+ return $mapvec[$index-1];
+}
+
+### snmp_rows_to_objects TARGET, CLASS, PREFIX, COLUMNS...
+###
+### Returns a reference to a hash that maps a table's index to objects
+### created from the set of COLUMNS. The COLUMNS are partial OID
+### names, to each of which the PREFIX is prepended. An object is
+### created for each row in the table, by creating a hash reference
+### with a slot for each column, named by the (partial) column name.
+### It is blessed to the CLASS.
+###
+### For example, if we have the following table at $TARGET:
+###
+### index fooBar fooBaz fooBlech
+###
+### 1000 asd 23498 vohdajae
+### 1001 fgh 45824 yaohetoo
+### 1002 jkl 89732 engahghi
+###
+### Then the call:
+###
+### snmp_rows_to_objects ($TARGET, 'MyFoo', 'foo', 'bar', 'baz', 'blech')
+###
+### will create a hash reference similar to this:
+###
+### $result = {};
+### $result{1000} = bless { 'bar' => 'asd',
+### 'baz' => 23498,
+### 'blech' => 'vohdajae' }, 'MyFoo';
+### $result{1001} = bless { 'bar' => 'fgh',
+### 'baz' => 45824,
+### 'blech' => 'yaohetoo' }, 'MyFoo';
+### $result{1002} = bless { 'bar' => 'jkl',
+### 'baz' => 89732,
+### 'blech' => 'engahghi' }, 'MyFoo';
+###
+sub snmp_rows_to_objects ($$$@) {
+ my ($target, $class, $prefix, @cols) = @_;
+ my $result = {};
+ snmp_map_row_objects
+ ($target, $class,
+ sub () {
+ my ($index, $object) = @_;
+ $result->{$index} = $object;
+ },
+ $prefix, @cols);
+ return $result;
+}
+
+### snmp_map_row_objects TARGET, CLASS, MAPFN, PREFIX, COLUMNS...
+###
+### This function traverses a table, creating an object for each row,
+### and applying the user-supplied MAPFN to each of these objects.
+###
+### The table is defined by PREFIX and COLUMNS, as described for
+### snmp_rows_to_objects above. An object is created according to
+### CLASS and COLUMNS, as described above. The difference is that,
+### rather than putting all objects in a hash, we simply apply the
+### user-supplied MAPFN to each row object.
+###
+sub snmp_map_row_objects ($$$$@) {
+ my ($target, $class, $mapfn, $prefix, @cols) = @_;
+ snmpmaptable ($target,
+ sub () {
+ my ($index, @colvals) = @_;
+ my $object = bless {}, $class;
+ foreach my $col (@cols) {
+ $object->{$col} = shift @colvals;
+ }
+ &$mapfn ($index, $object);
+ },
+ map ($prefix.ucfirst $_, at cols));
+}
+
+sub init_mibs () {
+ snmpmapOID
+ (qw(
+cbQosIfType 1.3.6.1.4.1.9.9.166.1.1.1.1.2
+cbQosPolicyDirection 1.3.6.1.4.1.9.9.166.1.1.1.1.3
+cbQosIfIndex 1.3.6.1.4.1.9.9.166.1.1.1.1.4
+cbQosFrDLCI 1.3.6.1.4.1.9.9.166.1.1.1.1.5
+cbQosAtmVPI 1.3.6.1.4.1.9.9.166.1.1.1.1.6
+cbQosAtmVCI 1.3.6.1.4.1.9.9.166.1.1.1.1.7
+cbQosConfigIndex 1.3.6.1.4.1.9.9.166.1.5.1.1.2
+cbQosObjectsType 1.3.6.1.4.1.9.9.166.1.5.1.1.3
+cbQosParentObjectsIndex 1.3.6.1.4.1.9.9.166.1.5.1.1.4
+cbQosPolicyMapName 1.3.6.1.4.1.9.9.166.1.6.1.1.1
+cbQosPolicyMapDesc 1.3.6.1.4.1.9.9.166.1.6.1.1.2
+cbQosCMName 1.3.6.1.4.1.9.9.166.1.7.1.1.1
+cbQosCMDesc 1.3.6.1.4.1.9.9.166.1.7.1.1.2
+cbQosCMInfo 1.3.6.1.4.1.9.9.166.1.7.1.1.3
+cbQosMatchStmtName 1.3.6.1.4.1.9.9.166.1.8.1.1.1
+cbQosMatchStmtInfo 1.3.6.1.4.1.9.9.166.1.8.1.1.2
+));
+ ## configuration
+ snmpmapOID (qw(
+cbQosQueueingCfgBandwidth 1.3.6.1.4.1.9.9.166.1.9.1.1.1
+cbQosQueueingCfgBandwidthUnits 1.3.6.1.4.1.9.9.166.1.9.1.1.2
+cbQosQueueingCfgFlowEnabled 1.3.6.1.4.1.9.9.166.1.9.1.1.3
+cbQosQueueingCfgPriorityEnabled 1.3.6.1.4.1.9.9.166.1.9.1.1.4
+cbQosQueueingCfgAggregateQSize 1.3.6.1.4.1.9.9.166.1.9.1.1.5
+cbQosQueueingCfgIndividualQSize 1.3.6.1.4.1.9.9.166.1.9.1.1.6
+cbQosQueueingCfgDynamicQNumber 1.3.6.1.4.1.9.9.166.1.9.1.1.7
+cbQosQueueingCfgPrioBurstSize 1.3.6.1.4.1.9.9.166.1.9.1.1.8
+cbQosQueueingCfgQLimitUnits 1.3.6.1.4.1.9.9.166.1.9.1.1.9
+cbQosQueueingCfgAggregateQLimit 1.3.6.1.4.1.9.9.166.1.9.1.1.10
+cbQosREDCfgExponWeight 1.3.6.1.4.1.9.9.166.1.10.1.1.1
+cbQosREDCfgMeanQsize 1.3.6.1.4.1.9.9.166.1.10.1.1.2
+cbQosREDCfgDscpPrec 1.3.6.1.4.1.9.9.166.1.10.1.1.3
+cbQosREDCfgECNEnabled 1.3.6.1.4.1.9.9.166.1.10.1.1.4
+cbQosREDValue 1.3.6.1.4.1.9.9.166.1.11.1.1.1
+cbQosREDCfgMinThreshold 1.3.6.1.4.1.9.9.166.1.11.1.1.2
+cbQosREDCfgMaxThreshold 1.3.6.1.4.1.9.9.166.1.11.1.1.3
+cbQosREDCfgPktDropProb 1.3.6.1.4.1.9.9.166.1.11.1.1.4
+cbQosREDClassCfgThresholdUnit 1.3.6.1.4.1.9.9.166.1.11.1.1.5
+cbQosREDClassCfgMinThreshold 1.3.6.1.4.1.9.9.166.1.11.1.1.6
+cbQosREDClassCfgMaxThreshold 1.3.6.1.4.1.9.9.166.1.11.1.1.7
+cbQosPoliceCfgRate 1.3.6.1.4.1.9.9.166.1.12.1.1.1
+cbQosPoliceCfgBurstSize 1.3.6.1.4.1.9.9.166.1.12.1.1.2
+cbQosPoliceCfgExtBurstSize 1.3.6.1.4.1.9.9.166.1.12.1.1.3
+cbQosPoliceCfgConformAction 1.3.6.1.4.1.9.9.166.1.12.1.1.4
+cbQosPoliceCfgConformSetValue 1.3.6.1.4.1.9.9.166.1.12.1.1.5
+cbQosPoliceCfgExceedAction 1.3.6.1.4.1.9.9.166.1.12.1.1.6
+cbQosPoliceCfgExceedSetValue 1.3.6.1.4.1.9.9.166.1.12.1.1.7
+cbQosPoliceCfgViolateAction 1.3.6.1.4.1.9.9.166.1.12.1.1.8
+cbQosPoliceCfgViolateSetValue 1.3.6.1.4.1.9.9.166.1.12.1.1.9
+cbQosPoliceCfgPir 1.3.6.1.4.1.9.9.166.1.12.1.1.10
+cbQosPoliceCfgRate64 1.3.6.1.4.1.9.9.166.1.12.1.1.11
+cbQosTSCfgRate 1.3.6.1.4.1.9.9.166.1.13.1.1.1
+cbQosTSCfgBurstSize 1.3.6.1.4.1.9.9.166.1.13.1.1.2
+cbQosTSCfgExtBurstSize 1.3.6.1.4.1.9.9.166.1.13.1.1.3
+cbQosTSCfgAdaptiveEnabled 1.3.6.1.4.1.9.9.166.1.13.1.1.4
+cbQosTSCfgAdaptiveRate 1.3.6.1.4.1.9.9.166.1.13.1.1.5
+cbQosTSCfgLimitType 1.3.6.1.4.1.9.9.166.1.13.1.1.6
+cbQosSetCfgFeature 1.3.6.1.4.1.9.9.166.1.14.1.1.1
+cbQosSetCfgIpDSCPValue 1.3.6.1.4.1.9.9.166.1.14.1.1.2
+cbQosSetCfgIpPrecedenceValue 1.3.6.1.4.1.9.9.166.1.14.1.1.3
+cbQosSetCfgQosGroupValue 1.3.6.1.4.1.9.9.166.1.14.1.1.4
+cbQosSetCfgL2CosValue 1.3.6.1.4.1.9.9.166.1.14.1.1.5
+cbQosSetCfgMplsExpValue 1.3.6.1.4.1.9.9.166.1.14.1.1.6
+cbQosSetCfgDiscardClassValue 1.3.6.1.4.1.9.9.166.1.14.1.1.7
+cbQosPoliceActionCfgIndex 1.3.6.1.4.1.9.9.166.1.21.1.1.1
+cbQosPoliceActionCfgConform 1.3.6.1.4.1.9.9.166.1.21.1.1.2
+cbQosPoliceActionCfgConformSetValue 1.3.6.1.4.1.9.9.166.1.21.1.1.3
+cbQosPoliceActionCfgExceed 1.3.6.1.4.1.9.9.166.1.21.1.1.4
+cbQosPoliceActionCfgExceedSetValue 1.3.6.1.4.1.9.9.166.1.21.1.1.5
+cbQosPoliceActionCfgViolate 1.3.6.1.4.1.9.9.166.1.21.1.1.6
+cbQosPoliceActionCfgViolateSetValue 1.3.6.1.4.1.9.9.166.1.21.1.1.7
+));
+ ## statistics
+ snmpmapOID (qw(
+cbQosCMPrePolicyPktOverflow 1.3.6.1.4.1.9.9.166.1.15.1.1.1
+cbQosCMPrePolicyPkt 1.3.6.1.4.1.9.9.166.1.15.1.1.2
+cbQosCMPrePolicyPkt64 1.3.6.1.4.1.9.9.166.1.15.1.1.3
+cbQosCMPrePolicyByteOverflow 1.3.6.1.4.1.9.9.166.1.15.1.1.4
+cbQosCMPrePolicyByte 1.3.6.1.4.1.9.9.166.1.15.1.1.5
+cbQosCMPrePolicyByte64 1.3.6.1.4.1.9.9.166.1.15.1.1.6
+cbQosCMPrePolicyBitRate 1.3.6.1.4.1.9.9.166.1.15.1.1.7
+cbQosCMPostPolicyByteOverflow 1.3.6.1.4.1.9.9.166.1.15.1.1.8
+cbQosCMPostPolicyByte 1.3.6.1.4.1.9.9.166.1.15.1.1.9
+cbQosCMPostPolicyByte64 1.3.6.1.4.1.9.9.166.1.15.1.1.10
+cbQosCMPostPolicyBitRate 1.3.6.1.4.1.9.9.166.1.15.1.1.11
+cbQosCMDropPktOverflow 1.3.6.1.4.1.9.9.166.1.15.1.1.12
+cbQosCMDropPkt 1.3.6.1.4.1.9.9.166.1.15.1.1.13
+cbQosCMDropPkt64 1.3.6.1.4.1.9.9.166.1.15.1.1.14
+cbQosCMDropByteOverflow 1.3.6.1.4.1.9.9.166.1.15.1.1.15
+cbQosCMDropByte 1.3.6.1.4.1.9.9.166.1.15.1.1.16
+cbQosCMDropByte64 1.3.6.1.4.1.9.9.166.1.15.1.1.17
+cbQosCMDropBitRate 1.3.6.1.4.1.9.9.166.1.15.1.1.18
+cbQosCMNoBufDropPktOverflow 1.3.6.1.4.1.9.9.166.1.15.1.1.19
+cbQosCMNoBufDropPkt 1.3.6.1.4.1.9.9.166.1.15.1.1.20
+cbQosCMNoBufDropPkt64 1.3.6.1.4.1.9.9.166.1.15.1.1.21
+cbQosMatchPrePolicyPktOverflow 1.3.6.1.4.1.9.9.166.1.16.1.1.1
+cbQosMatchPrePolicyPkt 1.3.6.1.4.1.9.9.166.1.16.1.1.2
+cbQosMatchPrePolicyPkt64 1.3.6.1.4.1.9.9.166.1.16.1.1.3
+cbQosMatchPrePolicyByteOverflow 1.3.6.1.4.1.9.9.166.1.16.1.1.4
+cbQosMatchPrePolicyByte 1.3.6.1.4.1.9.9.166.1.16.1.1.5
+cbQosMatchPrePolicyByte64 1.3.6.1.4.1.9.9.166.1.16.1.1.6
+cbQosMatchPrePolicyBitRate 1.3.6.1.4.1.9.9.166.1.16.1.1.7
+cbQosPoliceConformedPktOverflow 1.3.6.1.4.1.9.9.166.1.17.1.1.1
+cbQosPoliceConformedPkt 1.3.6.1.4.1.9.9.166.1.17.1.1.2
+cbQosPoliceConformedPkt64 1.3.6.1.4.1.9.9.166.1.17.1.1.3
+cbQosPoliceConformedByteOverflow 1.3.6.1.4.1.9.9.166.1.17.1.1.4
+cbQosPoliceConformedByte 1.3.6.1.4.1.9.9.166.1.17.1.1.5
+cbQosPoliceConformedByte64 1.3.6.1.4.1.9.9.166.1.17.1.1.6
+cbQosPoliceConformedBitRate 1.3.6.1.4.1.9.9.166.1.17.1.1.7
+cbQosPoliceExceededPktOverflow 1.3.6.1.4.1.9.9.166.1.17.1.1.8
+cbQosPoliceExceededPkt 1.3.6.1.4.1.9.9.166.1.17.1.1.9
+cbQosPoliceExceededPkt64 1.3.6.1.4.1.9.9.166.1.17.1.1.10
+cbQosPoliceExceededByteOverflow 1.3.6.1.4.1.9.9.166.1.17.1.1.11
+cbQosPoliceExceededByte 1.3.6.1.4.1.9.9.166.1.17.1.1.12
+cbQosPoliceExceededByte64 1.3.6.1.4.1.9.9.166.1.17.1.1.13
+cbQosPoliceExceededBitRate 1.3.6.1.4.1.9.9.166.1.17.1.1.14
+cbQosPoliceViolatedPktOverflow 1.3.6.1.4.1.9.9.166.1.17.1.1.15
+cbQosPoliceViolatedPkt 1.3.6.1.4.1.9.9.166.1.17.1.1.16
+cbQosPoliceViolatedPkt64 1.3.6.1.4.1.9.9.166.1.17.1.1.17
+cbQosPoliceViolatedByteOverflow 1.3.6.1.4.1.9.9.166.1.17.1.1.18
+cbQosPoliceViolatedByte 1.3.6.1.4.1.9.9.166.1.17.1.1.19
+cbQosPoliceViolatedByte64 1.3.6.1.4.1.9.9.166.1.17.1.1.20
+cbQosPoliceViolatedBitRate 1.3.6.1.4.1.9.9.166.1.17.1.1.21
+cbQosQueueingCurrentQDepth 1.3.6.1.4.1.9.9.166.1.18.1.1.1
+cbQosQueueingMaxQDepth 1.3.6.1.4.1.9.9.166.1.18.1.1.2
+cbQosQueueingDiscardByteOverflow 1.3.6.1.4.1.9.9.166.1.18.1.1.3
+cbQosQueueingDiscardByte 1.3.6.1.4.1.9.9.166.1.18.1.1.4
+cbQosQueueingDiscardByte64 1.3.6.1.4.1.9.9.166.1.18.1.1.5
+cbQosQueueingDiscardPktOverflow 1.3.6.1.4.1.9.9.166.1.18.1.1.6
+cbQosQueueingDiscardPkt 1.3.6.1.4.1.9.9.166.1.18.1.1.7
+cbQosQueueingDiscardPkt64 1.3.6.1.4.1.9.9.166.1.18.1.1.8
+cbQosTSStatsDelayedByteOverflow 1.3.6.1.4.1.9.9.166.1.19.1.1.1
+cbQosTSStatsDelayedByte 1.3.6.1.4.1.9.9.166.1.19.1.1.2
+cbQosTSStatsDelayedByte64 1.3.6.1.4.1.9.9.166.1.19.1.1.3
+cbQosTSStatsDelayedPktOverflow 1.3.6.1.4.1.9.9.166.1.19.1.1.4
+cbQosTSStatsDelayedPkt 1.3.6.1.4.1.9.9.166.1.19.1.1.5
+cbQosTSStatsDelayedPkt64 1.3.6.1.4.1.9.9.166.1.19.1.1.6
+cbQosTSStatsDropByteOverflow 1.3.6.1.4.1.9.9.166.1.19.1.1.7
+cbQosTSStatsDropByte 1.3.6.1.4.1.9.9.166.1.19.1.1.8
+cbQosTSStatsDropByte64 1.3.6.1.4.1.9.9.166.1.19.1.1.9
+cbQosTSStatsDropPktOverflow 1.3.6.1.4.1.9.9.166.1.19.1.1.10
+cbQosTSStatsDropPkt 1.3.6.1.4.1.9.9.166.1.19.1.1.11
+cbQosTSStatsDropPkt64 1.3.6.1.4.1.9.9.166.1.19.1.1.12
+cbQosTSStatsActive 1.3.6.1.4.1.9.9.166.1.19.1.1.13
+cbQosTSStatsCurrentQSize 1.3.6.1.4.1.9.9.166.1.19.1.1.14
+cbQosREDRandomDropPktOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.1
+cbQosREDRandomDropPkt 1.3.6.1.4.1.9.9.166.1.20.1.1.2
+cbQosREDRandomDropPkt64 1.3.6.1.4.1.9.9.166.1.20.1.1.3
+cbQosREDRandomDropByteOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.4
+cbQosREDRandomDropByte 1.3.6.1.4.1.9.9.166.1.20.1.1.5
+cbQosREDRandomDropByte64 1.3.6.1.4.1.9.9.166.1.20.1.1.6
+cbQosREDTailDropPktOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.7
+cbQosREDTailDropPkt 1.3.6.1.4.1.9.9.166.1.20.1.1.8
+cbQosREDTailDropPkt64 1.3.6.1.4.1.9.9.166.1.20.1.1.9
+cbQosREDTailDropByteOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.10
+cbQosREDTailDropByte 1.3.6.1.4.1.9.9.166.1.20.1.1.11
+cbQosREDTailDropByte64 1.3.6.1.4.1.9.9.166.1.20.1.1.12
+cbQosREDTransmitPktOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.13
+cbQosREDTransmitPkt 1.3.6.1.4.1.9.9.166.1.20.1.1.14
+cbQosREDTransmitPkt64 1.3.6.1.4.1.9.9.166.1.20.1.1.15
+cbQosREDTransmitByteOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.16
+cbQosREDTransmitByte 1.3.6.1.4.1.9.9.166.1.20.1.1.17
+cbQosREDTransmitByte64 1.3.6.1.4.1.9.9.166.1.20.1.1.18
+cbQosREDECNMarkPktOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.19
+cbQosREDECNMarkPkt 1.3.6.1.4.1.9.9.166.1.20.1.1.20
+cbQosREDECNMarkPkt64 1.3.6.1.4.1.9.9.166.1.20.1.1.21
+cbQosREDECNMarkByteOverflow 1.3.6.1.4.1.9.9.166.1.20.1.1.22
+cbQosREDECNMarkByte 1.3.6.1.4.1.9.9.166.1.20.1.1.23
+cbQosREDECNMarkByte64 1.3.6.1.4.1.9.9.166.1.20.1.1.24
+cbQosREDMeanQSizeUnits 1.3.6.1.4.1.9.9.166.1.20.1.1.25
+cbQosREDMeanQSize 1.3.6.1.4.1.9.9.166.1.20.1.1.26
+ ));
+}
+
+package MIBII::Interface;
+package CBQM::ServicePolicy;
+package CBQM::QosObject;
+package CBQM::PolicyMapCfg;
+
+sub tostring ($ ) {
+ my $result = $_[0]->{name};
+ $result .= ' ('.$_[0]->{desc}.')'
+ if $_[0]->{desc};
+ return $result;
+}
+
+package CBQM::ClassMapCfg;
+
+sub tostring ($ ) {
+ my $result = $_[0]->{name};
+ $result .= ' ('.$_[0]->{desc}.')'
+ if $_[0]->{desc};
+ return $result;
+}
+
+package CBQM::MatchStmtCfg;
+
+sub tostring ($ ) {
+ my $result = $_[0]->{name};
+ $result .= ' ('.$_[0]->{desc}.')'
+ if $_[0]->{desc};
+ return $result;
+}
+
+package CBQM::QueueingCfg;
+package CBQM::REDCfg;
+package CBQM::REDClassCfg;
+package CBQM::PoliceCfg;
+
+sub tostring ($ ) {
+ my $result = "rate: ".($_[0]->{rate64} || $_[0]->{rate});
+ return $result;
+}
+
+package CBQM::TrafficShaperCfg;
+package CBQM::SetCfg;
+package CBQM::PoliceActionCfg;
diff --git a/test/router-stats.pl b/test/router-stats.pl
new file mode 100755
index 0000000..c2471f5
--- /dev/null
+++ b/test/router-stats.pl
@@ -0,0 +1,161 @@
+#!/usr/local/bin/perl
+
+use BER;
+require 'SNMP_Session.pm';
+
+# Set $host to the name of the host whose SNMP agent you want
+# to talk to. Set $community to the community name under
+# which you want to talk to the agent. Set port to the UDP
+# port on which the agent listens (usually 161).
+
+my $routerfile = 'test/routers';
+my @routers = qw(swiEG1.switch.ch swiEZ1.switch.ch swiEZ2.switch.ch swiCS1.switch.ch swiCS2.switch.ch);
+my $redline=10;
+my $yellowline=5;
+
+my $redball = "<table bgcolor=red><tr><td> </td></tr></table>";
+my $yelball = "<table bgcolor=yellow><tr><td> </td></tr></table>";
+my $greenball = "<table bgcolor=green><tr><td> </td></tr></table>";
+
+print <<"TEXT";
+ <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+ <html>
+ <head>
+ <meta http-equiv="Content-Type"
+ content="text/html; charset=iso-8859-1">
+ <title>WAN Routers at a glance</title>
+ </head>
+
+ <body background="/images/background.gif">
+<table border="0" cellpadding="0" cellspacing="1" width="80%">
+ <tr>
+ <td align="right" rowspan="2">
+<!-- <img src="../images/pushme2.gif" width="150" height="159"></td> -->
+ <td colspan="2"><p align="center"><font size="5"
+ face="Palatino"><strong>WAN Routers at a Glance<br>
+ </strong></font><font size="4" face="Palatino"><strong>Technical
+ Team Only</strong></font></p>
+ </td>
+ </tr>
+</table>
+<br>
+TEXT
+ at results2=();
+for ($currouter=0; $currouter < $#routers; $currouter++) {
+
+$host=@routers[$currouter];
+$community = "public";
+$port = "161";
+$path = 'test/stats/';
+
+$session = SNMP_Session->open ($host, $community, $port)
+ || die "couldn't open SNMP session to $host";
+
+# Set $oid1, $oid2... to the BER-encoded OIDs of the MIB
+# variables you want to get.
+
+$oid1 = encode_oid (1, 3, 6, 1, 2, 1, 2, 1, 0);
+$oid2 = encode_oid (1, 3, 6, 1, 2, 1, 1, 5, 0);
+# Cisco CPU OID
+$oid3 = encode_oid (1, 3, 6, 1, 4,1,9,2,1,58,0);
+if ($session->get_request_response ($oid1,$oid2,$oid3)) {
+ ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
+ while ($bindings ne '') {
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ $interfaces=pretty_print ($value);
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ $sysname=pretty_print ($value);
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ $cpupercent=pretty_print ($value);
+ }
+} else {
+ die "No response from agent on $host";
+}
+print <<TEXT;
+
+ <div align="left"><left>
+
+ <table border="1" cellpadding="0" cellspacing="1" width=80%>
+<TR> <td colspan=
+TEXT
+ at results = ();
+ at outhead=();
+ at outvalue=();
+ at outhead[0]="CPU";
+if ($cpupercent>$redline){
+ $graphic=$redball;
+ } elsif ($cpupercent>$yellowline) {
+ $graphic=$yelball;
+ } else {
+ $graphic=$greenball;
+ }
+ at outvalue[0]=$graphic;
+$a=1;
+for ($i=1; $i <= $interfaces; $i++) {
+$oid1=encode_oid(1,3, 6, 1, 2, 1, 2, 2, 1 ,2, $i);
+$oid2=encode_oid(1,3, 6, 1, 2, 1, 2, 2, 1 ,8, $i);
+$oid3=encode_oid(1,3, 6, 1, 2, 1, 2, 2, 1 ,5, $i);
+if ($session->get_request_response ($oid1,$oid2,$oid3)) {
+ ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
+ while ($bindings ne '') {
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ $name=pretty_print ($value);
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ $status=pretty_print ($value);
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ $maxspeed=(pretty_print ($value)/8);
+
+ if ($status=="1") {
+ $file = $path.$host.".".$i.".log";
+ @temp=split(/\n/,$file);
+ $file=@temp[0]. at temp[1];
+ #print $file,"\n";
+ open(INFO, $file);
+ @lines = <INFO>;
+ @elements=split(/ /, at lines[1]);
+ $curtot=@elements[1]+ at elements[2];
+ if ($maxspeed == 0) {
+ $graphic="";
+ } else {
+ $percentage=($curtot/$maxspeed)*100;
+ if ($percentage>$redline){
+ $graphic=$redball;
+ } elsif ($percentage>$yellowline) {
+ $graphic=$yelball;
+ } else {
+ $graphic=$greenball;
+ }
+ }
+ @outhead[$a]=$name;
+ @outvalue[$a]=$graphic;
+ $a++;
+ }}
+} else {
+ die "No response from agent on $host";
+}
+
+}
+print $#outhead+1,">Utilisation statistics for ",$sysname," </TD></tr>";
+for ($x=0; $x <= $#outhead; $x++) {
+print "<td>", at outhead[$x],"</td>\n";
+}
+print "<tr>\n";
+for ($x=0; $x <= $#outvalue; $x++) {
+print "<td>", at outvalue[$x],"</td>\n";
+}
+ at outhead=();
+ at outvalue=();
+print <<"TEXT";
+</tr>
+</table><br>
+</left></div>
+TEXT
+
+}
+print "</body></html>";
diff --git a/test/sequence-bug.pl b/test/sequence-bug.pl
new file mode 100644
index 0000000..b7fe7c6
--- /dev/null
+++ b/test/sequence-bug.pl
@@ -0,0 +1,22 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use SNMP_Session;
+use SNMP_util;
+
+my $snmphost = $ARGV[0];
+
+my (%IN, %OUT);
+my @ret = &snmpwalk($snmphost, "ipAdEntIfIndex");
+foreach my $desc (@ret) {
+ my ($ipad, $ifType);
+ ($ipad, $desc) = split(':', $desc, 2);
+ next if $ipad=~/127.0.0.1/;
+
+ ($ifType,$IN{$ipad},$OUT{$ipad})=&snmpget($snmphost,"ifType.$desc","ifInOctets.$desc","ifOutOctets.$desc");
+}
+
+foreach my $ipad (sort keys %IN) {
+ printf "%-15s %12d %12d\n", $ipad, $IN{$ipad}, $OUT{$ipad};
+}
diff --git a/test/shipmr b/test/shipmr
new file mode 100755
index 0000000..6b37058
--- /dev/null
+++ b/test/shipmr
@@ -0,0 +1,119 @@
+#!/usr/local/bin/perl -w
+
+use strict;
+
+use BER;
+use SNMP_Session;
+use Socket;
+
+sub get_table_entry ($$$ );
+
+my $version = '2c';
+my $port = 161;
+my $debug = 0;
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-p/) {
+ if ($ARGV[0] eq '-p') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+$/) {
+ $port = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+
+my $source = '130.59.4.2';
+my $group = '233.2.47.1';
+my $source_mask = '255.255.255.255';
+my $index = $group.".".$source.".".$source_mask;
+usage (1) if $#ARGV >= $[;
+
+
+my @ipMRouteTableOIDs = ([1,3,6,1,3,60,1,1,2,1,4],
+ [1,3,6,1,3,60,1,1,2,1,5],
+ [1,3,6,1,3,60,1,1,2,1,6],
+ [1,3,6,1,3,60,1,1,2,1,7],
+ [1,3,6,1,3,60,1,1,2,1,8],
+ [1,3,6,1,3,60,1,1,2,1,9],
+ [1,3,6,1,3,60,1,1,2,1,10],
+ [1,3,6,1,3,60,1,1,2,1,11]);
+
+&print_route_at_router ($host, $community);
+
+my %router_seen = ();
+
+sub print_route_at_router {
+ my ($host, $community) = @_;
+ return if $router_seen{$host};
+ ++$router_seen{$host};
+ my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, $port)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, $port)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+ $session->debug (1) if $debug;
+ my ($upstream_neighbor, $in_if_index, $up_time, $expiry_time, $pkts, $different_in_if_packets, $octets, $protocol)
+ = get_table_entry ($session, \@ipMRouteTableOIDs, $index);
+ return undef unless defined $upstream_neighbor;
+ my $upstream_name = gethostbyaddr(pack ("C4",split ('\.',$upstream_neighbor)),
+ AF_INET) || $upstream_neighbor;
+ print "Router: $host\n";
+ print " upstream neighbor: $upstream_neighbor ($upstream_name)\n";
+ print " in-interface: $in_if_index\n";
+ print_route_at_router ($upstream_name, $community)
+ unless $upstream_neighbor eq '0.0.0.0';
+}
+
+sub get_table_entry ($$$ ) {
+ my ($session, $columns, $index) = @_;
+ my @result;
+
+ if ($session->get_request_response (map { encode_oid (@{$_},split ('\.',$index)) } (@{$columns}))) {
+ my $response = $session->pdu_buffer;
+ my ($bindings) = $session->decode_get_response ($response);
+ my ($binding, $oid, $value);
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ push @result, pretty_print ($value);
+ }
+ } else {
+ warn "SNMP problem: $SNMP_Session::errmsg\n";
+ }
+ @result;
+}
+
+#foreach my $oid (@ipMRouteTableOIDs) {
+# warn "OID: $oid";
+#}
+#system "snmpget $host ipMRouteTable.ipMRouteEntry.ipMRouteInIfIndex.$index";
+1;
diff --git a/test/snmpmap_table-test.pl b/test/snmpmap_table-test.pl
new file mode 100644
index 0000000..aa5eb3d
--- /dev/null
+++ b/test/snmpmap_table-test.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+use BER;
+use SNMP_Session;
+use SNMP_util "0.86";
+
+snmpmapOID (qw(locIfInBitsSec 1.3.6.1.4.1.9.2.2.1.1.6
+ locIfOutBitsSec 1.3.6.1.4.1.9.2.2.1.1.8
+ locIfDescr 1.3.6.1.4.1.9.2.2.1.1.28));
+
+sub usage () { die "Usage: $0 community\@host\n"; }
+
+my $host = shift @ARGV || usage ();
+
+snmpmaptable ($host,
+ sub {
+ my ($index, $descr, $in, $out, $comment) = @_;
+
+ printf "%2d %-24s %10s %10s %s\n",
+ $index,
+ defined $descr ? $descr : '',
+ defined $in ? $in/1000.0 : '-',
+ defined $out ? $out/1000.0 : '-',
+ defined $comment ? $comment : '';
+ },
+ qw(ifDescr locIfInBitsSec locIfOutBitsSec locIfDescr));
diff --git a/test/snmpspeed.pl b/test/snmpspeed.pl
new file mode 100644
index 0000000..dc369bb
--- /dev/null
+++ b/test/snmpspeed.pl
@@ -0,0 +1,213 @@
+#!/usr/local/bin/perl
+# -*- mode: Perl -*-
+##################################################################
+# Config file creator
+##################################################################
+# Created by Tobias Oetiker <oetiker at ee.ethz.ch>
+# this produces a config file for one router, by bulling info
+# off the router via snmp
+#################################################################
+#
+# Distributed under the GNU copyleft
+#
+# $Id: snmpspeed.pl,v 1.3 2001-11-14 13:24:32 leinen Exp $
+#
+use SNMP_Session "0.54";
+$SNMP_Session::default_timeout = 0.2;
+$SNMP_Session::default_backoff = 1.5;
+
+use BER "0.51";
+
+%snmpget::OIDS = ( 'sysDescr' => '1.3.6.1.2.1.1.1.0',
+ 'sysContact' => '1.3.6.1.2.1.1.4.0',
+ 'sysName' => '1.3.6.1.2.1.1.5.0',
+ 'sysLocation' => '1.3.6.1.2.1.1.6.0',
+ 'sysUptime' => '1.3.6.1.2.1.1.3.0',
+ 'ifNumber' => '1.3.6.1.2.1.2.1.0',
+ ###################################
+ # add the ifNumber ....
+ # add the ifNumber ....
+ 'ifDescr' => '1.3.6.1.2.1.2.2.1.2',
+ 'ifType' => '1.3.6.1.2.1.2.2.1.3',
+ 'ifIndex' => '1.3.6.1.2.1.2.2.1.1',
+ 'ifInErrors' => '1.3.6.1.2.1.2.2.1.14',
+ 'ifOutErrors' => '1.3.6.1.2.1.2.2.1.20',
+ 'ifInOctets' => '1.3.6.1.2.1.2.2.1.10',
+ 'ifOutOctets' => '1.3.6.1.2.1.2.2.1.16',
+ 'ifInDiscards' => '1.3.6.1.2.1.2.2.1.13',
+ 'ifOutDiscards' => '1.3.6.1.2.1.2.2.1.19',
+ 'ifInUcastPkts' => '1.3.6.1.2.1.2.2.1.11',
+ 'ifOutUcastPkts' => '1.3.6.1.2.1.2.2.1.17',
+ 'ifInNUcastPkts' => '1.3.6.1.2.1.2.2.1.12',
+ 'ifOutNUcastPkts' => '1.3.6.1.2.1.2.2.1.18',
+ 'ifInUnknownProtos' => '1.3.6.1.2.1.2.2.1.15',
+ 'ifOutQLen' => '1.3.6.1.2.1.2.2.1.21',
+ 'ifSpeed' => '1.3.6.1.2.1.2.2.1.5',
+ 'ifDescr' => '1.3.6.1.2.1.2.2.1.2',
+ 'ifType' => '1.3.6.1.2.1.2.2.1.3',
+ 'ifIndex' => '1.3.6.1.2.1.2.2.1.1',
+ 'ifSpeed' => '1.3.6.1.2.1.2.2.1.5',
+ 'ifOperStatus' => '1.3.6.1.2.1.2.2.1.8',
+ 'ifAdminStatus' => '1.3.6.1.2.1.2.2.1.7',
+ # up 1, down 2, testing 3
+ 'ipAdEntAddr' => '1.3.6.1.2.1.4.20.1.1',
+ 'ipAdEntIfIndex' => '1.3.6.1.2.1.4.20.1.2',
+ 'sysObjectID' => '1.3.6.1.2.1.1.2.0',
+ 'CiscolocIfDescr' => '1.3.6.1.4.1.9.2.2.1.1.28',
+ 'CiscoportIndex' => '1.3.6.1.4.1.9.5.1.4.1.1.2',
+ 'CiscoportName' => '1.3.6.1.4.1.9.5.1.4.1.1.4',
+ 'CiscoportIfIndex' => '1.3.6.1.4.1.9.5.1.4.1.1.11',
+ 'CiscoswPortName' => '1.3.6.1.4.1.437.1.1.3.3.1.1.3',
+
+ );
+
+
+
+
+sub main {
+
+ my $session = SNMP_Session->open ('ezci1.ethz.ch', 'public', 161)
+ || die "open SNMP session: $SNMP_Session::errmsg";
+ $|=1;
+for (my $i=0;$ i < 100; $i++){
+ print "$i, ";# if $i % 10 ==0;
+ my($ifinoct) = snmpget($session,'ifInOctets.1');
+ $ifinoct = snmpget($session,'ifInOctets.2');
+}
+ $session->close ()
+ || die "close SNMP session: $SNMP_Session::errmsg";
+}
+main;
+exit(0);
+
+sub snmpget {
+ my($session, at vars) = @_;
+ my(@enoid, $var,$response, $bindings, $binding, $value, $inoid,$outoid,
+ $upoid,$oid, at retvals);
+ foreach $var (@vars) {
+ if ($var =~ /^([a-z]+)/i) {
+ my $oid = $snmpget::OIDS{$1};
+ if ($oid) {
+ $var =~ s/$1/$oid/;
+ } else {
+ die "Unknown SNMP var $var\n"
+ }
+ }
+ print "SNMPGET OID: $var\n" if $main::DEBUG >5;
+ push @enoid, encode_oid((split /\./, $var));
+ }
+ srand();
+ if ($session->get_request_response(@enoid)) {
+ $response = $session->pdu_buffer;
+ ($bindings) = $session->decode_get_response ($response);
+ while ($bindings) {
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($oid,$value) = decode_by_template ($binding, "%O%@");
+ my $tempo = pretty_print($value);
+ $tempo=~s/\t/ /g;
+ $tempo=~s/\n/ /g;
+ $tempo=~s/^\s+//;
+ $tempo=~s/\s+$//;
+ push @retvals, $tempo;
+ }
+ return (@retvals);
+ } else {
+ return (-1,-1);
+ }
+}
+
+
+sub snmpgettable{
+ my($host,$community,$var) = @_;
+ my($next_oid,$enoid,$orig_oid,
+ $response, $bindings, $binding, $value, $inoid,$outoid,
+ $upoid,$oid, at table,$tempo,$tempoO);
+ die "Unknown SNMP var $var\n"
+ unless $snmpget::OIDS{$var};
+
+ $orig_oid = encode_oid(split /\./, $snmpget::OIDS{$var});
+ $enoid=$orig_oid;
+ srand();
+ my $session = SNMP_Session->open ($host ,
+ $community,
+ 161);
+ for(;;) {
+ if ($session->getnext_request_response(($enoid))) {
+ $response = $session->pdu_buffer;
+ ($bindings) = $session->decode_get_response ($response);
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($next_oid,$value) = decode_by_template ($binding, "%O%@");
+ # quit once we are outside the table
+ last unless BER::encoded_oid_prefix_p($orig_oid,$next_oid);
+ $tempo = pretty_print($value);
+ #print "$var: '$tempo'\n";
+ $tempo=~s/\t/ /g;
+ $tempo=~s/\n/ /g;
+ $tempo=~s/^\s+//;
+ $tempo=~s/\s+$//;
+ push @table, $tempo;
+
+ } else {
+ die "No answer from $ARGV[0]\n";
+ }
+ $enoid=$next_oid;
+ }
+ $session->close ();
+ return (@table);
+}
+
+sub snmpgettable2{
+ my($host,$community,$var) = @_;
+ my($next_oid,$enoid,$orig_oid,
+ $response, $bindings, $binding, $value, $inoid,$outoid,
+ $upoid,$oid, at table,$tempo,$tempoO);
+ die "Unknown SNMP var $var\n"
+ unless $snmpget::OIDS{$var};
+
+ $orig_oid = encode_oid(split /\./, $snmpget::OIDS{$var});
+ $enoid=$orig_oid;
+ $tempoO = pretty_print($orig_oid);
+ $tempoO=~s/\t/ /g;
+ $tempoO=~s/\n/ /g;
+ $tempoO=~s/^\s+//;
+ $tempoO=~s/\s+$//;
+ srand();
+ my $session = SNMP_Session->open ($host ,
+ $community,
+ 161);
+ for(;;) {
+ if ($session->getnext_request_response(($enoid))) {
+ $response = $session->pdu_buffer;
+ ($bindings) = $session->decode_get_response ($response);
+ ($binding,$bindings) = decode_sequence ($bindings);
+ ($next_oid,$value) = decode_by_template ($binding, "%O%@");
+ # quit once we are outside the table
+ last unless BER::encoded_oid_prefix_p($orig_oid,$next_oid);
+ $tempo = pretty_print($next_oid);
+ $tempo=~s/\t/ /g;
+ $tempo=~s/\n/ /g;
+ $tempo=~s/^\s+//;
+ $tempo=~s/\s+$//;
+ $tempo=substr($tempo,length($tempoO)+1);
+ #print "$var: '$tempo'\n";
+ push @table, $tempo;
+
+ } else {
+ die "No answer from $ARGV[0]\n";
+ }
+ $enoid=$next_oid;
+ }
+ $session->close ();
+ return (@table);
+}
+
+sub fmi {
+ my($number) = $_[0];
+ my(@short);
+ @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]);
+}
diff --git a/test/snmptrap.note b/test/snmptrap.note
new file mode 100644
index 0000000..a511146
--- /dev/null
+++ b/test/snmptrap.note
@@ -0,0 +1,4 @@
+The first two arguments to snmptrap are the trap destination and the
+community string. The next argument is the Trap OID, the source's
+hostname or IP address as a dotted-quad, the generic trap ID, the
+specific trap ID, then the variable bindings (OID and value pairs).
diff --git a/test/snmptrap.pl b/test/snmptrap.pl
new file mode 100644
index 0000000..3b926b1
--- /dev/null
+++ b/test/snmptrap.pl
@@ -0,0 +1,83 @@
+#! /usr/local/bin/perl5
+# -*- mode: Perl -*-
+BEGIN{
+$main::OS = 'UNIX';
+#$main::OS = 'NT';
+#$main::OS = 'VMS';
+##################################################################
+ # The path separator is a slash, backslash or semicolon, depending
+ # on the platform.
+ $main::SL = {
+ UNIX=>'/',
+ WINDOWS=>'\\',
+ NT=>'\\',
+ VMS=>''
+ }->{$main::OS};
+
+ # The search path separator is a colon or semicolon depending on the
+ # operating system.
+ $main::PS = {
+ UNIX=>':',
+ WINDOWS=>';',
+ NT=>';',
+ VMS=>':'
+ }->{$main::OS};
+
+ # We need to find the place where this is installed, and
+ # then take the .pm programms from there.
+ $main::binpath ="";
+ if ($0 =~ /^(.+\Q${main::SL}\E)/) {
+ $main::binpath="$1";
+ } else {
+ foreach $pathname ( split ${main::PS}, $ENV{'PATH'}) {
+ if ((($main::OS eq 'NT') &&
+ (-e "$pathname${main::SL}$0")) ||
+ (-x "$pathname${main::SL}$0")) {
+ $main::binpath=$pathname;
+ last;
+ }
+ }
+ }
+ die "ERROR: Can\'t find location of mrtg executable\n"
+ unless $main::binpath;
+ unshift (@INC,$main::binpath);
+}
+
+# The older perls tend to behave peculiar with
+# large integers ...
+require 5.003;
+
+if ($main::OS eq 'UNIX' || $main::OS eq 'NT') {
+ use SNMP_util "0.54";
+ $main::SNMPDEBUG =0;
+}
+
+use strict;
+
+$main::DEBUG=0;
+
+sub main {
+
+ my($trapid, $sev, $message);
+ my($machine, $ret);
+ # unbuffer stdout to see everything immediately
+ $|=1 if $main::DEBUG;
+
+ $trapid = 1100;
+ $sev = "Major";
+ $message = "MCM -- I'm testing, please ignore";
+ $machine = `hostname` ;
+ chop($machine);
+
+ $ret = &snmptrap("dizzy.unx.sas.com", "",
+ "1.3.6.1.4.1.11.2.17.1", $machine, 6, $trapid,
+ "1.3.6.1.4.1.11.2.17.2.1.0", "Integer", 14,
+ "1.3.6.1.4.1.11.2.17.2.2.0", "OctetString", $machine,
+ "1.3.6.1.4.1.11.2.17.2.4.0", "OctetString", $message,
+ "1.3.6.1.4.1.11.2.17.2.5.0", "OctetString", $sev);
+
+ print "ret = <$ret>\n";
+}
+
+main;
+exit(0);
diff --git a/test/sorrento-nest-list b/test/sorrento-nest-list
new file mode 100644
index 0000000..ec8dfcd
--- /dev/null
+++ b/test/sorrento-nest-list
@@ -0,0 +1,85 @@
+#!/usr/local/bin/perl -w
+
+use SNMP_util;
+
+sub process_nest ($$);
+
+snmpmapOID ("slotCardName", "1.3.6.1.4.1.2522.1.1.2.1.1.2",
+ "slotCardType", "1.3.6.1.4.1.2522.1.1.2.1.1.3",
+ "slotCardStatus", "1.3.6.1.4.1.2522.1.1.2.1.1.4",
+ "slotIpAddress", "1.3.6.1.4.1.2522.1.1.3.1.1.2",
+ "cardName", "1.3.6.1.4.1.2522.1.1.3.1.1.3");
+
+my @nestmasters =
+ (['mCE11','public at 130.59.48.16'],
+ ['mCE13','public at 130.59.48.17'],
+ ['mLS11','public at 130.59.48.80'],
+ ['mLS13','public at 130.59.48.81'],
+ ['mBE11','public at 130.59.48.144'],
+ ['mBE13','public at 130.59.48.145'],
+ ['mBA11','public at 130.59.48.208'],
+ ['mEZ11','public at 130.59.49.16'],
+ );
+
+## Override here if you just want to re-generate the names for one nest.
+# @nestmasters = (['mCE11','public at 130.59.48.16']);
+
+my %short_types =
+ qw(GMI-1GSX c
+ GMI-1GLX c
+ GMOA-1A A
+ GMTR-15 c
+ GMTR-25 c
+ GMOX-06 c
+ GMOX-15 c
+ GMOX-25 c
+ GMOX-ER c
+ GM-GE2 c
+ GM-GE2-2.5G-A c
+ GMGE2-2.5G-M c
+ GM-GE4-2.5G-A c
+ GMCR-10GL-LR c);
+
+foreach (@nestmasters) {
+ process_nest ($_->[0], $_->[1]);
+}
+1;
+
+sub process_nest ($$) {
+ my ($name, $dest) = @_;
+ my %slot_name = ();
+ my %slot_type = ();
+
+ my ($nest_ip_address) = ($dest =~ /.*@(.*)$/);
+ (out_ip ($name.'-M0', $nest_ip_address, undef),
+ print "$name\t\tIN\tCNAME\t$name-M0\n")
+ if defined $nest_ip_address;
+ snmpmaptable ($dest, sub () {
+ my ($slotCardSlot, $slotCardName, $slotCardType, $slotCardStatus) = @_;
+ return if $slotCardStatus == 3; # empty
+ $slot_name{$slotCardSlot} = $slotCardName;
+ $slot_type{$slotCardSlot} = $slotCardType;
+ },
+ qw(slotCardName slotCardType slotCardStatus));
+ snmpmaptable ($dest, sub () {
+ my ($slotIndex, $slotIpAddress, $cardName) = @_;
+ return unless exists $slot_name{$slotIndex};
+ return if $slot_type{$slotIndex} eq 'N.A.';
+
+ my $short_type = $short_types{$slot_type{$slotIndex}};
+ if (!defined $short_type) {
+ warn "unknown type $slot_type{$slotIndex}";
+ $short_type = 'other';
+ }
+ out_ip ($name.'-'.$short_type.$slotIndex, $slotIpAddress, $slot_type{$slotIndex});
+ #print "$name: slotIndex $slotIndex (name $slot_name{$slotIndex} type $slot_type{$slotIndex}) ip $slotIpAddress type $cardName\n";
+ },
+ qw(slotIpAddress cardName));
+ print ";;\n";
+}
+
+sub out_ip ($$$) {
+ my ($name, $ip, $type) = @_;
+ my $comment = $type ? "\t;$type" : '';
+ print "$name\tIN\tA\t$ip$comment\n",
+}
diff --git a/test/trap-listener b/test/trap-listener
index 2bc6112..23c176f 100755
--- a/test/trap-listener
+++ b/test/trap-listener
@@ -10,10 +10,11 @@ package main;
use strict;
-use SNMP_Session;
+use SNMP_Session "1.14"; # requires receive_trap_1()
use SNMP_util;
use BER;
use Socket;
+use Socket6;
### Forward declarations
sub print_trap ($$);
@@ -23,13 +24,14 @@ sub pretty_addr ($ );
sub hostname ($ );
sub fromOID ($ );
sub fromOID_aux ($$);
-sub really_pretty_oid ($);
+sub really_pretty_oid ($ );
my $port = 162;
my $print_community = 0;
my $print_port = 0;
my $print_hostname = 1;
+my $ipv4only = 0;
register_pretty_printer {BER::object_id_tag(), \&really_pretty_oid};
@@ -42,6 +44,9 @@ while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
} elsif ($ARGV[0] eq '-h') {
usage (0);
exit 0;
+ } elsif ($ARGV[0] eq '-4') {
+ shift @ARGV;
+ $ipv4only = 1;
} else {
usage (1);
}
@@ -49,16 +54,15 @@ while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
snmpLoad_OID_Cache($SNMP_util::CacheFile);
die unless SNMP_util::toOID("mib-2") eq SNMP_util::toOID ("1.3.6.1.2.1");
%SNMP_util::revOIDS = reverse %SNMP_util::OIDS unless %SNMP_util::revOIDS;
-my $session = SNMPv2c_Session->open_trap_session ($port)
+my $session = SNMPv2c_Session->open_trap_session ($port, $ipv4only)
or die "couldn't open trap session";
$SNMP_Session::suppress_warnings = 1; # We print all error messages ourselves.
-my ($trap, $sender, $sender_port);
+my ($trap, $sender);
-while (($trap, $sender, $sender_port) = $session->receive_trap ()) {
+while (($trap, $sender) = $session->receive_trap_1 ()) {
my $now_string = localtime time;
print "$now_string ";
- print pretty_addr (inet_ntoa ($sender));
- print ".$sender_port" if $print_port;
+ print pretty_addr ($sender);
print "\n";
print_trap ($session, $trap);
}
@@ -106,27 +110,71 @@ sub usage ($) {
Usage: $0 [-p port]
$0 -h
- -h print this usage message and exit.
-
- -p port Select the UDP port on which the program will listen
- for SNMP traps. The default port is 162.
+ -h Print this usage message and exit.
+ -p port Listen for traps on a specific UDP port. The default is 162.
+ -4 Listen for IPv4 packets only.
EOM
exit (1) if $_[0];
}
-sub pretty_addr ($ ) {
- my ($addr) = @_;
- my ($hostname,$aliases,$addrtype,$length, at addrs)
- = gethostbyaddr (inet_aton ($addr), AF_INET);
- $hostname ? $hostname." [".$addr."]" : $addr;
+sub make_sockaddr ($$) {
+ my ($af, $addr) = @_;
+ if ($af == AF_INET) {
+ return pack_sockaddr_in 0, $addr;
+ } elsif ($af == AF_INET6) {
+ return pack_sockaddr_in6 0, $addr;
+ } else {
+ die "Unsupported address family $af";
+ }
}
-sub hostname ($ ) {
- my ($addr) = @_;
- my ($hostname,$aliases,$addrtype,$length, at addrs)
- = gethostbyaddr (inet_aton ($addr), AF_INET);
- $hostname || "[".$addr."]";
+### pretty_addr SOCKADDR
+###
+### Return a pretty representation of the given socket address.
+### If $print_hostname is non-zero, try to resolve the host part
+### of the address to a hostname. The numeric form of the address
+### will be included in any case. If $print_port is non-zero, the
+### port is also included in numerical form.
+###
+### For example, assuming that addresses 192.0.2.1 and 2001:db8::1
+### both map to hostname "foo.example", this function would return the
+### following results:
+###
+### pretty_addr (sockaddr_in (1234, inet_aton ("192.0.2.1")))
+### if $print_hostname $print_port
+### => '192.0.2.1' ==0 ==0
+### => 'foo.example [192.0.2.1]' !=0 ==0
+### => '192.0.2.1.1234' ==0 !=0
+### => 'foo.example [192.0.2.1].1234' !=0 !=0
+###
+### pretty_addr (sockaddr_in6 (1234, inet_pton (AF_INET6, "2001:db8::1")))
+### if $print_hostname $print_port
+### => '2001:db8::1' ==0 ==0
+### => 'foo.example [2001:db8::1]' !=0 ==0
+### => '2001:db8::1.1234' ==0 !=0
+### => 'foo.example [2001:db8::1].1234' !=0 !=0
+###
+### Handling of IPv4-mapped IPv6 addresses
+###
+### When receiving an IPv4-mapped IPv6 address, this routine will
+### print the embedded IPv4 address in the numeric part. The mapping
+### to a hostname should also work, because that's what getnameinfo()
+### is specified to do. Therefore,
+###
+### pretty_addr (sockaddr_in6 (1234, inet_pton (AF_INET6, "::ffff:192.0.2.1")))
+### should always return the same string as
+### pretty_addr (sockaddr_in (1234, inet_aton ("192.0.2.1")))
+###
+sub pretty_addr ($ ) {
+ my ($sockaddr) = @_;
+ my ($hostname, $port, $result);
+ ($result, $port) = getnameinfo ($sockaddr, NI_NUMERICHOST | NI_NUMERICSERV);
+ $result = $1 if $result =~ /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i;
+ ($hostname) = getnameinfo ($sockaddr) if $print_hostname;
+ $result = $hostname." [".$result."]" if $hostname;
+ $result .= '.'.$port if $print_port;
+ return $result;
}
sub fromOID ($ ) {
diff --git a/test/v6-list-prefixes b/test/v6-list-prefixes
new file mode 100644
index 0000000..531e62f
--- /dev/null
+++ b/test/v6-list-prefixes
@@ -0,0 +1,13 @@
+#!/usr/local/bin/perl -w
+
+use SNMP_util;
+
+snmpmapOID (
+ "cIpv6InterfaceEffectiveMtu", "1.3.6.1.4.1.9.10.86.1.2.3.1.2",
+ "cIpv6InterfaceReasmMaxSize", "1.3.6.1.4.1.9.10.86.1.2.3.1.3",
+ "cIpv6InterfaceIdentifier", "1.3.6.1.4.1.9.10.86.1.2.3.1.4",
+ "cIpv6InterfaceIdentifierLength", "1.3.6.1.4.1.9.10.86.1.2.3.1.5",
+ "cIpv6InterfacePhysicalAddress", "1.3.6.1.4.1.9.10.86.1.2.3.1.6",
+ );
+
+1;
diff --git a/test/vc-counters.pl b/test/vc-counters.pl
new file mode 100644
index 0000000..8b27e91
--- /dev/null
+++ b/test/vc-counters.pl
@@ -0,0 +1,446 @@
+#!/usr/local/bin/perl -w
+######################################################################
+### Observe interface counters in real time.
+######################################################################
+### Copyright (c) 1995-2000, Simon Leinen.
+###
+### This program is free software; you can redistribute it under the
+### "Artistic License" included in this distribution (file "Artistic").
+######################################################################
+### Author: Simon Leinen <simon at switch.ch>
+### Date Created: 21-Feb-1999
+###
+### Real-time full-screen display of the octet and (Cisco-specific)
+### CRC error counters on interfaces of an SNMP-capable node
+###
+### Description:
+###
+### Call this script with "-h" to learn about command usage.
+###
+### The script will poll the RFC 1213 ifTable at specified intervals
+### (default is every five seconds).
+###
+### For each interface except for those that are down, a line is
+### written to the terminal which lists the interfaces name (ifDescr),
+### well as the input and output transfer rates, as computed from the
+### deltas of the respective octet counts since the last sample.
+###
+### "Alarms"
+###
+### When an interface is found to have had CRC errors in the last
+### sampling interval, or only output, but no input traffic, it is
+### shown in inverse video. In addition, when a link changes state
+### (from normal to inverse or vice versa), a bell character is sent
+### to the terminal.
+###
+### Miscellaneous
+###
+### Note that on the very first display, the actual SNMP counter
+### values are displayed. THOSE ABSOLUTE COUNTER VALUES HAVE NO
+### DEFINED SEMANTICS WHATSOEVER. However, in some versions of
+### Cisco's software, the values seem to correspond to the total
+### number of counted items since system boot (modulo 2^32). This can
+### be useful for certain kinds of slowly advancing counters (such as
+### CRC errors, hopefully).
+###
+### The topmost screen line shows the name of the managed node, as
+### well as a few hard-to-explain items I found useful while debugging
+### the script.
+###
+### Please send any patches and suggestions for improvement to the
+### author (see e-mail address above). Hope you find this useful!
+###
+### Original Purpose:
+###
+### This script should serve as an example of how to "correctly"
+### traverse the rows of a table. This functionality is implemented in
+### the map_table() subroutine. The example script displays a few
+### columns of the RFC 1213 interface table and Cisco's locIfTable. The
+### tables share the same index, so they can be handled by a single
+### invocation of map_table().
+###
+require 5.003;
+
+use strict;
+
+use BER;
+use SNMP_Session "0.67"; # requires map_table_4
+use POSIX; # for exact time
+use Curses;
+use Math::BigInt;
+
+### Forward declarations
+sub out_interface ($$$$$$@);
+sub pretty_bps ($$);
+sub usage ($ );
+
+my $version = '1';
+
+my $desired_interval = 5.0;
+
+my $all_p = 0;
+
+my $port = 161;
+
+my $max_repetitions = 0;
+
+my $suppress_output = 0;
+
+my $debug = 0;
+
+my $show_out_discards = 0;
+
+my $cisco_p = 0;
+
+my $counter64_p = 0;
+
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
+ if ($ARGV[0] =~ /^-v/) {
+ if ($ARGV[0] eq '-v') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] eq '1') {
+ $version = '1';
+ } elsif ($ARGV[0] eq '2c') {
+ $version = '2c';
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-m/) {
+ if ($ARGV[0] eq '-m') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+$/) {
+ $max_repetitions = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-p/) {
+ if ($ARGV[0] eq '-p') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+$/) {
+ $port = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] =~ /^-t/) {
+ if ($ARGV[0] eq '-t') {
+ shift @ARGV;
+ usage (1) unless defined $ARGV[0];
+ } else {
+ $ARGV[0] = substr($ARGV[0], 2);
+ }
+ if ($ARGV[0] =~ /^[0-9]+(\.[0-9]+)?$/) {
+ $desired_interval = $ARGV[0];
+ } else {
+ usage (1);
+ }
+ } elsif ($ARGV[0] eq '-a') {
+ $all_p = 1;
+ } elsif ($ARGV[0] eq '-c') {
+ $cisco_p = 1;
+ } elsif ($ARGV[0] eq '-l') {
+ $counter64_p = 1;
+ } elsif ($ARGV[0] eq '-n') {
+ $suppress_output = 1;
+ } elsif ($ARGV[0] eq '-d') {
+ $suppress_output = 1;
+ $debug = 1;
+ } elsif ($ARGV[0] eq '-D') {
+ $show_out_discards = 1;
+ } elsif ($ARGV[0] eq '-h') {
+ usage (0);
+ exit 0;
+ } else {
+ usage (1);
+ }
+ shift @ARGV;
+}
+my $host = shift @ARGV || usage (1);
+my $community = shift @ARGV || "public";
+usage (1) if $#ARGV >= $[;
+
+my $ifDescr = [1,3,6,1,2,1,2,2,1,2];
+my $ifAdminStatus = [1,3,6,1,2,1,2,2,1,7];
+my $ifOperStatus = [1,3,6,1,2,1,2,2,1,8];
+my $ifInOctets = [1,3,6,1,2,1,2,2,1,10];
+my $ifOutOctets = [1,3,6,1,2,1,2,2,1,16];
+my $ifInUcastPkts = [1,3,6,1,2,1,2,2,1,11];
+my $ifOutUcastPkts = [1,3,6,1,2,1,2,2,1,17];
+my $ifOutDiscards = [1,3,6,1,2,1,2,2,1,19];
+my $ifAlias = [1,3,6,1,2,1,31,1,1,1,18];
+## Counter64 variants
+my $ifHCInOctets = [1,3,6,1,2,1,31,1,1,1,6];
+my $ifHCOutOctets = [1,3,6,1,2,1,31,1,1,1,10];
+## Cisco-specific variables enabled by `-c' option
+my $locIfInCRC = [1,3,6,1,4,1,9,2,2,1,1,12];
+my $locIfOutCRC = [1,3,6,1,4,1,9,2,2,1,1,12];
+
+my $clock_ticks = POSIX::sysconf( &POSIX::_SC_CLK_TCK );
+
+my $win = new Curses
+ unless $suppress_output;
+
+my %old;
+my $sleep_interval = $desired_interval + 0.0;
+my $interval;
+my $linecount;
+
+sub rate_32 ($$$@) {
+ my ($old, $new, $interval, $multiplier) = @_;
+ $multiplier = 1 unless defined $multiplier;
+ my $diff = $new-$old;
+ if ($diff < 0) {
+ $diff += (2**32);
+ }
+ return $diff / $interval * $multiplier;
+}
+
+sub rate_64 ($$$@) {
+ my ($old, $new, $interval, $multiplier) = @_;
+ $multiplier = 1 unless defined $multiplier;
+ return 0 if $old == $new;
+ my $diff = Math::BigInt->new ($new-$old);
+ if ($diff < 0) {
+ $diff = $diff->add (2**64);
+ }
+ ## hrm. Why is this so complicated?
+ ## I want a real programming language (such as Lisp).
+ my $result = $diff->bnorm () / $interval * $multiplier;
+ return $result;
+}
+
+sub rate ($$$$@) {
+ my ($old, $new, $interval, $counter64_p, $multiplier) = @_;
+ $multiplier = 1 unless defined $multiplier;
+ return $counter64_p
+ ? rate_64 ($old, $new, $interval, $multiplier)
+ : rate_32 ($old, $new, $interval, $multiplier);
+}
+
+sub rate_or_0 ($$$$$) {
+ my ($old, $new, $interval, $counter64_p, $multiplier) = @_;
+ return defined $new
+ ? rate ($old, $new, $interval, $counter64_p, $multiplier)
+ : 0;
+}
+
+sub out_interface ($$$$$$@) {
+ my ($index, $descr, $admin, $oper, $in, $out);
+ my ($crc, $comment);
+ my ($drops);
+ my ($clock) = POSIX::times();
+ my $alarm = 0;
+
+ ($index, $descr, $admin, $oper, $in, $out, $comment, @_) = @_;
+ ($crc, @_) = @_ if $cisco_p;
+ ($drops, @_) = @_ if $show_out_discards;
+
+ grep (defined $_ && ($_=pretty_print $_),
+ ($descr, $admin, $oper, $in, $out, $crc, $comment, $drops));
+ $win->clrtoeol ()
+ unless $suppress_output;
+ return unless $all_p || defined $oper && $oper == 1; # up
+ return unless defined $in && defined $out;
+
+ if (!defined $old{$index}) {
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %-24s %10s %10s",
+ $index,
+ defined $descr ? $descr : '',
+ defined $in ? $in : '-',
+ defined $out ? $out : '-'))
+ unless $suppress_output;
+ if ($show_out_discards) {
+ $win->addstr (sprintf (" %8s",
+ defined $drops ? $drops : '-'))
+ unless $suppress_output;
+ }
+ if ($cisco_p) {
+ $win->addstr (sprintf (" %10s",
+ defined $crc ? $crc : '-'))
+ unless $suppress_output;
+ }
+ $win->addstr (sprintf (" %s",
+ defined $comment ? $comment : ''))
+ unless $suppress_output;
+ } else {
+ my $old = $old{$index};
+
+ $interval = ($clock-$old->{'clock'}) * 1.0 / $clock_ticks;
+ my $d_in = rate_or_0 ($old->{'in'}, $in, $interval, $counter64_p, 8);
+ my $d_out = rate_or_0 ($old->{'out'}, $out, $interval, $counter64_p, 8);
+ my $d_drops = rate_or_0 ($old->{'drops'}, $drops, $interval, 0, 1);
+ my $d_crc = rate_or_0 ($old->{'crc'}, $crc, $interval, 0, 1);
+ $alarm = ($d_crc != 0)
+ || 0 && ($d_out > 0 && $d_in == 0);
+ print STDERR "\007" if $alarm && !$old->{'alarm'};
+ print STDERR "\007" if !$alarm && $old->{'alarm'};
+ $win->standout() if $alarm && !$suppress_output;
+ $win->addstr ($linecount, 0,
+ sprintf ("%2d %-24s %s %s",
+ $index,
+ defined $descr ? $descr : '',
+ pretty_bps ($in, $d_in),
+ pretty_bps ($out, $d_out)))
+ unless $suppress_output;
+ if ($show_out_discards) {
+ $win->addstr (sprintf (" %8.1f %s",
+ defined $drops ? $d_drops : 0))
+ unless $suppress_output;
+ }
+ if ($cisco_p) {
+ $win->addstr (sprintf (" %10.1f",
+ defined $crc ? $d_crc : 0))
+ unless $suppress_output;
+ }
+ $win->addstr (sprintf (" %s",
+ defined $comment ? $comment : ''))
+ unless $suppress_output;
+ $win->standend() if $alarm && !$suppress_output;
+ }
+ $old{$index} = {'in' => $in,
+ 'out' => $out,
+ 'crc' => $crc,
+ 'drops' => $drops,
+ 'clock' => $clock,
+ 'alarm' => $alarm};
+ ++$linecount;
+ $win->refresh ()
+ unless $suppress_output;
+}
+
+sub pretty_bps ($$) {
+ my ($count, $bps) = @_;
+ if (! defined $count) {
+ return ' - ';
+ } elsif ($bps > 1000000) {
+ return sprintf ("%8.4f M", $bps/1000000);
+ } elsif ($bps > 1000) {
+ return sprintf ("%9.1fk", $bps/1000);
+ } else {
+ return sprintf ("%10.0f", $bps);
+ }
+}
+
+$win->erase ()
+ unless $suppress_output;
+my $session =
+ ($version eq '1' ? SNMPv1_Session->open ($host, $community, $port)
+ : $version eq '2c' ? SNMPv2c_Session->open ($host, $community, $port)
+ : die "Unknown SNMP version $version")
+ || die "Opening SNMP_Session";
+$session->debug (1) if $debug;
+
+### max_repetitions:
+###
+### We try to be smart about the value of $max_repetitions. Starting
+### with the session default, we use the number of rows in the table
+### (returned from map_table_4) to compute the next value. It should
+### be one more than the number of rows in the table, because
+### map_table needs an extra set of bindings to detect the end of the
+### table.
+###
+$max_repetitions = $session->default_max_repetitions
+ unless $max_repetitions;
+while (1) {
+ unless ($suppress_output) {
+ $win->addstr (0, 0, sprintf ("%-20s interval %4.1fs %d reps",
+ $host,
+ $interval || $desired_interval,
+ $max_repetitions));
+ $win->standout();
+ $win->addstr (1, 0,
+ sprintf (("%2s %-24s %10s %10s"),
+ "ix", "name",
+ "bits/s", "bits/s"));
+ if ($show_out_discards) {
+ $win->addstr (sprintf ((" %8s"),
+ "drops/s"));
+ }
+ if ($cisco_p) {
+ $win->addstr (sprintf ((" %10s"), "pkts/s"));
+ }
+ $win->addstr (sprintf ((" %s"), "description"));
+ $win->addstr (2, 0,
+ sprintf (("%2s %-24s %10s %10s"),
+ "", "",
+ "in", "out"));
+ if ($show_out_discards) {
+ $win->addstr (sprintf ((" %8s"),
+ ""));
+ }
+ if ($cisco_p) {
+ $win->addstr (2, 0,
+ sprintf ((" %10s %s"),
+ "CRC",
+ ""));
+ }
+ $win->clrtoeol ();
+ $win->standend();
+ }
+ $linecount = 3;
+ my @oids = ($ifDescr,$ifAdminStatus,$ifOperStatus);
+ if ($counter64_p) {
+ @oids = (@oids,$ifHCInOctets,$ifHCOutOctets);
+ } else {
+ @oids = (@oids,$ifInOctets,$ifOutOctets);
+ }
+ @oids = (@oids,$ifAlias);
+ if ($cisco_p) {
+ push @oids, $locIfInCRC;
+ }
+ if ($show_out_discards) {
+ push @oids, $ifOutDiscards;
+ }
+ my $calls = $session->map_table_4
+ (\@oids, \&out_interface, $max_repetitions);
+ $max_repetitions = $calls + 1
+ if $calls > 0;
+ $sleep_interval -= ($interval - $desired_interval)
+ if defined $interval;
+ select (undef, undef, undef, $sleep_interval);
+}
+1;
+
+sub usage ($) {
+ warn <<EOM;
+Usage: $0 [-t secs] [-v (1|2c)] [-c] [-l] [-m max] [-p port] host [community]
+ $0 -h
+
+ -h print this usage message and exit.
+
+ -c also use Cisco-specific variables (locIfInCrc)
+
+ -l use 64-bit counters (requires SNMPv2 or higher)
+
+ -t secs specifies the sampling interval. Defaults to 5 seconds.
+
+ -v version can be used to select the SNMP version. The default
+ is SNMPv1, which is what most devices support. If your box
+ supports SNMPv2c, you should enable this by passing "-v 2c"
+ to the script. SNMPv2c is much more efficient for walking
+ tables, which is what this tool does.
+
+ -m max specifies the maxRepetitions value to use in getBulk requests
+ (only relevant for SNMPv2c).
+
+ -m port can be used to specify a non-standard UDP port of the SNMP
+ agent (the default is UDP port 161).
+
+ host hostname or IP address of a router
+
+ community SNMP community string to use. Defaults to "public".
+EOM
+ exit (1) if $_[0];
+}
diff --git a/test/verio-problem.pl b/test/verio-problem.pl
new file mode 100644
index 0000000..0116ff3
--- /dev/null
+++ b/test/verio-problem.pl
@@ -0,0 +1,35 @@
+#!/usr/bin/perl
+
+use BER;
+require 'SNMP_Session.pm';
+
+# Set $host to the name of the host whose SNMP agent you want
+# to talk to. Set $community to the community name under
+# which you want to talk to the agent. Set port to the UDP
+# port on which the agent listens (usually 161).
+
+$host = "vcp-nt.cp.verio.net";
+$community = "public";
+$port = "161";
+
+$session = SNMP_Session->open ($host, $community, $port)
+ || die "couldn't open SNMP session to $host";
+
+# Set $oid1, $oid2... to the BER-encoded OIDs of the MIB
+# variables you want to get.
+$oid = "1.3.6.1.2.1.1.1.0";
+
+%pretty_oids = ( encode_oid(1,3,6,1,2,1,1,1,0), "sysDescr.0" );
+
+if ($session->get_request_response (encode_oid (split '\.',$oid))) {
+ ($bindings) = $session->decode_get_response ($session->{pdu_buffer});
+
+ while ($bindings ne '') {
+ ($binding,$bindings) = &decode_sequence ($bindings);
+ ($oid,$value) = &decode_by_template ($binding, "%O%@");
+ print $pretty_oids{$oid}," => ",
+ &pretty_print ($value), "\n";
+ }
+} else {
+ die "No response from agent on $host";
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libsnmp-session-perl.git
More information about the Pkg-perl-cvs-commits
mailing list