snmptt_1.5/0000775000175000017500000000000014277306167011451 5ustar alexalexsnmptt_1.5/#sample-trap-daemon0000664000000000000000000000061314277306025015150 0ustar rootroot1092665195 server01.domain.com 192.168.1.1 .1.3.6.1.2.1.1.3.0 111:21:48:19.07 .1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.232.0.11003 .1.3.6.1.2.1.1.5.0 SERVER01 .1.3.6.1.4.1.232.11.2.11.1.0 0 .1.3.6.1.4.1.232.11.2.8.1.0 "Compaq Management Agents Test Trap sent - Friday, August 16, 2002 2:05:15 PM" .1.3.6.1.6.3.18.1.3.0 192.168.1.1 .1.3.6.1.6.3.18.1.4.0 public .1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.232 snmptt_1.5/#sample-unknown-trap-daemon0000664000000000000000000000051414277306025016645 0ustar rootroot1092665195 server01.domain.com 192.168.1.1 .1.3.6.1.2.1.1.3.0 111:21:48:19.07 .1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.0.0.0.0.0 .1.3.6.1.2.1.1.5.0 SERVER01 .1.3.6.1.4.1.232.11.2.11.1.0 0 .1.3.6.1.4.1.232.11.2.8.1.0 "Sample Unknown Trap" .1.3.6.1.6.3.18.1.3.0 192.168.1.1 .1.3.6.1.6.3.18.1.4.0 public .1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.232 snmptt_1.5/BUGS0000664000000000000000000000000114277306025012146 0ustar rootroot snmptt_1.5/COPYING0000664000000000000000000004313114277306025012531 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. snmptt_1.5/ChangeLog0000664000000000000000000015175114277306025013260 0ustar rootrootAug 17, 2022: Alex Burger - Changed version to snmptt_1.5 for release. Nov 27, 2021: Alex Burger - Changed version to snmptt_1.5beta2 for release. - Added reload support to the snmptt.service systemd file. This will allow you to use the 'systemctl reload snmptt' command to re-load the configuration. - Updated documentation on securing SNMPTT to ensure the snmptt user has read access to the configuration files. This is required when issuing a reload. Oct 2nd, 2021: Alex Burger - Fixed a bug that prevented the hostname from being extraced when IPv6 is disabled and the hostname is passed from Net-SNMP as UDP: [x.x.x.x]:xxxx->[x.x.x.x]:xxxx. Thanks to Danilo Godec for the fix. (issue #3). - Fixed a bug with IPv6 address handling for NODES in snmptt.conf. - Added --preexec and --preexec_file options to snmpttconvertmib. Feature request from issue #4 by Andrew Shark. - Fixed debug output bug with snmptthandler-embedded. Thanks to kazuo ito for the fix. PR #1. - Added PREEXEC support for unknown traps. Results are stored in the variable $pun. See the unknown_trap_preexec setting in snmptt.ini. Feature request from issue #3 by Andrew Shark. July 19th, 2021: Alex Burger - Fixed a bug that prevented snmptt from starting when debug mode was disabled (bug 48). April 11th, 2021: Alex Burger - Log skipped trap statistics only when unknown_trap_nodes_match_mode = 1. April 2nd, 2021: Alex Burger - Added unknown_trap_nodes_match_mode setting to allow you to change how traps are handled when they do not match due to MATCH and NODES. If set to 1, traps are considered skipped instead of unknown. Statistics now include the number of skipped traps. Thanks to Andrew Choo for the code. - Fixed a Perl warning issue with syslog logging. - Varbind types Gauge32 and Hex-STRING now have the 'Gauge32: ' and 'Hex-STRING: ' text removed for incoming traps in snmptthandler-embedded along with unicode line endings (Perl 5.10 and higher). Thanks to Andrew Choo for the code. - Added remote syslog support using the Perl module Log::Syslog::Fast and Log::Syslog::Constants. - Added log_format snmptt.ini setting to allow you to define the STDOUT, text log and eventlog text format. - Added syslog_format snmptt.ini setting to allow you to define the syslog text format. - Added variable substitution $j to pull out the enterprise number from the full enterprise OID. Example: For enterprise OID .1.3.6.1.4.1.232, $j would contain 232. March 29th, 2021: Alex Burger - Changed version to snmptt_1.5beta2 - Add support for glob (wildcard) for snmptt.conf file list. Thanks to Andrew Choo for the code. March 25th, 2021: Alex Burger - Changed version to snmptt_1.5beta1 for release March 21st, 2021: Alex Burger - Changed net_snmp_perl_best_guess default from 0 to 2 as any modern system should support this. March 20th, 2021: Alex Burger - Added support for IPv6. Feature must be enabled by setting ipv6enable = 1 in snmptt.ini. - Changed Perl module for DNS support from Socket to IO::Socket::IP and from functions aton/ntoa to getaddrinfo/getnameinfo to support IPv6. - Fixed a bug where DNS resolution was not working for enterprise variables when net_snmp_perl_enable was disabled. March 13th, 2021: Alex Burger - Used PerlCritic to check all source files. - Changed open() calls from two arguments to three. - Replace Perl bare words with variables. - Enabled Perl warnings. March 11th, 2021: Alex Burger - Replaced Perl smartmatch as it's not considered experiemental. March 10th, 2021: Alex Burger - Fix bug in readtrap function where it was not properly removing quotes around strings. If string happened to only have a starting quote, or would remove it and the last character of the string. - Fixed bug with debug mode that was causing some debug mode output even when debug mode was off. March 05th, 2021: Alex Burger - Added --exec_file to snmpttconvertmib to allow you to provide an EXEC command inside of a file instead of specifying on the command line. Useful for commands that include quotes so that you don't have to worry about escaping on the command line. Also allows you to define multiple EXEC lines instead of just one. - Added --exec_mode to snmpttconvertmib to allow you change how the EXEC line is built. Setting to 0 will append the format line to the end of the line (default). Setting to 1 does not append the format line to the end of the line. This is useful if you have added \$Fz to the --exec line so that SNMPTT can replace it with the FORMAT line. Setting to 2 is similar to 1, but instead of SNMPTT having to replace \$Fz with the FORMAT line, snmpttconvertmib will do the substitution. February 28th, 2021: Alex Burger - snmptt.ini can now be located in /etc/snmptt and is searched for at this location first. February 22nd, 2021: Alex Burger - Fixed a bug with daemon_uid that prevented SNMPTT from starting on FreeBSD (bug 47). Thanks to Christian Ullrich for the fix. - Added support for sub-second sleep for spool folder processing when sleep is set to less than 1. Thanks to Tyler Leeds for the code. - Fixed a bug where traps arriving with the hostname set to UNKNOWN were not being handled properly (bug 46). Thanks to Tom Griep for the fix. July 23rd, 2020: Alex Burger - Removed the daemon_gid option and enhanced the daemon_uid option so that it retreives the group membership for daemon_uid from the OS. This fixes a bug in 1.4.1 where it was not possible to define multiple group members with daemon_gid. June 29th, 2020: Alex Burger - Fixed a security issue with EXEC / PREXEC / unknown_trap_exec that could allow malicious shell code to be executed. Thanks to Toshiyuki Goto for reporting the issue. Thanks to Toshiyuki Goto and Jon Nistor for testing. - Fixed a bug with EXEC / PREXEC / unknown_trap_exec that caused commands to be run as root instead of the user defined in daemon_uid. - Added the snmptt.ini option daemon_gid to allow the gid to be set in addition to the uid. Defaults to 'nobody' if not defined. July 5th, 2020: Alex Burger - Fixed an issue with MATCH not matching integers properly (bug 41). - Fixed an issue where the agent IP address was not handled correctly when it's received from Net-SNMP as IpAddress:x.x.x.x. Bug 27. December 21st, 2016: Alex Burger - Fixed a race condition bug with snmptthander and snmptthandler-embedded which could cause traps to be missed. Spool files are now immediately locked after creation. If flock() is not supported, the spool file will be created with a temporary filename and then renamed after closing. Spool files must now start with # otherwise they are ignored. Thanks to Andrew Darmody for reporting the issue and providing a solution. Thanks to Kazuo Ito for an additional fix using flock. March 29th, 2015: Alex Burger - Fixed a bug with wildcard_expansion_separator which caused an issue when using wildcard separators that were longer than one character (bug 38). Thanks to Zalex_UA for the patch. - Changed version to snmptt1.5beta1 November 6th, 2013: Alex Burger - Changed version to snmptt_1.4 for release. March 18th, 2012: Alex Burger - Fixed bug with unknown_trap_exec_format which resulted in duplicate text when a custom format is used. (Bug #37) Oct 22nd, 2012: Alex Burger - Fixed bug with fix that was applied for bug 3567748 (syslog % escaping). - Changed version to snmptt1.4beta2 Oct 21th, 2012: Alex Burger - Release of snmptt1.4beta1 Oct 21th, 2012: Alex Burger - Fixed bug with snmptt hanging if the log message sent to syslog contained a % symbol. All %'s are now escaped before sending to syslog (3567748). Thanks to Edward Greenspan. - Fixed bug with LOGONLY severity (3567744). EXEC was being executed even if the trap had a severity of LOGONLY. - Fixed bug with not being able to write to the debug log file when running snmptt as non-root if the debug file didn't already exist with the correct permissions at startup. The ownership of snmptt.debug is now set to daemon_uid before switching to the new uid. Patch 3423525. Thanks to Matvey Marinin. Oct 30th, 2011: Alex Burger - Installation documentation updates (bug 3425999). Thanks to Brian Excarnate. - Fixed bug with MATCH where number ranges were not working (3397982). Thanks to Maex. - Fixed bug with syslog logging. Function was not being called properly (3166749). Oct 9th, 2010: Alex Burger - Fixed bug where if the spool directory is not defined, files may be deleted from the wrong folder (3020696). May 5th, 2010: Alex Burger - Fixed possible bug with MySQL. Put CONNECT string on one line. - Fixed bug where the hostname is not detected properly when snmptrapd is configured to not use DNS. - Fixed bug with syslog (2980512). Log entries were supposed to be logged with snmptt[pid] but instad of the pid it was actually the effective user ID. Thanks to Matteo P for the fix. - Fixed bug with multi-line traps (2915658). Thanks to AndrewB for the fix. - Added snmptt.ini option net_snmp_perl_cache_enable to enable caching of Net-SNMP Perl module OID and ENUM translations. This may speed up translations and reduce CPU load when net_snmp_perl_enable and translate_* options are enabled. April 23rd, 2010: Alex Burger - Fixed bug with MATCH. The PREEXEC $p variable could not be used with MATCH. PREEXEC is now executed first if MATCH contains $p. April 15th, 2010: Alex Burger - Fixed bug with snmptthandler-embedded where IP addresses and OIDs were not being detected properly because they contained 'OID:', 'IpAddress:' etc. February 16th, 2010: Alex Burger - Changed version to snmptt_1.4beta1 - Added patch 2943209 from Ricardo Duarte for snmpttconvertmib to add new format option 4 for trap name and variables similar to FORMAT $+*. November 15th, 2009: Alex Burger - Changed version to snmptt_1.3 for release. June 25th, 2009: Alex Burger - Changed version to snmptt_1.3beta2 for release. July 21th, 2009: Alex Burger July 18th, 2009: - Fixed bug with snmptthandler-embedded. It was using the wrong IP address for hostname / ipaddress. July 18th, 2009: Alex Burger July 18th, 2009: - Updated documentation for 1.3beta1 release July 18th, 2009: Alex Burger July 18th, 2009: - Updated snmptthandler-embedded to support securityName and contextName. - Added variable substitutions Be, Bu, BE, Bn for SNMPv3 when using snmptthandler-embedded. May 17th, 2009: Alex Burger - Added snmptthandler-embedded - a Net-SNMP embedded Perl version of snmptthandler. - Added LSB init keywords and actions to snmptt-init.d and changed the priority for start / stop so that it starts after snmptrapd and stops before snmptrapd. Thanks to Ville Skytta for the patch. - Changed the default log path to /var/log/snmptt for Unix and c:\snmpt\log for Windows to make it easier to grant write permission to the snmptt process. Thanks to Ville Skytta for the patch. - Changed umask for log files to 002 to ensure they are not created as world writable. Thanks to Ville Skytta for the suggestion. - Fixed a bug where the the PID file was being created using the parent (root) PID instead of the child (daemon_uid) when daemon_uid is used. - The DEBUG log file will now be re-opened when a HUP signal is sent. Thanks to Ville Skytta for the suggestion. - When debugging is enabled, flush buffers every sleep cycle so we can tail the debug log file. - Don't print messages to the console when starting in daemon mode unless debugging is enabled or an error occurs. Thanks to Ville Skytta for the suggestion. - 'Could not open debug output file!' is no longer reported when debugging is disabled. Thanks to Ville Skytta for the suggestion. - Added snmptt.logrotate file from Ville Skytta. January 15th, 2009: Alex Burger - Re-applied patch for bug 1748512. Somehow it was added wrong and it prevented trap lines that contained single line items inside of quotes from working. August 4th, 2008: Alex Burger - Added duplicate_trap_window variable for duplicate trap detection. March 2nd, 2008: Alex Burger - Fixed a bug where with handling escaped quotes in a trap message. Bug 1748512. Thanks to Erwin Abrahamse for the fix. October 12th, 2007: Alex Burger - Updated snmptt-net-snmp-test to test MIB descriptions. June 16th, 2007: Alex Burger - Changed version to snmptt_1.3beta1 - Fixed a bug where snmpttconvertmib would not recognize a MIB file if the DEFINITIONS ::= BEGIN line was split across two lines. Thanks to Alex Peeters for reporting the bug. Bug 1678270. - Improved a previous bug fix for when a TRAP-TYPE / NOTIFICATION-TYPE line was split across two lines. Fix did not allow the trap name to contain a symbol such as a -. June 16th, 2007: Alex Burger - Changed version to snmptt_1.2 for release. June 9th, 2007: Alex Burger - Change REGEX to use Text::Balanced instead of split to fix bug with REGEX statements that contain captures. Apr 6th, 2007: Alex Burger - Release snmptt_1.2beta3 Apr 6th, 2007: Alex Burger - Add workaround to Net-SNMP 5.4 bug 1638225 where the host name passed in the traphandle is . Mar 4th, 2007: Alex Burger - Fixed bug where snmptt tried to log to syslog when changing UIDs even if syslog_system_enable was set to 0. Thanks to Alex Peeters for reporting the bug. - The snmptt.pid file is now deleted when SNMPTT exits gracefully. - When running in daemon mode with a daemon_uid user defined, a second (child) process will be started as the daemon_uid user so there will be two snmptt processes running. The first process will continue to run as the user that ran snmptt (usually root), waiting for the child to quit. After the child quits, the parent process will remove the snmptt.pid file and exit. The snmptt.pid file will contain the pid of the parent process. Sending a TERM signal to either process will cause SNMPTT to terminate gracefully. If you are not starting snmptt using root, then you should not be defining daemon_uid. - Now aborts startup if an existing snmptt.pid file is found. - Verifies that it is able to write to the pid_file folder before starting up. If it can not, it aborts. Only does this if pid_file has been defined. Otherwise it attempts to create the pid file and if it fails, it just continues as in previous versions. Mar 3rd, 2007: Alex Burger - Added a text system log in addition to the existing syslog and Event Log logs. Added log_system and log_system_file snmptt.ini options. Thanks to Alex Peeters for reporting the bug. - Added snmptt.ini option pid_file to allow for custom pid file locations when running in daemon mode. Thanks to Alex Peeters for the suggestion. - Fixed bug where pid file did not contain the current pid of snmptt. Thanks to Alex Peeters for reporting the bug. Feb 10th, 2007: Alex Burger - Added information to Nagios section of documentation for using freshness checks to automatically clear trap alerts. - Added information to Nagios section of documentation for using SNMP traps as heartbeats by using freshness checks. Thanks to Martin Fuerstenau. See bug 1629565. Jan 27th, 2007: Alex Burger - Fixed bug in threads support. EXEC'd commands were all passed the same $command variable. Threads are now detached after being created. - Changed version to snmptt_1.2beta3 Dec 21st, 2006: Alex Burger - Fixed bug in snmpttconvertmib where a --#SEVERITYMAP line would be used instead of --#SEVERITY. Dec 18th, 2006: Alex Burger - Added unknown_trap_exec_format option Dec 7th, 2006: Alex Burger - Added exec_escape option - Changed version to snmptt_1.2beta2 Nov 18th, 2006: Alex Burger - Changed version to snmptt_1.2beta1 Sept 23rd, 2006: Alex Burger - Added a simple check to see if the trap file being processed is a valid file. If it is not, the file is skipped and not deleted. Sept 15th, 2006: Alex Burger - Added snmptt.ini options date_format, time_format, date_time_format, date_time_format_sql and stat_time_format_sql to allow the output format for $x and $X substitution variables, and the format of the date/time for text logs and SQL to be changed using strftime() variables. This allows for proper date/time data types to be used in SQL databases. June 18th, 2006: Alex Burger - Prevent logging to syslog and event log when running SNMPTT with the --time option. Thanks to Stefan Mohr. - Fixed a bug under Windows where SNMPTT was trying to log to syslog instead of the event log. Thanks to Roger Lindholm. - Add threads (ithreads) support for EXEC. When enabled, EXEC commands will launch in a thread to allow SNMPTT to continue processing other traps. Added snmptt.ini options threads_enable and threads_max. May 13th, 2006: Alex Burger - Moved unknown_trap_exec to Exec section in snmptt.ini. Patch 1476071. Thanks to George Kourvoulis. Mar 23rd, 2006: Alex Burger - Added MATCH support for bitwise AND Mar 13th, 2006: Alex Burger - Fix a bug where snmpttconvertmib does not handle ARGUMENTS lines that have $1, $2 etc instead of %0, %1. Thanks to Todd A. Green. Bug 1438394. Mar 12th, 2006: Alex Burger - Move all SQL update code to subroutines (mysql_update etc). - Added logging of trap statistics to SQL table. Added *table_statistics snmptt.ini variable to define the table to be used. - Added ability to add custom columns to *_table and *_table_unknown tables. Added sql_custom_columns and sql_custom_columns_unknown snmptt.ini options. - Added variable substitution $H. This variable will default to the host name of the computer that is running SNMPTT, unless the snmptt_system_name snmptt.ini option is defined. - Sys::Hostname is now required. Mar 4th, 2006: Alex Burger - Fix a bug where snmpttconvertmib would not translate a TRAP-TYPE / NOTIFICATION-TYPE line if it was split across two lines. Thanks to Todd A. Green. Bug 1438794. Jan 20th, 2006: Alex Burger - Changed version to snmptt_1.2beta - Fix bug in process trap for $match_temp 'remove any white space from before and after i modifier' - Added 'use strict' to snmptt - Added 'use strict' to snmpttconvertmib - Added 'use strict' to snmpttconvert - Added 'use strict' to snmptthandler and improved debugging Jan 17th, 2006: Alex Burger - Changed version to snmptt_1.1 Dec 31st, 2005: Alex Burger - Clean up white space formatting of code. Dec 10th, 2005: Alex Burger - Update version stamp in all files - Changed 'sub match' to only do variable substitution if needed. Nov 22nd, 2005: Alex Burger - MATCH statement now accepts any variable name instead of only enterprise variables. Example: MATCH $s:(Normal) Nov 5th, 2005: Alex Burger - Added NODES MODE= snmptt.conf file option to allow you to select either POS (positive - the default) or NEG (negative) for NODES matches. If set to NEG, then the NODES is a 'match' only if NONE of the NODES entries match. POS = x | x | x etc. NEG = !x & !x & !x etc. - Fixed bug in NODES where NODES entries from previous EVENTs were not being purged correctly. - Changed version to snmptt_1.1beta2 Nov 5th, 2005: Alex Burger - Changed version to snmptt_1.1beta1 - Changed MySQL INSERT statements to PREPARE / EXECUTE as it is safer than trying to escape all the fields manually. This way all escaping is done via the MySQL Perl module. Nov 2nd, 2005: Alex Burger - Added PREEXEC snmptt.conf file option to allow an external program to be run before processing the FORMAT and EXEC lines. The output of the external program is stored in the $pn variable where n is a number starting from 1. Multiple PREEXEC lines are permitted. The first PREEXEC stores the result of the command in $p1, the second in $p2 etc. Any ending newlines are removed. The snmptt.ini parameter pre_exec_enable can be used to enable / disable it. - If the debug log file can not be opened, a message is now logged to syslog if syslog_system_enable is enabled, and to the Event Log if eventlog_system_enable is enabled. - Fixed a problem when --format_desc=n was used. It was adding a trailing whitespace for every non existent line in the description. Thanks to Carlos Velasco for the fix. - Added unknown_trap_exec snmptt.ini option. If defined, the command will be executed for ALL unknown traps. Passed to the command will be all standard and enterprise variables, similar to unknown_trap_log_file but without the newlines. Thanks to Carlos Velasco for the patch. - Modify output of hash dump to print Event: instead of hash: - Add dump of duplicate events for --dump option - Fixed bug with --dump trying to open up syslog, eventlog etc Oct 9th, 2005: Alex Burger - Changed PostgreSQL INSERT statements to PREPARE / EXECUTE to prevent issue with trap data being interpreted as placeholders in the SQL statement which was causing logging errors. April 16th, 2005: Alex Burger - snmpttconvertmib was not printing 'Processing MIB xxx' if DEFINITIONS::= line didn't contain spaces (similar to 11/19/04). April 15th, 2005: Alex Burger - Added Windows Event Log forwarding documentation to integration section April 11th, 2005: Alex Burger - Fix bug in snmpttconvertmib: --ARGUMENTS {} line was expected to have one space between ARGUMENTS and {}. It can now be any amount of white space, or no space at all. November 19th, 2004: Alex Burger - Allow for .* wildcard. Only .x.* worked before. November 19th, 2004: Alex Burger - Changed version to snmptt_1.0.1 - Change snmpttconvertmib to allow for DEFINITIONS::= (no spaces) Aug 30th, 2004: Alex Burger - Changed version to snmptt_1.0 Aug 18th, 2004: Alex Burger - Change PostreSQL and ODBC ping functions to not use errno like in MySQL. Thanks to mike at mccartney.net. Aug 16th, 2004: Alex Burger - Change version to 0.9.5 - Add daemon sample traps - Fixed missing _unknown in unknown logging for PostgreSQL, ODBC and Win32::ODBC. Thanks to mike at mccartney.net Aug 15th, 2004: Alex Burger - Moved xx_ping_on_insert check before if (defined ($dbh..)) Aug 10th, 2004: Alex Burger - Added database ping support to PostgreSQL and DBD::ODBC. Added debug logging improvements for database connect and INSERT sections. Thanks to mike at mccartney.net. Debug output now prints out $n variable number beside 'Ent Value x' such as 'Ent Value 2 ($3):' to reduce confusion when troubleshooting. Aug 9th, 2004: Alex Burger - Debug output now prints out $n variable number beside 'Ent Value x' such as 'Ent Value 2 ($3):' to reduce confusion when troubleshooting. July 14th, 2004: Alex Burger - snmpttconvertmib: Fix bug with infinite loop when processing VARIABLES section if MIB file had spaces after }. Thanks to mike at mccartney.net July 5th, 2004: Alex Burger - Add SEC documentation - Update help for snmpttconvertmib June 25th, 2004: Alex Burger - Changed mysql_ping_insert to mysql_ping_on_insert. - Added $Fz variable which outputs the processed FORMAT line for use in EXEC lines so if the FORMAT and EXEC lines contain the same thing (minus the program to execute of course) the EXEC line can be simplified. - Removed mib_descriptions_enable and replaced with description_mode and description_clean. Can now use the description from either the SNMPTT.CONF or the MIB files. - Snmpttconvertmib: Bug: Was not parsing OBJECTS lines, only VARIABLES lines. June 24th, 2004: Alex Burger - Removed references from sub substitute2. June 23th, 2004: Alex Burger - Added config option mysql_ping_insert to enable / disable pinging the database before an INSERT. - Changed ping_mysql subroutine to mysql_ping June 18th, 2004: Alex Burger - MySQL: Now does a ping before trying to do the INSERT even if mysql_ping_interval is disabled. - Set default for mysql_ping_interval in snmptt to 500 match the .ini default. June 17th, 2004: Alex Burger - Removed escape_sequences_enable option and replaced it with variable substitutions $Fa, $Ff, $Fn, $Fr and $Ft. June 11th, 2004: Alex Burger - Added mysql_ping_interval option to set how often the MySQL server should be 'pinged'. If the 'ping' fails, it will attempt to re-connect to the database. - Added escape_sequences_enable option to enable the use of common escape sequences in FORMAT and EXEC lines. - Added variable substitution $D to dump the description text from the MIB file. Enabled using the mib_descriptions_enable snmptt.ini variable. Thanks to Toshiyuki Haginaga. June 7th, 2004: Alex Burger - Moved database connection code to create_db_connections function. Function is now called AFTER changing users when running in daemon mode to prevent 'MySQL gone away' errors on some systems. Thanks to Toshiyuki Haginaga. May 24th, 2004: Alex Burger - Added 'Total traps ignored' to stats. May 14th, 2004: Alex Burger - Added error number output for MySQL. May 13th, 2004: Alex Burger - Add support for /usr/local/etc/snmp and /usr/local/etc. - Added help text for user when a Perl module is not found when loading via 'require'. - Bug fix: debug log started time printed as serial instead of regular date/time. April 4th, 2004: Alex Burger - Add db_unknown_trap_format variable, update docs, faq - Statistics dumped at shutdown: Total traps received Total traps translated Total unknown traps - Statistics can be dumped every x seconds via statistics_interval variable or by creating a !statistics file in the spool directory. April 3rd, 2004: Alex Burger - Add support for logging unknown traps to SQL table. Added xxx_table_unknown variable for each SQL type. January 25th, 2004: Alex Burger - Clean up yesterday's symbolic fix. It now translates all variable NAMES (if needed), the trap OID and the enterprise VALUE. January 24th, 2004: Alex Burger - Traps received using symbolic format (snmptrapd without -On) were not being handled correctly. Code in readtrap was looking at wrong variable name. Moved to function translate_symbolic_to_oid. Called for trap OID variable value, agent_address, community and agent addrss variable names December 5th, 2003: Alex Burger - Changed version to snmptt_0.9.1 - snmptt.conf file can now contain EXEC lines without FORMAT lines. November 3rd, 2003: Alex Burger - Changed version to snmptt_0.9 - snmptt-net-snmp-test now defaults to best_guess=2 October 27th, 2003: Alex Burger - Syslog messages now use snmptt[pid] instead of TRAPD (traps) and snmptt-sys[pid] instead of SNMPTT (system messages) October 10th, 2003: Alex Burger - Added daemon_uid support to change user snmptt runs as after starting up October 9th, 2003: Alex Burger - Fixed bug with MATCH statement integer range. 1-5 would match only 2-4 instead of 1-5. October 1st, 2003: Alex Burger - Fixed bug with translate_value_oids_sub. Was not detecting IP addresses correctly (backtracing problem). Function resolve_value_ip_addresses_sub also changed. September 16th, 2003: Alex Burger - Changed version to snmptt_0.8.1 September 16th, 2003: Alex Burger - Changed version to snmptt_0.8 September 15th, 2003: Alex Burger - Bug fix - sometimes Net-SNMP would pass a quoted line on multiple lines. They are now combined into one in snmptt. Thanks to Antïżœnio Cardoso. September 12th, 2003: Alex Burger - Fix MATCH. sub match_result was not returning a 0 (didn't really matter). If a match regex contained a space, it was removed... Fixed - Add i modifier support for MATCH September 8th, 2003: Alex Burger - Added snmptt.pid support September 7th, 2003: Alex Burger - Snmpttconvertmib: Problem from below must be a Net-SNMP bug. MIBS environment variable now set to the filename of the MIB instead of the MIB module name. - Snmpttconvertmib: Checks for TRAP / NOTIF lines that have a -- in it. Ignores them - comments.. September 5th, 2003: Alex Burger - Snmpttconvertmib now exits with exit 1 instead of die when not enough parameters specified - Snmpttconvertmib: Some mib files contain multiple BEGIN/END statements and they were not being handled correctly. Script now looks for them throughout the file and sets the mib_name variable correctly so lookups are correct with snmptranslate. May be a bug in Net-SNMP: If you set the MIBS environment variable to anything except ALL or the first BEGIN value in a file that contains multiple BEGIN/ENDs, then snmptranslate does not work. For now, MIBS environment is set to the first occurance only. If it turns out to be a bug in Net-SNMP, it will be changed here to account for it. Also added a --mibs=s parameter to override the environment variable. - Snmpttconvertmib: exec defaults to blank instead of qpage... - Clean up some warnings when running with perl -w September 4rd, 2003: Alex Burger - Finished fix for translate_value_oids_sub. Both while loops now have a break out to prevent an infinit loop, although it should not occur.. - When using e modifier with REGEX, force a new package to prevent user from accessing internal snmptt variables, and prevent access to the main:: package variables. - Added resolve_value_ip_addresses option, updated .ini files September 3rd, 2003: Alex Burger - Added support for i, g and e modifiers for REGEX - Fix translate_value_oids_sub. If string contained multiple OIDs, they may not be converted correctly. Example, if you had .1.2.3.4 and .1.2.3.4.5 it would translate to whatever and whatever.5. Improved with lookaheads and lookbehinds August 14th, 2003: Alex Burger - Added support for DBD:Pg for PostgreSQL - Added postgresql_dbi_module and postgresql_dbi_hostport_enable options July 8th, 2003: Alex Burger - Improve NODES handling of files to only load the NODES file once when dynamic is off - uses a hash to store the entries for each 'filename' July 7th, 2003: Alex Burger - Allow multiple EXEC lines in config - Added dynamic_nodes config option - Allow multiple NODES lines in config July 2nd, 2003: Alex Burger - Added remote database support (host & port) for MySQL June 28th, 2003: Alex Burger - Added remote database support (host & port) for PostreSQL June 24th, 2003: Alex Burger - Snmpttconvertmib: Took out most of the net_snmp_perl code and moved only the variables code into the main snmptranslate code as that's really the only difference, and snmptranslate now works fast as the exact MIB name is used on the command line. Perl module only used to convert variable syntax, desc and enums. - Improved formatting of variables output - Allow comments on VARIABLES lines in MIB files June 21th, 2003: Alex Burger - Snmpttconvertmib: Changed --net_snmp_perl method to use the moduleID from the hash of OIDs instead of trying to load a specific MIB using setMib etc as some MIB files were not converting. June 20th, 2003: Alex Burger - Snmpttconvertmib: Changed snmptranslate method to use modulename::trapname instead of enterprise.trapname as some MIB files were not converting. June 19th, 2003: Alex Burger - Fixed $# - was giving one less than the actual number of enterprise variables - Fixed MATCH statement to start at $1 instead of $0 - Changed $n to $i to prevent confusion June 18th, 2003: Alex Burger - Change help for snmpttconvertmib --exec= line June 11th, 2003: Alex Burger - Changed ODBC & WinODBC code to not escape quotes. Instead, double up single quotes, and do not touch double quotes. May 27th, 2003: Alex Burger - Added trapoid database column to contain the actual trap OID received from Net-SNMP. eventid will contain what was matched in the .conf file. May 9th, 2003: Alex Burger - Fixed bug when using wildcards. The received trap was reset to the matching EVENT entry which contained the .*. This caused things like $S not to work. Changed so that actual received trap is not overwritten with what it matched to in the EVENT table. May 7th, 2003: Alex Burger - SNMPTT will strip DOS carriage return from snmptt.conf files if detected. April 29th, 2003: Alex Burger - Snmpttconvertmib: Printing of INTEGER enums was wrong. Fixed. April 29th, 2003: Alex Burger - Enterprise variables in snmpttunknown.log now start with Ent Value: instead of just Value: April 25th, 2003: Alex Burger - Snmpttconvertmib: Now prints the variables contained in each trap unless --no_variables is set. When using --net_snmp_perl it will also resolve the Syntax (INTEGER, OCTETSTR etc) and description. If it's an INTEGER, will also print out the enums. April 23rd, 2003: Alex Burger - Added my_getType and my_mapEnum functions. Both attempt to use the numeric OID first. If that fails, then it converts the OID to a short symbolic and uses that. UCD-SNMP works with numeric, Net-SNMP 5.0.8 does not. Newer versions of Net-SNMP will work with numeric. Numeric is preferred as there is no chance of matching the wrong object. April 22st, 2003: Alex Burger - Add getType test to snmptt-net-snmp-test - If DNS is enabled, NODES entries are resolved to IP addresses before comparing. Will allow for aliases / CNAMES to match etc. - NODES match uses agent IP instead of host IP April 20th, 2003: Alex Burger - If agent IP address missing, copies IP from host IP address - If host name not resolved, resolves via DNS if enabled - If agent IP same as host IP, copies host DNS instead of resolving. - Added dns_enable option and changed (nearly) all $var[0] to $agent_dns_name April 19th, 2003: Alex Burger - Added additional translation options April 18th, 2003: Alex Burger - Cleaned up NODES / MATCH code - Changed version to snmptt_0.7.1 April 17th, 2003: Alex Burger - Added MATCH support for snmptt.conf - Changed version to snmptt_0.7 - Added Net-SNMP Perl module version output to debug - Added snmptt-net-snmp-test program April 15th, 2003: Alex Burger - Added ability to reload configuration files by adding a file called '!reload' to the spool directory - Added support for symbolic OIDs from snmptrapd April 12th, 2003: Alex Burger - Added support for translate_trap_oid - Added support for symbolic OIDs on EVENT line April 2nd, 2003: Alex Burger - Replace all back ticks with forward ticks on received trap - Prepend a backslash to ' and " characters when logging to SQL databases (community and format line) - Added REGEX support for snmptt.conf - Added translate_oids option - Snmpttconvertmib: Replace any spaces with -'s in severity April 1st, 2003: Alex Burger - Snmpttconvertmib: Add --severity and --no_severity switches - Snmpttconvertmib: Combines multiple --#SUMMARY lines into one - Snmpttconvertmib: Uses --#SEVERITY line - Checks for both INTEGER and Integer32 types when checking MIB entries in SNMPTT. Improve debugging output for this section also. March 31th, 2003: Alex Burger - Snmpttconvertmib: --exec command line option March 28th, 2003: Alex Burger - Snmpttconvert, snmpttconvertmib: Will strip DOS carriage return from input files if detected. - Snmpttconvertmib: Add new options to include both summary and description lines in the FORMAT and EXEC lines. Allows either or both. Also allows x number of sentences from the description to be used on the FORMAT and EXEC lines. March 27th, 2003: Alex Burger - NODES can now contain CIDR address and network ranges. The NODES line can now contain a mix of host names, IP addresses / network ranges and filenames. - Added option to disable the default of $* in snmpttconvertmib when there is no --#SUMMARY line, and the description does not contain any variables March 26th, 2003: Alex Burger - Translation of integers not working under Net-SNMP 5.0.8. Fixed. Reasons: Problem #1: UCD-SNMP 4.2.3 allows getType and mapEnum to be passed a numerical OID. Net-SNMP 5.0.8 does not. Because of this, we should make sure we use a textual OID instead when calling getType or mapEnum. It's probably the correct way to do it anyways. Problem #2: UCD-SNMP 4.2.3 translateObject returns the textualOID followed by .#, even if it is called with ,0 to use short names. Net-SNMP 5.0.8 returns a short name some what correctly, but trails the textual name with a . - PostgreSQL support via DBD::PgPP - Changed version to snmptt_0.6.1 March 25th, 2003: Alex Burger - Changed version to snmptt_0.6 - Fixed bug in snmptt - domain name strip not working correctly - Re-fixed bug in snmpttconvertmib that would cause the last trap (notification) converted from a v2 MIB file to contain an incorrect DESCRIPTION and possibly FORMAT line when the last thing in the MIB file is not the last trap definition (broken when merging code below). Applied only to Perl module version. March 24th, 2003: Alex Burger - Can now specify a NODES entry with snmpttconvertmib March 23rd, 2003: Alex Burger - Re-fixed bug in snmpttconvertmib that would cause the last trap (notification) converted from a v2 MIB file to contain an incorrect DESCRIPTION and possibly FORMAT line when the last thing in the MIB file is not the last trap definition (broken when merging code below) March 22nd, 2003: Alex Burger - Cleanup snmpttconvertmib code - combine V1 and V2 trap conversion into one section - snmpttconvertmib now uses GetOpt::Long for arguments, using --in and --out switches for in / out file, --version, --help, --debug. - Added support to snmpttconvertmib for the NET-SNMP Perl module. Can enable using --net_snmp_perl switch, or run as usual using snmptranslate. Must also specify the mib file directory with --mibdir March 21th, 2003: Alex Burger - Removed unused emerg ini setting, init $debugcmdline and $debugfilecmdline, remove unused $vartype variable, change a \1 to $1 - Cleanup: Replaced many @xxx[] that should be $xxx[] March 18th, 2003: Alex Burger - Fixed bug with MySQL database not closing correctly at shutdown due to previous MySQL changes March 13th, 2003: Alex Burger - Fixed bug with handling trap data that contains spaces but is not inside of quotes for traps received from snmptrapd 4.2.3 - Fixed bug with translation of integers. If the value could not be translated, it should use the non-translated value - If a variable is passed from snmptrapd that is blank, snmptt will replace it with (null) - snmpttconvertmib now checks the version of snmptranslate before converting the mib. To ensure OIDs are outputted as numerical, the -On switch is added to the snmptranslate command when snmptranslate is v5.0.2 or newer. Net-SNMP 5.0.2 and newer (more specifically, snmplib/mib.c version 5.7 or newer in CVS) uses the -On switch to SET numeric OID output. Anything before 5.0.2 is used to TOGGLE the setting. For older than v5.0.2, the snmp.conf file should contain the line: printNumericOids 1 March 9th, 2003: Alex Burger - Cleaned up wildcard expansion code. Added wildcard_expansion_separator variable - Remove trailing space after expanding wildcards March 7th, 2003: Alex Burger - Improved syslog and event log support by allowing different syslog and NT Event Log levels based on the severity level defined in the snmptt.conf file - Added variable expansion for variable names using $vn, $+n, $-n, $+* and $-* - Fixed bug that would prevent variables numbered 10 or higher from being translated correctly February 28th, 2003: Alex Burger - Added more entries to snmptt-eventlog.mc (total of 30) and recompiled DLL February 27th, 2003: Alex Burger - Fixed error messages for SQL and NT event ID numbers. Udpated docs February 25th, 2003: Alex Burger - Updated keep_unlogged_traps option to have SNMPTT erase the spooled trap file only after it successfully logs to at least one or all log systems. This will help prevent traps from being lost due to logging problems. February 24th, 2003: Alex Burger - Added keep_unlogged_traps variable to prevent SNMPTT from deleting the spooled trap file if NONE of defined logging systems successfully logged the trap - Added support for logging of traps using DBD::ODBC - Cleaned up MySQL code - Fixed bug with Win32::ODBC connection not being closed on exit of SNMPTT. February 22nd, 2003: Alex Burger - Added support for logging traps to the NT Event Log - Added syslog and NT Event Log support for SNMPTT 'system' events such as startup, shutdown, errors handling trap spool directory and files. See readme for info on NT Event Log. - Added separate debug file for snmptthandler - Cleaned up defaults code for snmptthandler February 21st, 2003: Alex Burger - Added ability to translate integer values to enumeration tags defined in MIB files. Configure using snmptt.ini variables translate_integers and mibs_environment. Requires UCD / NET-SNMP Perl module (SNMP.pm) February 18th, 2003: Alex Burger - Fixed bug in snmpttconvertmib that would cause the last trap (notification) converted from a v2 MIB file to contain an incorrect DESCRIPTION and possibly FORMAT line when the last thing in the MIB file is not the last trap definition - Values passed to snmptt that contained spaces but were not inside of quotes were being treated as multiple variables / values. Replaced main readtrap code to fix this. February 15th, 2003: Alex Burger - Changed version to snmptt_0.5.1 February 12th, 2003: Alex Burger - Changed version to snmptt_0.5 - Changed fqdn_enable ini parameter to strip_domain. Strip_domain can now be 0, 1 or 2. 0 = do not strip domain name. 1 = strip domain name. 2 = strip domain name based on list of domains contained in strip_domain_list ini parameter. Defaults to 0. Note: With fqdn_enable, it defaulted to disable (strip domain). If you were using the CVS version with fqdn_enable set to 0, change strip_domain to 1. - Added strip_domain_list paramater to ini file January 23rd, 2003: Alex Burger - Spool file list sorted before processing to ensure traps are processed in the order they are received when in daemon mode. January 22nd, 2003: Alex Burger - Changed SNMPTTHANDLER to output the current date and time on the first line of the spool file. - Changed SNMPTTHANDLER to v0.2 - Added use_trap_time variable to config file for daemon mode to have SNMPTT use either the time from the spool file, or the current time. - Fixed code for default variable settings for .ini file. Defaults were not being set correctly if variable was not defined in .ini file. January 20th, 2003: Alex Burger - Cleaned up code for determining whether to run as a daemon or standalone mode. - Fix bug with Nodes list not being flushed out when processing multiple traps at once. Thanks to Christophe Belmont. January 6th, 2003: Alex Burger - Added fqdn_enable option to remove domain name from host name passed to SNMPTT. Defaults to stripping the domain name (0) December 3rd, 2002: Alex Burger - By default, snmpttconvertmib will now prepend the --TYPE line to the summary line followed by a ':'. This can be disabled in the script in the OPTIONS section. This should create a more descriptive FORMAT / EXEC line. Example: "Status Trap: NIC switchover to slot $3" - Changed version to snmptt_0.4.1 - Changed version to snmpttconvertmib_0.1.1 November 18th, 2002: Alex Burger - Changed version to snmptt_0.4 November 13th, 2002: Alex Burger - Changed default Windows drive from d: to c: in ini file November 12th, 2002: Alex Burger - Added $c, $@, $O, $o, $ar, $R, $aR, $G, $S, $X, $N - Modified: $x - Modified: $S was name, now is Specific November 11th, 2002: Alex Burger - Updated snmptthandler to use .ini file - Cleaned up variable names to be easier to read - Created snmptt.ini-nt file and tested under Windows NT November 10th, 2002: Alex Burger - Fixed multiple_event bug - if it was disabled, would not process traps - Now requires Config::IniFiles - Moved all config options to external ini file - Added -ini= command line parameter - Changed version to snmptt_0.3.3 October 22th, 2002: Alex Burger - Fixed bug with issuing a RELOAD (-HUP) when using a configuration file that contains a list of other configuration files. Variable was not being cleared. October 18th, 2002: Alex Burger - Minor bug fix for MySQL / ODBC support - eventid wrong, use INSERT INTO instead of just INSERT for ODBC to allow it to work with MS Access October 17th, 2002: Alex Burger - Added support for MySQL (Linux / Windows) and ODBC (Windows) September 27th, 2002: Alex Burger - Changed version to snmptt_0.3.1 - Fixed bug with $x variable - code was searching for $r, not $x to replace September 11th, 2002: Alex Burger - Changed version to snmptt_0.3 September 10th, 2002: Alex Burger - Bug fix - some variables were not being cleared causing data from previous traps being merged with newer traps when using daemon mode. September 4th, 2002: Alex Burger - Added variable to allow user to disable forking for daemon mode. - Cleaned up file / directory error handling for daemon mode - Tested snmptt in deamon mode and snmptthandler on both Linux and Windows NT September 3rd, 2002: Alex Burger - Added fork for daemon mode for non Win32 systems - Fixed minor Win32 file problems and tested deamon mode under Windows NT. Snmptthandler not tested August 29th, 2002: Alex Burger - Fixed reload of configuration file to purge memory first - Cleaned up spool file error handling for daemon mode - Added HUP signal to daemon mode to force reload of configuration file(s). Thanks to Stefan Mohr. - You can now have multiple definitions of the same trap. Giving each entry a different NODES list will allow you to define actions for a particular trap based on the node. Thanks to Ingo Flaschberger. - NODES file / list now accepts both IP addresses and host names. Thanks to Ingo Flaschberger. - Lines starting with # in NODES file treated as comment. Thanks to Ingo Flaschberger. - Changed @var[3] references to $receivedtrap August 26th, 2002: Alex Burger - Added snmptthandler script which when run from snmptrapd.conf instead of snmptt, will dump received traps (quickly) into the directory defined by $spooldirectory to be later processed by snmptt running as a daemon. - Added daemon mode support to snmptt. snmptt --daemon or setting daemonmode=1 in script to enable. Looks in $spooldirectory for files to process every $sleeptime seconds. August 21th, 2002: Alex Burger - Passed IP address sometimes in the format of udp:ipaddress:port. udp: and :port now removed. Thanks to Ingo Flaschberger. August 20th, 2002: Alex Burger - Changed version to snmptt_0.2.2 August 15th, 2002: Alex Burger - Changed version to snmptt_0.2.1 - Added command line options to snmptt: debug, debugfile, dump, help, version, time - Updated snmpttconvertmib to use also use enterprise when running snmptranslate - Added updated readme.html (readme is text version of readme.html which is updated when new versions are released) - Added sample-trap to CVS. snmptt < sample-trap to use - Added support for NOTIFICATION-TYPE v2 MIB files to snmpttconvertmib August 14th, 2002: Alex Burger - Add first version of SNMPTTCONVERTMIB July 15th, 2002: Alex Burger - Changed version to snmptt_0.2 for release June 15th, 2002: Alex Burger - Using UCD-SNMP v4.2.3, data passed from SNMPTRAPD was not being handled correctly. Data being passed only contained one item per line instead of two. This appears to be a bug with v4.2.3 as it does not happen with v4.2.1 or v4.2.5. SNMPTT can now work around it but v4.2.3 should NOT be used with SNMPTT especially if SNMP V2 traps are being used. - Fixed bug when receiving SNMP V2 traps. SNMPTT was assuming the last three variables were the agent IP, community name and enterprise, which was incorrect. It now looks for specific OIDs. UCD-SNMP V4.2.3 should not be used due to the bug mentioned above. June 13th, 2002: Alex Burger - Bug fix with $s - Severity. Thanks to Hugues Mertens - Started this ChangeLog April 30th, 2002: Alex Burger - Changed version to snmptt_0.1.1 - Improved debugging output April 18th, 2002: Alex Burger - Initial release of snmptt_0.1 snmptt_1.5/INSTALL0000664000000000000000000000020714277306025012524 0ustar rootrootSNMP TRAP TRANSLATOR (SNMPTT) Copyright 2002-2022 Alex Burger alex_b@users.sourceforge.net 4/11/2002 See the docs/index.html file. snmptt_1.5/README0000664000000000000000000000020714277306025012353 0ustar rootrootSNMP TRAP TRANSLATOR (SNMPTT) Copyright 2002-2022 Alex Burger alex_b@users.sourceforge.net 4/11/2002 See the docs/index.html file. snmptt_1.5/bin/0000775000000000000000000000000014277306167012253 5ustar rootrootsnmptt_1.5/bin/snmptt-eventlog.dll0000664000000000000000000003000014277306025016100 0ustar rootrootMZ@ !L!This program cannot be run in DOS mode. $ 5ۯA[A[A[hG]A[RichA[PELύ_>!  `0 .rsrc@@.reloc  @B 0 H` %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 %1 snmptt_1.5/contrib/0000775000000000000000000000000014277306167013143 5ustar rootrootsnmptt_1.5/contrib/README0000664000000000000000000000060714277306025014017 0ustar rootrootSNMPTT Contribution files ========================= September 16th, 2003 These files are NOT supported and are meant to be used on a 'WYSIWYG' basis. FILE DESCRIPTION ============================================================================== sendemail.sh A script to send emails for the EXEC line cacti-syslog-connector.py A script to connect SNMPTT to the Cacti Syslog plugin snmptt_1.5/contrib/cacti-syslog-connector.py0000664000000000000000000000407214277306025020102 0ustar rootroot #!/usr/bin/python3 #### Created by Sean Mancini sean@seanmancini.com www.seanmancini.com ### usage enter /opt/cacti-syslog-connector.py --hostname $aA --alert "$Fz" --priority 6 --facility 2 in you SNMPTT EXEC config line ### EXEC /opt/snmptt_syslog.py --hostname $aA --alert "$Fz" --priority 6 --facility 2 ( Change variables as you see fit) ## this will write the message directly to the cacti syslog incoming table to be ingested into the syslog database ### connect to mysql database ### import modules import mysql.connector import sys from datetime import datetime import argparse # Define Arguments for variables from snmptt parser = argparse.ArgumentParser(description = 'SNMPTT Variables') parser.add_argument('--hostname', help = "Device Hostname", required=True) parser.add_argument('--alert', help = "Alert Messege", required=True) parser.add_argument('--facility', help = "Alert Facility", required=True) parser.add_argument('--priority', help = "Alert Priority", required=True) args = parser.parse_args(sys.argv[1:]) if args.hostname is not None: ({'device Hostname': args.hostname}) #snmp timeout if args.alert is not None: ({'Alert Messege': args.alert}) #device template if args.facility is not None: ({'Alert facility': args.facility}) #device template if args.priority is not None: ({'Alert priority': args.priority}) #device template ### connect to database try: db = mysql.connector.connect( host = "localhost", user = "", passwd = "", database = "" ) ### create cursor cursor = db.cursor() ### write to database sql = "INSERT INTO syslog_incoming (facility_id, priority_id, program, logtime, host, message) VALUES (%s, %s, %s, %s ,%s, %s)" val = (args.facility,args.priority, "traps", datetime.now(),args.hostname, args.alert) cursor.execute(sql, val) ### commit changes db.commit() ### close database db.close() ### print success message print("Successfully wrote to database") except: ### print error message print("Unable to write to database") sys.exit(1) snmptt_1.5/contrib/sendemail.sh0000664000000000000000000000226214277306025015433 0ustar rootroot#!/bin/sh # # Author: Jon Nistor (nistor@snickers.org) # Date: September 9th, 2003 # Purpose: This script is meant to integrate with snmptt as a method to # send emails via the EXEC statement for received traps # Usage: sendemail.sh priority contact@email subjectLine textArea # Requirements: sendmail # SNMPTT example: EXEC sendemail.sh high admin@company.com "SNMPTT Alert" # "FORMAT NIC switchover to slot $3 from slot $5" FROM="SNMP contact " PRINTF=/usr/bin/printf if [ ! "$4" ]; then echo "Insufficient number of arguments" echo "usage: $0 priority contact@email subjectLine textArea" echo " priority: 1=low, 2=med, 3=high" echo " you can use \"\"'s around words to make it a section eg: \"text here\"" exit 1 fi PRIORITY="$1" TO="$2" SUBJECT="$3" TEXT="$4" #echo "VARS: P: $1 To: $2 Sub: $3 Text: $4" send_email() { $PRINTF "From: $FROM\nTo: $TO\nSubject: $SUBJECT\nImportance: $1\nX-Priority: $PRIORITY\n\n$TEXT\n.\n" | sendmail $TO } # Now to send page if [ "x$PRIORITY" = "x3" ]; then send_email High elif [ "x$PRIORITY" = "x2" ]; then send_email Medium else send_email Low fi # EOF # snmptt_1.5/docs/0000775000000000000000000000000014277306207012426 5ustar rootrootsnmptt_1.5/docs/faqs.epub0000664000000000000000000015424414277306025014245 0ustar rootrootPKѥUoa,mimetypeapplication/epub+zipPKѥU META-INF/PKѥUtMETA-INF/container.xmlU D~٫i+[VU"ѿklqgg88'+H%9K"*Zʼnv Zmj+ ЙSYϽ".aZGihrW"wk[#.㕎 W䎥6yPKѥUX^E: content.opfMo0 /9li;Æn"юZ[v-Ih+A[/IH|HJBe֓'/TzR]Nn⋨d@PS(eUJC߿E8 =!/5R22Pb#_``o2B9V4:j T>`#I/_wrJ S,2`lx"-X)0 q~WF w?CC錙"_oFt X%2pY{XoUdfp _*ʲCQݵr!PC_Wj:E΃K}ߡcwSi{P@v3ٔo;&^O]--,恘]"#`0m; i)CmaWAHS:$!kJet8boxOag2|KԆ/qCȴ&Eo4晍ާYh9hp?9 ׺Z[ gLC;M(~n58+DGR* iG;]ff| ? }g3'ٿbAئ䅼kX;{hh_PKѥU[ Smtoc.ncxY[Sۺ}y4$\H@ KI`ΌFX-I΅_>Ɏepw_->s}hAd!?߫ a|v+|ۧ#nlVjjVJٖ%}_ҫv-Q^ #GTI@K~SBvԨS4u:ԥ,[)m6zTN^QNQal*(mU?TdF˜W[~~S*ਲ S>MQt ׷h$Hpk֨{rwT1?;dqMt 6d\!٠u,ݤ[BO7¡⸴_JV&kdJͅ G捣EʆwPgVVvꀤK.yXF>SZy*qpC:#À^'_>\[K >p z䵏Onp8NG_gVrʫT(ا[P  h8A+Cmqi d.u%UՃL}j0GqB!W0PxMcz2.!|5a',F%$;e[\ufkmiNKV1N{$ZTNz_yS,; nfNJ\K-CFRJυZ6cujglXr>ij)#YLK$"6@܅28JE*XSNeFP?SPXa~FD|*u<ͨBEPY8 p8N  "r'Ѹo߶{wbpX$\Q4ӬLoBԨ5A׽ӤTi 4g%D y=lDTmKlX,Yf"HMfχ; Y[7eOO)`SZ<)b'CT^͞_~;ݓۈ\l2T(,GBv#7g gf\xF=՛v_V=QCi6^9i%.'D(0iB8K4Y4w2\]9?sAXSy'$)-c~s*DϦJqtM6 Ddl03;[I8g aXP~prκxz 2 XL`K([O&>ֶo *Ҙ?3V4\t'l"5 ]\]]>&#BY O$ Z6J26hBM3lTy.\&8!IQwv?>g>9~ԷUq`o->˃R/7(*i*HZ/`^Ŏ5 b PY=H*{Cq:m [Uq Yؗgjq*_݊EoF0;Mp 𻗯vkH*aKWaUgr_pmH\\,o$1VV}32]]'vDi0%b-zI}3-!s׵$ŝcyt zRNhpmG\/54 /(%jgP:^q,L*>sT?rsAxP4;Sj\QcU^Jdk"#U ы~lR_ؾ/h\=:1Fl+H1"랴ʓҏ|Xm2^IfNݭAI6-Z N~7KGNz=xˉ}oYzLgkĉ8DN+WUE, H< Gy0~!T^y逾|?PKѥU>faqs_split_000.htmlR0#_hlYUUaYjIj]{MQnX{o,yet>OPצQLtB98&Cb\z3ѡl"p$A=ENq j 5{yR5Gq:dXzIH ߾|gPo9:)/ʣH y:;Db@Z{ϠsHi v+[Ne~~ TT&DU҇9ԲWCǓf Tsmm]SdeCR3CCaA# D“3ȊwB=( v슂 Xox/]?`Nyةa=`I؀O [Qe nc1Sa/PKѥUmä,7cover_image.jpgXS.:"MjTPHSZD ((-* QA@P@"A齗i7s}{s1o~}`Ny8PQQ)?״ux漵y'O=lKH^%O'~R6'OМ=u&fVf3gX988Yq;"TԔshNҝ2}fg?8MhXۈظ ?%gdfe~++^Uدťյuwt[.*_\NPXiN^>uV͐s^fVH'(cxO)$;w [?${$ٿ rTQ`G'{M2T g/Egi-͓,gIΝɠ.pDvuPfH؎?[G~}(!I/yuhz?k.FEp=nclERwda^.P8P*#:7(ûթy/N rd{qqik\ quYe.7E;MN љ1/r喬$$^k@9 ΄ފ#ѭ Ò"=tVaLEKd~/ yKPVbL<31nђK3^*f1+%dW.Deb7g=h$ǰE88.5hxszܶu0Ƥgĝh;u*zA)P8jTHZ>.l'Mg,y-y^+tH-/k/`zjC{`$7ׯz:=}dڊ:A%"L˿G0e+I%jh/L̗l &P:':+qF`N0?,NotpNrM]q_uf!ʼn̻C|wμ4Y|#>N/Qer5gR)f/GRL戢m:>G✹ݿ8)LBBqɀ!@ "Xz^#@i:-Lumh8%8bb`3` 1p+#U̺_N8d耋 4HQ]ڇrgv/ {RYuLA dkJRPQ畇1bgE[7-g/qy9sDsGsI6Lͪ$g0_W]囃@4sW° ga'ڂ{U궰fu2"QIHNR sSٻ<Iӻ6>C[d ',\;fVak&  ݸ>A5/O0ץKk2|[ /0Z8]3R5y{?aLAglP 2o:WF 8g#J6/We}]cNWwzop2BŌ` uSU| sv{ B[{zL!#Cxmx]>ߎ=4NT'hF 1sh'Rp/=5_F=XHjtu&?9r=zV4>Ya 77JX7,0M,}ɍO8^GSyFj A-m0̡>b8S(h<m-cc{lnOYKV,O?rv3}ñǽ<(|iXNۇI<<(5S^p_Ui_ܿIw*>Adob/bJš ґ?@8,P"j"uwYLċpvx _p+zDzV-ߒχ3*1\Xl$o^ox4fM?$!_8\RRӓdn5J:=Xy$m%Ije~A\tZ^/X5%#8]9Zw8d1# 47 g-~v<ʐ_Ь}f^_ = ii@dVFN011ȦY .mޡGۚ^}hKWcM[=4ʓpetW7ٴ'NZI@R=9I%*7×ֱyJz |egP)_Nq !oe6#ܰf>3cz-W%YR­QGb\m cOISC2&>"0Rg*__e2].<[zHA9+6a:%hD`1 AI˚_?7[)b7[VҭzĻm`PAT9ӷ[2;|I2DRpl{ x@qUCtGNSNSև<&8d8/7.IeO]Ins)  )92[b(+8 c"(wfs;( "Pԃj/mS*qVžfKB咷:F5V4 -DB>ɬp0d_hPRjPy*jSk:IoTĨۭHOw #VZx׶GT3} d`2{#Nf`͘8nSXtD#۳DE ;h>sm|;6A]W~7&ni㵘T*_h9mJzf+~[%޽Z2O ~3% sqz3~uY8TldpEK7FGH/O=ڙ@Ngv=Aʀ*57%U;ԶJ;VLjfpAe26]Rrqπv=-Oog S\|ɚoYjq4}쪈 C6cTreC"H2K[Bx1!˶U R09j 7T-hSZ8A~enߒP4@18AЌ{XN{&ݸE}t|Nduac B'9̡8TV²s}Xמ jܥ-\1}5L89oLmsY-C q4~3tb1,xMorHo$B v# , .QB]Ue)&P ֠i饃ms]O妝 < FGn9Ѓ㧐ώ wRjaј ƹk\I7\~:/eRXuʊJI(!2L>P<y = PƬBS3~{ wZQϨ[gh.x-ˇ$D6xS(ܼsA>fR^b=v!SDU{gK'7C+SOJd,W,ȷ(7#e9JYn!oA\xZ`a9"\D0kǻs\'pՉMŽ.,F4̨9aC_ݡ6<(\,Ks[Ql[ЀW'if؁Z̊ep!{-1? hx/q ^4tHU#HܪhZe?b"\-${^(QP*Y䫰Ձm||\0їC+ 8gOqt^V}=!s媑 Pmfzԫ͓AMA'1sSSw+̽]O:O|moq[DW+&u}+4mk,\L› MӸ/hu$Kb-)rDQo/IɇZ(/;ڪC@(.rZ?rJ:hf)v>bUV O)҃-[kQw3Đd_t$0⒪{C &HF(1I)MtNL%9d^V WT80C8ݸD&a`Cڐ$RO_7:,nƬUr 7O+kѪ r' F# * | ;^^bz|jbal8d;^35?ڿpE$Y[,u φ{p,eg^O(;xql:㵄j|v628~CLJZߥ]+BlHEGUم^C_ {WCь} ܈&d.)@_"@b; pH!#Rd;'° G<70F OVwu=.f{l= (n}Yx *\f.prM8ct,F\};IDO*O3ڑ_@8@Չ9_p&3pVHm /srۨpCk;njj>w$X X {et[<1r")_|dDٯ4&}F}:ٌ \2`y DM}ck$,e)JLx~Փ]ꭹd~vI VH>IjCWC~ۃJˠ'u |ڻ2@DG?$̦E^E.F_DsRK5˹‚ٙ5:g;$-pL9!}X : Kp)ɭ'>V S}^9?cLSC2/[<.ʗIId,i0Y5wg 3ͳյJ%OgG~ǔÞJ 6żP~ tTyաLXplv]$\˙Njs=/L l7nSM@qEWN؏8~RUo-%įC%BΖs1V)9wIrs loxmeʩyv{ j̓ױ.S>_5;Wĝ,W{S8ٔÆ{R v?AaaH6X=%DU=;= W-%Ir óAOU,.X D R/^pets阫Ӻ|uSV۲{!ҿIgzҺ}wɞCXř*s*AJ 9:*xR! *Aĭ5!c ?ھF`-|Qjٓ&;nvbKTD$a AW@fw1WJ}bbdFUhjHj:N$AB8Hиfal{n/qb0{лR~;5^C^Gh+ ;%Nnk/roa&]~3q;SwJf|ma5wy.0f^4|V`Apexz5F]+մEAO':Ip& Ծ|o2w^e|^+n_MCJVQ ͈l;S=5h 3' K<D_MKB͒*RYsF yKqe#[D WuNt)֝౸Eδ"QC.:hpA/g}Z#\tS bҹ<6Zr^fblSldgOo:M m3OdQ̹8eG-td 6C EKi  ϭy-xۆ,ǥ 9aJ ?&8𑒝[9/fw2*J5L2:fSlLkHo>ҳтfn-fV7.;rּ."؀jfdʉi]AKǏMӍ3aMg>0#E⅍=Db82P ;~-NQ60ф.BM3Xf9=z Cl&QEE]K=roh9%a@]?P[Gn ,]lh_l̀=Q͏&|[%8sò8_qjY1; R.J ΁A.7?O~XդRH]iXI5; l f^W0OoP+3|PѯS.i]3#O,XM̮h}>N-IM̐n):3z ^Vsj.M~E{hϞKAmd̛N92*1$E;L^@ǵ,cV'ߔ}FNs0~+Хo7 iw~T| 3N%e [AR8]^W48Z`enF8\~s"_vwcݺ9GNᲖ30AX5H}0ٲ@W+tp]q|ԯozW=A%InY6x}Νyc\v&-;{݊+B#-r+v+PH-S/q t[MT,*,bn.0mG>@=BL.tR XvQ8R1!Z #M.Rxw[a 1ApXx4[DL*{g~g f4QRY 1]%561Zr86P?Q=Qs¢@uykwN3_NYL6^z,*x8MS++M" }W}RՇCRO9{Qs(};ܬ0?8 78M a sbN}s_>h˃׏՗-ԌC~u3F1</!c w"Gʖ[D:4|pg1[ z9НՎBekơ˻q+į}IW<<8u'MEi2J^È̪p<%ͬn#a6HބIw:_6p Žy~hTSYfju (Y`[\=Sїd:ʲ}u9ۏO^Ќ.ʹ{ սaٓ&=bPK"(A#Pґ.v|;0AzT",G;L ~՝f $qM)Դ-/fO n/[(yDفC3."wGv i]_>%*h!Ygo$hX̚j{å˪:BA$ݞR-4G}9坍i=0 )<#X&A34,Xju|x͉͢2u^o ~51WEF^M9^mY=EOu-xy  + VⰯR-)sҸm5yb1SnN4+ 0^4U{e pD:z~ybTA.l2fɳ@ب2n>,`J y*'}N+bA_Smf^tE٩mF֓w4тrFGaVɅЛσ1q9Za%m Ǐ@"`nQӦ"&);+9uDʙb:Y\=~JG)7'Y<3r g|FmQ@a@+oڙt2$+|AM T N7s$ 6̂]$##ރcdcp"%K%{\PuMl\6Xʆ6  l / 8z6ձԘnԏ|dJ]?Up7cQ̓a y6|=-gYj+7wx6^OF#CoX:lMoX =x۳1l&ɲ`7e)pۤZ*kȬCOӮWY @0gΘo?l =SI>x$\~Kx!N+wl9Hf; ա>e@Re Qi%Qѷomwa[c8a&m;pfe䔲@i-=&Al6 |)Ǫppp谤7Q_vB3ARxA&'@T+G| & 7OƎ!%,2P;0UY5toLx\?ۥU{'۹8`7*xU>Ug kwMCY/jdͳH]u{ڧ/UlDѮAc0\rWz $V DZTwcYI˜ѵTt)enˏ8Eת(-64kps^gkI<[onxFWK6XDבDKӇ| >Udz& UU|\-ƍU ؾj/xMcDClk܂U u <"CWJA>H8|a[^R&GBSRq05~&MpvJCQV^J"ǐe< 6t7YJśmud :Qg̲K&Vl*e'o <:0ßX?40w=>uDx4v]٩tĴ$XR3v8q4$L|0oy[U1ά)zޟS?BΝ! jwS/=ig(_jn9͆?sa!8ҬiN{p5f!/˨2ufʧ"l=̨IOHurX;gl8(H5e>$as\aqI^#v)Z&Ԧ4C}ޝJ 74 BDm V[ 3gnhBoY)onI*b|ڪ-1T~"%ys%@4z/f-&Pr WRez4螒׏ [ @f ^׉TeEd` ΰyn [pK{i 9yH1>I! W"d4~*ë^JL(`?rVZa,Dӻ9%J=]!V{VLxv Ec/|w 5=髅b̩W^3[>'ډ/DV:a /҂:> 63o/qy(1S_\yj;Z=XvSGQTeɦ"V=d\[Z6dGïiAyLكէ {c >XHىӺP20#` U!;G9.Okp.#OzqՃ:ڻWc5bϽ?ADY.(`OT.^V7j*iyXU~O֞E2ܞZgImr'x[i5zGER6E<}A~tap =SPm\xc&,lt:3%ՕB+`N_ ka t4Ȁۂ <'Ԫ`\"lTX7 8ICnv9!OVzŕ B..غBK!~<~&_xj.M[5BC?m{M 8[hk>T6VM㕧7 ebVnqε2'uߝ}k0O ϜTK/ɏl}u]:ܮC~0,[_uH"6_ݴÖY$\|A wN֪ki1b ZmWƔlVA37ӄ@U[~d9^/^꒾+BWϸyI|%V]D=.F]_Yj`o`Hyb&x1 WC$E}ssl hǠ'H`&2?nv$\L4-9|ADKgLeSo4 撏lM,rfk˩x75ޙU$A$K8lasRYÄXx񖈚q\iחTzKUᘺ6Ϻ BΡ6'}"NW{4M[he}Icփza zdqc.8U VZ Fs]ϏELp͍DgL଺93-[9ȇ&!V/lboG\Id` <>"~|T%{GZj >3Th_4+IYہgbJ d;S5XBEp7AQIB0펑&k.z8k3x+Ǟ&g{`T#3׾,ؾq6v ";bjĐkE"Ez-Q1T@QP@@ZPTޑ@ ߽{=}>go9g̲c|Xsߓ4*SDvhv %VpVZx=T.mKlcw?riަ%frhqn,lFb'K#(ia$()ԍDސ3"й2ՅMmFE߰&~]J +kq A&gOvs*MXHs8|%SeԤtMֆ+Ԍqqfs,*ЗGWT*XDIߛuELBp[{b-lhEbf!'p)miFMOݢ1sj0[14m-pow|gO I}EØxlfhyb@R> {Wx?Nk I`fXݮYN^>h_q1fEjvRLN2zn@WMZu\/l TĖ^sO~WKM༛\bDO_JA =jȾB"sG'̿hFU]^ƒ6"b ' ddhW ܋9]ٺeq6%jx7/;Go5h&GAQқRkR٦5l<agx1ֶ7.)Th#\gPr髿#%DwzA*/E+Sw;6qNZ.϶`_)~,WL'2A\w")lWR2Y$znĕ{)}Xx$}Ufڛ'DeɗW3dH,6m+lxiϫ׃N\5xyiΈAc>6Ai#LX޽#?gOj ?P$5AKn|v zk/5zghI P͢o^::d1F$FE$;r뻿5vnq j|{L\X]m2 [+X,x~ʌ^LOw씌Ѯ9\K7 4olR/)n)G* Qiyǚ 3yxKO ǧ w?UiUI)+1ظV7Xc&`7p{Y$Iz _b>ȿ1 hD6Ү^Y5;.y]e~_ {saS,N;LD4s'y}[~__=-(tI=uw`KAFX*or>h-:Za}Z[Wj+_UDLxuYAOrDey52՜k릮*翉hT`3kteaa2=+bo<~N̫{s!\Bg1y==8"(]*^.x1"?v9.-WE yZO/+>x{~ALۍ{OM8I͜2y2D_R Rޯ &X޳__: "~(ӋKD3&-{|"ݳ%=@Eqd hʉ,zA5[=cH"%2 ~J"ikm`>nV zCU/sˇ,ON1%.\ڋvI1ֆ9f{Nf0/ T3Q/02xdbyPuƇvfU4Nb.|ԉWg Ԯ]`zmT㈠#Xxs|ojx8,gyxr qN'du\v0~GXj>*'4"ǁ'@xxpv7I/2}8>1nZ"~%*>?/8;bڵ F#} J,{O_T$B2ށGIm٭8cφ%yi~MuLlފͥ e3v>\8ΰzk?6dtV l}@*n&.Ye@lď2ܿP/?ӪC> Ad7ںOdY+ ZY֯;q9:rt#g6i%Fڵii77-=Ulwxm5{?1F,?g/ij9qW⻧gCL+]vQ lOߺ^:ypSOk H{-EFQ3d@XMKLmik` >rY`ٙafh]{a$0 'd1}fـkGa$_[Wmk7^/h^X{'>jx`ܞ/SOf'aJ:Vb*Q.)ppwX%ku8)-Ѥv=^W!ILwXx3Ҷ(ˋJ1wH#KF?6NPLizi=%EvcViLsۃ|h楻H*VUq35zWo?|r Mɏġ;Et`;*7a92|QUPEƙcp3Wh'D^.z_VK Q4,RueU2j f;x8bnxC :&ف滶V`v[X`P6;!ܘ}fύ]RsnyկXSLAnv#ڥdW=Q UXʒՐy& |6{.̹#pSu `|X[R$vSN$᪋l 3L 8IܲUNq ; ? d-`x(sA'AeWI:FDž["qg ݘnSDi\9嫿S2Z>E_J9G TL̈=F;yuEzZ#/)pGFRbsL(q݁#fQW(pf`x 8U;w=7t6Nbɀ>gǕ-XeˀɀIP9ċ +}#5U8js [Րݍ0lD5Xej`5%7Tm|C{&#k3ڹVA n.z`QVOG=WTm=j6tz|N[t8N[Un X^wJ=ؒ0@BׯpAm G#Jnhy֏䇟Ӹy|z+gA+/_<|,'l紦_.Lݓ<$3]id];UAu}=_Tcn;kgTk{IGy؁D,*TvrCe̍I>VˏqRQJ8ƄNT/9 / oW7pxtWU@7罱I"m,YV|_VC,W=6tA&'?o-AmӴa+b `jVMҥd)@p6`KKf,3]w%W7;0(؂Eo_G (&:. wgMPi4T+%_F;qJ\dī )iX1cytr[&) PCpnEY*nH2PQiŚ)jed酈HQ*07b,tU_]q˨ь_g#Em1[>#0{EӋ$F oX,cwv$>~smLC`h9n~I'W^'G[a0COTL'Gh TsNA6ҙA\%Ke D4ļ8K b6H^^rװ=~Mw2U愫5%L(&[?j2{iqx0AB64mUmYJI{wBFj93'2$qObry_Al[p(`q"&| ReKjں1IjfHraCS)ۛ8Kg[|,ٷb n+} d{>؅-ƍi`|uڤWx +ʼnOV&FrżPWdX6_&N}x ~OWW5(n%W*yJLT~s+0 yu%M.ͤ9Oճ@1:V#5{׺`j]Dҁn;߁7 ȧ%F,y 蘤E>U+EW=qs:I'ߣ0#ݝ`:D4ƊD1ϫn"q0ӱ~;KWrkV[ .i{fZU٬J4YC~(p3?MԚi= CZx'n/aFvfre3u9Ė\;2`tM*в"-ļh n>s!A"bUzŰSBRIYjXG ŚJ'G b[wwQQD5X=]\˓d?=ZWj5qŸZawp\UCDᓎdL1οed:q=&q6OP6͞ҜLeZz?+4ՏqބA]+A? "`\#n¦WUjNŽ3QiIJmieR+u\ P$yg_c6$.m:D`8zXXǮysAbjꢃ]Qštҁ_[ P8KԁFdyoT%b |H.1DCä/4UaFK?+`OGpC`w ݤoV"b''^kG= y8B١f8 +[5@ZU_}Wv0L\qU6j"k 1|FtR<80Z(@ftls~7>} l r Jfz&|Ѯ9#@;Ok[[y; /|.wc5,uou58ʐ[?WCyU( o Ytӛ#)PV;]cZXa>+N'A[k5,]]@>|0`>;Rz$N|5RPnc<~V0i v5QeY#|[:NPP1pVr梻z0^Y|R *=ilɘ32C\kTϦTcgj4Fl &L%N>{NE_,``t>ݍPHbOR_[j&|B%`{bε0:l1}x"s ̏=vJZZ곰, P])wYtxf`d5 _]V3B&0ZJ`h# +_~ g=r.=Qz7wup 8Eۢb(btFjOVv]S0tȬ< Pdz%Ce% Ph vOGo>bǢz*$_ׇ7QMddYy /l?4W_nhR]G&|Y .)5W0ɤKM[)aCK0`hw09km@l./2 %^,"Ī^8qd/xѬs$)/6MZx% כS*t=~FN!Q3xBs},%RETSQ`>ѨkSr]w)?;v5uq-j\GSΟ;)ďohi "}'KrO}`:y&ݏ֏Xs{8B`mLp.נ bn'LH}$AkPFÅzZbҝF~! m\ny2Vk23.bcY]&QӲz٘ma?U|?Ϥ1V[ـw$)@-p#R[<1C} / u 8p,Դpť2hѸ!c k6a-~B냄ҸV4&43^PE79#ԯzvVȀ9Ħ~x.G.%]gp}#ߔ@  ߄B;QeVã$- X;<.P֚RV\c1*_S>ᎷfE۰b_RP=۪z}K6^C{Z[q}Op/xHKd/$} U#f:aZ}8;'qr%dl$K[7kHX gDc)_.UI2963E>ƛHhiz>TQ;>k,j: ;AFޜ+SI\/%53[Pg6^p[]ʤ7ɀ;dfBnaa8K _FZ(6N^hYk}pn ҏ:FG_+=`nhXUz&i>tW}MZ$:0o]a{ T0xzI{[Xs:uF$J d$&“%mem|ԛA5 僿w]H있 ueњ8O&cU%[>z{lLeO kȩ5Fu"<TYWv#Rt_*  xnwm=XfosM"6}΀#lߵe7t8OƷsʈ\ᯏn>0p,2b}PK2molߗ|۫cNȯ)Ѿv2ci3$kƛ D4&?TīNXkN/_?vоI;M}]&PMFJV|.I0蹒KɳB*+XB?._qYdc|#dsz7`)IDy\̮" w<.olbx>:mݥSGx>5f!OQU\%[?l#mjTq{Ev7/'|9Wh=7q%hd-`%Ɉs2o(O8f҆5<ٱpКWh 309y!{P\>bʽǫXP2ZY":;ƭ[drwb< o4[Ȧ^׫Q $z1 1[f z%C/ サH=f>c=f!'W w48 ΎJ6y~)dUDσ_[~ aČwDC^|7Gȓָc%`2|,{0i)G_*dZ{ZEE9XϾBP6j>&@bN\lag s#tKUkͿ~]{j_4!o6{k|!.{O9 ]ٿ5`[i7__NQyTh3$nrH#OجE}buks;,?Ȁ0ndCdb"`CKX( E\RS3'Z}dn{t je}r*'b\3p,mM0T\K/E\xO|&S.kŎ5 Q@ )zD9Uy[A9DtD`Wu0 ՛IZ΃f=R\en._Rnkw0 tczw3}ջ^أm/>_6L<>NWxVGu)-Y^y:,=|:krP/j4[< gk( mr2NF_RV]#pGr枪ov^ !o!$i"+jn2O6X]>+P},s3Ax /s -FpIQuķ-.tq)Jˆ!w!ee!{onGQ 9LN"ox+J{|c] 霩;_bq?YPW֩@QtQK R"uS#%df؆zgt2Y@9~rh \@D͓{-dL{VO9K# +@WpgޞNiy0Cseڟ_bZR: >SWDU?"uAIB )Pyi{yɜL„4sZ &_1y[lo^ܮ1ZL|@=Goʢ*<^lj{u{e3WL~'<w ~t62C;HEXL6!]zx+t֣`*_tT;VlSjIo *!GwfC ulߢч04E dևP= QQNFbpsJ&S0# x;AAMG•#t\ `[{{l0E2I>bP B(v bޡ.9+{iұQG?0s1=5F>Y.>5#{=PBSCQ:tyƩj:*/.<ܖ叝oԇ@#)>:.vDS? ?JD}rD[J* z@=Cta=t1B ?ֿg_[ٌ~Hu#柏WܪMGΜ wJ:.Yy(">J͓p{(`oeY@f᪌ X)\|.KW6sGx:fޛ|S.:YFB@Y]iZ냾Tfʾ&(>̯><߅"FE`?՟CA>?JktI dwhP@O?=&: NJ#O?eG(Ἷΰ'$zюoJMx2SaԐ mѱQ^NPH?֮L|19ʏI>ېy,AapCQ~MQQ݌,kT@ˡOPh+GP8 |}cU펪9{_EtJ?dLwG5FL*~ ڇ1q}'0<&l揂1(V(i]Jcn"EUř˞=9әLRK]T/J!.!sZŸAeJ0%,R()^HJ#:(~?C7[A7E(>)ʂ"g$QD8Ga#3ٗSȘ\m? iWtK(`JGA_HzDGJGeQOkՇ^X(؋C }vch2R)RPxN7C;ȗGUU4DEFI߂Pd.y1B(Ψss e2HK sY H<"K\uYfGN&CB9T K+p 7t;?,;D( Ӣ{J[YsT$P切gY|GgJ*g&o-9Qj0e׳c[E&6P* S+,ec!)Hj;Q,$ɀ .6pBN5dbGn'&着KLwa^19[ts~j,<cG cE)&HIL;ؙyYT` E1U} ľrS}zrS4)kD3~ xI`-kDw1aJE0׸ckpiPL=g. NKhx\\Oױ#zZжHɀ)n2 ]\ݘb b&* 'dY*_5E!%wߎ'"sI%!3VtO8n"Rj& 13@VsMoI'ix-O.p͵ NU_nm[R언}Jн˰=!ar\oZ*Lg`Y4N s囫zK(~<#4bȿxvsw~广L6I:\aldcT[GFQ{z:g2`>qRog'V~"қw Vd@Jt":Wq:x3 _0i'CM%?+.vBʻQr ߎsg{Zv҅'Bxke:)qiwx*yN!)ӶT*̩bztc]?⧃D]S)qFyU)H㐚/>VrPD( v635ێ)? >&%v¢q(w"ص U&O샂%;U@uD?|2rwk{$*5?HNVnx_`P ˢA?SŘ& (V 3fU[?#Ԏ<|WU,yZqVyAuXG4vXi})R`ѯ%Bek4 4n,DN+}ɍ i@6Zu >t6kJSz'paxHl}I·7֥'dTstXP0ϽѤD/EЇ`FIz̩Tn_W&42=9{[eHMB  D71А'f3/Zxya ?}2W+Txsy >hl*L*vJ{_MWy'C`^եcY'F4BSܮ.OMuGLEױIL|t" ]-LU([φ~OV/7y h y#ߒ^dTXls30Āp֌%S/i^:հ!~g\ -$.8`EaoLo+IWtTsWv3ɮk7D AzGB޻ 4EZ* P J-$;?93g3ϟ5^{ٳs_tPk~vPW=^VPH'u mp^&Mp4w醊 y Hnx!IvxJ ΊPdX$uZNQeVK('RGA/uRT%KKZL֔'5$ ?:oTj=P%9Ng'Fi:D!!}U2A`Ӓ+Ѭk˥> JdSizeFP8*HҐ.;͹rjlK'婳_DƲy;}R=+_QRU~ܡg޼Of=N@ኲy؃M (YSTmXlad{9ҧGO6Jz{Daey.7ѩjYK~/?%sܜ3K PO{hEUΕ<QFM 4G 8-: UfٳI ?DtrRAjT?li yȨreC !ȼF.-x5Z.k,.vM^J؃-Y찐OiyyQ 909L-p{Cfzسk۷;2F^5}Ͳˆ~,]~9േ_mu}D(("a =AKϥ2ZD.3_ХgүK 蟽⻰+t!|]$Ocu/+3_ Q^s)W^7ZD俱R5d'ʯ+ D=C^aMnߘ`p_馮Z W)~.t\ M33vK,B)!Ih8i]2X6ODј%]ywZьSX]T"r[K2,GSki(qw b{"jq>q62t (ũBdM^Tpu>jٛI|<6z&O)u)$s<,y`_Ԟ7 ;JFk3O Z[9K' ] 1itUV_ wIv!7T8yLNOSfk`\b`u! X?9OD΅֖zbKe%o$;Q 3v.'UT-I_~Hsp(^AONŽe`P(G%0Pw%ceM>m5Ph9];FS%D;"u~߃<  ~01XPvҶ$ ,&:ku8' S6o{Ia452޿ER9=$ r*'(rҋ֚k6@1ƪ꺘W1A5(nJɜ㺡9ޔفՕ]c=Q&6dhVuBYžep^^܄\ F]Pg"&Xg3Cos8۷I.j+X=`Qr0=eˁk)ga/A׎8Eh피f9!H(Q*Xni &Zi8̻;2<7V[0N]5/R{r@뽆YTdj~ [^Yc "H^]Ԝ1zcmYNv~iLU{Ku_!gJ6$ʜ[EgojtJuY+A=8 c-2;ٙvږ<JL=f: ~wvb2t/&&B08o+gNX`)YQBh58pFݚ:\WЮ}^BZ~>ԋGG\w5,S1.Y߭ЮtGJSOjS%ǞN *O%޳xAy?5, R{|D8ETWPWʯ#=QR/y!J kZ6ю=]xVj(&Yu{X(c+O(a;4x+l:=M۹w ElmE,CX`JQqf:xI}pm{BNWm?,A fD{@*F\?%"AbX$ >6 ĂpxAzҷ{&v*.[6v 9:x{NBCTWT$mb5P+FYDe^mʛ,KAX<AP½{5ݓ z}rNjFK\"Q$TCVg;h{u1:܊z.l*{!@eЫWK?D5dKhHc{JipD1݃(Eʄknu* ʲ2_g*NtgZGNwo94W})}>6ƺ3mտ&/m653țloS(;9ݶa2x0fŹ~Ra7OfcLN96[<e  ɮmҟOO`y'Rm,c2HD$ 7f/Ee[Ϭ|;WX N!lSxOˆؼUe˕LH:;ƾHa;pN~B7t 7ۗA4z1ayA"霛h+6#r Z!:<V۹w/|!wyYT~>ގڒsEj2xf^VըC4Jx߻+a,˳94 Sho9Y4;<.f.{.i>bc4Ǽ ?jM52]deĈpaU뛝5gEI*!5t͙ $ H )sԚ)ImSe6j]K^Â%fS'K^#ɍsroMj212;֘~Wߨo *TPʁA..[*Y MnD. sI[)=k$ɱ  P~I`C=n҇. >:-u.q=w2Q$/57/(-NH[=}9=*Ip8_g:|_Y̋׆@7.@mBqmp? T3|DW!:u+,iO?ޓZK<@-s<.;bp>6N4$$Z(h_,/8KlCq4JwH6x^:e}}]Cb+IbYf6ޔ(ȡE`~fldI]=^М:sfw/jk~6Z`h+7F \<Ƥ(nk'٦lt)NY z`SJ'}pVs KEdeG-\U*MJar @DzS]$26LIV42R/0l[(̫kkT~*>w?qcpUF}ɳ]j &}w{Ho9ŕ?zb c/ 0I%|i~' Gq].fy0 }Kɤ䄤&ͤ;g䳳ٲ<5/6Ti 8IIoL? G\#UzͿ{;Sn2<>WC2X>_? |zБ-5 Ľj oZL;ѻm~t74!f749Պj$v%8i@3ۤiTӀ`h `8K< %mZo*{ 3]>hf4ޓrȊk2[4SDmJPEMo%NY;GrSl G=eL".q2л RMe/jq23o.Gjg+~]LpKѬc]dkmfr-5`"8ѹDD160(]u Us,Nv$ʸȦ,Xe[ 4,WJBPגgfFQ֭o~9bnb#ΕAK)rMuF0RAҮm>J0YLx 뀸r>C&f5CDa==EwWp[2}eeKֺ%t1CϽt޺ lvYqgoO}ʬX|] pXm]$OaD wd*F\&<5Pv?tŨH&zRXKǾO$w 2-ehsOAش,J0Z@ֵ.|3"uttۂF0|U*̛@m!'Yq\WtYCz懩9n] 6 V@>pe7|Q WXzRۡDá1K=xXTF&?mjM$I.Wp[)?5PICRh(K`B/A?u.Ke)9_R+o5ZD+?_{oohI<Dk/-5Q/H$NPKѥU6W(titlepage.xhtmluR]o0|_Z'I*KHU9`NQ{mHמ7{riZ'\ټ--zϯa*0ŠK1?ǭ+El0@i=n~ebeC!Dwoe=:BȗJ!"n!կ=bV70׵yqе ״{ L,CMA3Ja8Ss8E/JcSw~iAЏҦ{p/bL.KYzLa[Y?䝔Rx.t[ b28x3~g繐 c2Y8Uv&*SEXŎTYl7U8/$zLN]||<!ˤLoʫ"2/O7=.~g;'S:J~uXhU>O$$ >^]4,l~9b a|,.TIcK Ɔ]=a:r0*s0j<7[rkO+6/~C͉J^%0$J`D9R h:@& ZV'K'waHIbS<ɧw1T3nVvB]Bg[ݪBx#q^NVFn2:D'01`@|:}p1 E)-. mEUXn?e"z)? j '&wp@* ,s04YJPC^N ὃ|(#z ZQ N`r +B.y3{ ,G߲;DF?pݬ%vXs`pJ8'n5HtfGۑ]SK3|ҟ=N^_|@nzFB  C:#@nk[ei 0;*ѻ>YB1Jna- B4&WD 5v=o>J#2J+Z|`Z3}v!j!k-):m8@gW 5zV unxwˇwgD xb޴%gD_`TIivP'9_|׷r#G+UR(q-j <^,gsk3}Dws1/^;CѰpϠSum߃8V#ij=-tSs'R"Ѫ5V'$y- GVz,ǣ =N0Z P_ ռ},9BWj)0ɄOGyDesPLGD}pꎎ3O;i'c"aD̥-jERGy\|R4y-\Bk9AO)%FWFCܓ:UE(m}8H:;n#qx',9q[&;OCgfQ3jAlta=D|l2m%N"^w)(&MgvN&6,i:iύd%쾨P'J34Ì!Aw8ßW~wd%g0j Ry CÝų쓱h  09!p$Qg@Ytnp_Y&6GZ$ikER43"iXJ,3ԤvTRo w_!P"EG Z9]΍VSރsO0 Pׅbn 4H^`o iiy^".&!^0f ɡZOJ_12teUxftvik…{-|qX%ҨKf@Cɸ=80SFC͊; MJwE>@*{24@W FŠlI햠"Tl8~0bv?/NBθ] bh Ӗ 3iI2`4/l9Xo }@wز*{rw1uUV UN',I@l&n.źu>[倒1,<)0_9) riҲÒ={ [;g6kMEŢIL"|%piT;L;=wu@2Ie J(̒ Tc睾F4d֜ rrvh$hXR*Mt6U~$KaJFx$ a: +bK/$(fQ" ٲ8׹a%|ډ ٌFUky|RBʩ$(1,h _~xwr~tڝr πaq/][TASEV{m@wdj. ɏ$SL^hl򟉪Z@{|RZHI1T?8Exy1 W $@HIO!7hLhϗpϰMr&pE :^Q.%B%im-V%#s:,]aIJ)O<7IW=~m#5e ~=xtȥ"[hSE"ODzKXR߱O_8Irhfs0hk1$')m>opx0!)z9>ݴ]l<PϿfb}_4fS#rmBRBlXxPl8*V>|kzUE#F*njm$ 0}f#MqПiNetF~[3Dǰzo[6|蹼U#AWشC*LKʋjz#@6G6pyIJhㆼP vġ <7" MeWYFyu#JcEE^iWo=W뗶,VPb T|kt 6}g\9-&+pz*+=V&=$f4~g =$ 'ć2aXe‰vb<8G$Ami(9`Ў lLj ׆0D;4NÛq|AAez zRṼSЌT%M\m+:nIS>0c\䨢dVjױ}&PM5H4&o.de_ZgQ48cc 18{ej ,Ǩnɳlv~Jc5栗*@4*xz.}OR?SNlaWHKA.a]XDW^X(0sY_`hU~!@c (ģ~ICDl< _BB1-М4$_Ġ8wZ&T=XdLi9Z`艮O8^6UHcF%{<6Zo=lEVoq}l>kj5UةȺ%8.k2)RJLbN5W:4F\RH]+oZ(x>&{BWc/li "tԚ:uRjDTSj((Z%qw.#hzh@~1m*k5;]0u`*2V=tPapWác@I+?]̡}Qz{!kŸsfyZư*1|Pc#JZ!e$m $z=޵qPI񐋝ww8U$\T'9f.sqQ2Mk v5sCc%;yP2\j"/sl`GutU RU/=iz)ߒŦ=v vgѽe-# mjbi5өrS^,9)IlHe+J ~k#]`L%+s(D@m]+M1X8kGܒ:x'X>2%#h㴩;~5[t|6K`& LO? Ī÷'˽hid{CRk S qFIt^_,9M)M[|n"g jSQIW2%~u^S+RGwr)@LWY︅W;b/ {=ھ;3pt9t4'g7YuWj+]HAF 6X2?RL-Igr[l,Qb]ok_P*._g.X;Va%';V@V%imqO4 ڸzwk}i-cTa6)˜\Ana"Ό\laGu!Lp 2M8ݿ-*qA}ȴ{l6\^bS>Njֺӝ+)㺋BVܰhۃ~]\A kE8d\Y7< MRzm ݚx$=P׵@r#^SG€ZKDC=GtɐN7x V87vI$?QPSAdU^i86SCٵ\T_c5ae ;@?9y&T @} 3ɾ9(3z4|gb!gqRsN3oMI=h4D*šE]1`@yewUTPV,k)ckF(|5gM蠎7<>"P\MG?&)6(v[w\ӃBc!ڼ8N{{z jE:ra`KRX-fZѻewdؤlY 0r ,f{aFb5DgKlXw~`+ gA#ӧc8m3 ˒ 9iUn*_5מӈc92Q\DRwmi7pV)!JzQc.G&j^ kY;+8$@Fyc&m|0&h$[ȸhjD@b:uGj~a2rL Bn*Rڅ۽>kXҵWٵWڻ'o =>B1*&yAzL \߳$#7 `AvZ+TBjlķeDym@_" 5(}a.k7](vX[’kBpg@.ES̄8rAǼH& VJ'fTyML8BғgLe`$kyxF(,Lװ5Q={^8n/ ؾәՊRˊVk}>9,k ;)ciպ'W)+O֋[l$}ՒK©/vPMP?jNd.fCզ SP Hݭe6ŸgAVA~LثW^*h6IntR^^;9cGrL6W>6&4 e -zd洂[Ј W`ζ> Ӌz>g#dCEgqT7{YsZEpV%])\"h;)-B!od8m$o8?lĢkR8lQ[!-it+P5qy>|0Gk@yJ}!%a"=?',:Bƿ ٬Zo1'IlWιrZT>RǼuyC )U3}8&D`.F)]o܇lÈ#_GG/Nۗ'bL?ʂ&ԲBS.8"ïuq݊.kؾNlp4;Q^P?paYjDy g<Һ[.RGhvuVtvkKP3A]ׇ&saLn[بsTB0@_*]>ZLF\7.׳H#L@%+93U&F=r\S m&HY~ g[+6D QTYOX̷?ѺaG+YP5KY*nF;[bUJɏ4_-?+s[ZO4*막H|'_5=GEq(knᐽ/J~M>a ;66Ooz-s-?E?\Q;ߖbSPQئ #,uu} zȿrݟo]%s KےwFDSx)q՘c6j-J8ݖcum"gF̒^!25OY>eS4I,!<륐֋+kb<6eo%-a,{nCʻ{9u2`'+T,-LVZXq5S4-<faqs_split_000.htmlPKѥUmä,7cover_image.jpgPKѥU6W(titlepage.xhtmlPKѥUusUl!ȳfaqs_split_002.htmlPKѥU7 ''3epage_styles.cssPK snmptt_1.5/docs/faqs.html0000664000000000000000000007164614277306025014262 0ustar rootroot SNMP Trap Translator FAQ

SNMPTT FAQ / Troubleshooting

(www.snmptt.org)
This file was last updated on: March 25th, 2021

Table of Contents

Installation

General

Q: What version of Net-SNMP should I run?

A: If you have no plans on using the Net-SNMP Perl module (see the next question), then any recent version of Net-SNMP should be sufficient with the exception of Net-SNMP 5.1. Net-SNMP 5.1.1 and higher can be used. If you plan on using UCD-SNMP 4.2.3 (provided by Compaq for RedHat 7.2), then you must at least use a newer version of snmptrapd.

If you plan on enabling the Net-SNMP Perl module, Net-SNMP v5.1.1 or higher is recommended. This will allow you to use all the features of snmptt. Net-SNMP 5.0.8 and 5.0.9 can also be patched with patch 722075 to provide similar functionality. The patch is available from the Net-SNMP patch page.

The standard way to apply the patch is to follow these steps:

  1. Download a fresh copy of Net-SNMP 5.0.9 or 5.0.8
  2. Uncompress the archive (tar xvf net-snmp-5.0.9.tar.gz)
  3. cd net-snmp-5.0.x
  4. patch -p0 < /path/to/patch.net-snmp-722075
  5. Compile Net-SNMP as per Net-SNMP documentation

If you run freebsd, you can simply copy the patch file into /usr/ports/net/net-snmp/files and rename it to patch-snmp-72205. When you rebuild Net-SNMP, the patch will be included.

Q: Do I need the UCD-SNMP / Net-SNMP Perl module?

A: SNMPTT does not REQUIRE the Perl module, but it is recommended. By enabling the Perl module, you will get the following benefits:

  • Proper $v, $E, $O, $-n, $+n, $-*, $+* (and others that use textual names) variable substitution
  • Conversion of numerical OIDs to text form
  • Conversion of INTEGER values to enumeration tags (for example: Status is now OK instead of Status is now 1)
  • Conversion of the enterprise into textual form for logging to SQL databases
  • Ability for traps passed from snmptrapd or loaded from the snmptt.conf files to contain symbolic OIDs such as linkDown and IF-MIB::linkUp
  • Variable syntax, description and enums when converting a MIB file using snmpttconvertmib

Unless Net-SNMP 5.1.1 or higher or 5.0.8 / 5.0.9 with patch 72205 is used, some Perl features may not work correctly. The use of Net-SNMP 5.1.1 or higher or 5.0.8 / 5.0.9 with patch 72205 is highly recommended.

Note1: In addition to the regular Perl modules, Net-SNMP allows Perl to be embedded into the SNMP agent (snmpd) and trap receiver (snmptrapd) by specifying --enable-embedded-perl during compilation. This is only required by SNMPTT if you plan on using the embedded trap handler (snmptthandler-embedded).

Note2: Do not confuse the CPAN module Net::SNMP (use Net::SNMP;) with the Net-SNMP Perl module (use SNMP;). They are two completely unrelated programs. Net::SNMP is a stand-alone SNMP module for Perl, while the Net-SNMP Perl module is a Perl extension of the Net-SNMP software and is included with Net-SNMP. Distributions such as RedHat provide the Perl modules in a separate RPM package called 'net-snmp-perl'.

Q: I want to enable the Perl support for under Linux, but I installed Net-SNMP using RPMs. What is the easiest way to install the module without recompiling everything?

A: There are two Perl components for Net-SNMP:

1) The Perl modules which allow you to create stand-alone Perl programs that use the 'SNMP' module (use SNMP;)

2) Embedded Perl for snmpd and snmptrapd which allow you to write Perl programs that are loaded and run from inside of snmpd and snmptrapd.

The Perl modules (1) are optional but recommended. See Do I need the UCD-SNMP / Net-SNMP Perl module? for the benefits of enabling the Perl modules.

Embedded Perl (2) is only needed if you want to use the embedded trap handler (snmptthandler-embedded).See xxxxx for the benefits of using the embedded handler.

For the Perl modules (1), most Linux distributions provide RPMs. For RedHat, install the net-snmp-perl RPM package using yum.

If you compiled Net-SNMP from source, then the Perl module should be enabled by default. Typing the following is an easy test to see if the Perl module has been installed:

perl
use SNMP;

If you get an error message starting with 'Can't locate SNMP.pm in @INC....' then the Perl module has not been installed. Press control-C to exit Perl if there was no error.

For embedded Perl support (2), you may have to compile Net-SNMP yourself using the '--enable-embedded-perl' configuration option. To test to see if you already have embedded Perl enabled:

  • Type snmptrapd -H 2>&1 | grep perl. It should give perl PERLCODE if embedded Perl is enabled.

  • If it's not available, you need to compile and install Net-SNMP using the --enable-embedded-perl configure option. Use the net-snmp-users mail list for assistance.

If you are using UCD-SNMP, you should not have to re-compile the entire package. Try the following:

  1. Download the source RPM that matches the binary RPM you downloaded. For example: ucd-snmp-4.2.3-1.src.rpm
  2. Install the RPM
  3. Locate the installed source code. For Mandrake, it should be in /usr/src/RPM/SOURCES
  4. Go into the sub directory perl/SNMP
  5. Follow the instructions in the README file

Note: If you have installed Net-SNMP 5.0.9 or 5.0.8 using RPM packages, and want to apply patch 722075, you will need to re-compile the entire package and re-install. Patch 722075 makes modifications to both the Perl source files, and the main snmp libraries so the above steps will not work.

Q: I want to use Net-SNMP under Windows. What should I do?

A: You have at least three options:

  1. Download the Net-SNMP 5.1.2+ binary from the Net-SNMP home page and install.

  2. Download the Net-SNMP 5.1.2+ source and compile using MSVC++, MinGW or Cygwin as described in the Net-SNMP README.WIN32 file. This should create a working snmptrapd. See the question: How to I enable the Perl support for UCD-SNMP / Net-SNMP under Windows? Note: Net-SNMP 5.1.2 or higher is recommended if compiling under Windows as it contains the latest Windows specific compiling improvements.

Q: How to I enable the Perl support for UCD-SNMP / Net-SNMP under Windows

A: There are two Perl components for Net-SNMP:

  1. The Perl modules which allow you to create stand-alone Perl programs that use the 'SNMP' module (use SNMP;)
  2. Embedded Perl for snmpd and snmptrapd which allow you to write Perl programs that are loaded and run from inside of snmpd and snmptrapd.

The Perl modules (1) are optional but recommended. See Do I need the UCD-SNMP / Net-SNMP Perl module? for the benefits of enabling the Perl modules.

As of July 2009, embedded Perl (2) is not currently supported under Windows.

Native Windows:

Install ActiveState ActivePerl and then the ActivePerl .ppm module included in the Net-SNMP binary available from the Net-SNMP web site.

If you compiled your own version of Net-SNMP, see the perl/README document for instructions on compiling the Perl modules.

Cygwin:

Download the Net-SNMP 5.1.2+ source and compile using Cygwin as described in the Net-SNMP README.WIN32 file.

Compile the Perl modules as described in the perl\SNMP\README file.

The program snmptt-net-snmp-test can be used to perform various translations to test the functionality of the installed UCD-SNMP / Net-SNMP Perl module. The value of best_guess can be specified on the command line to determine how translations should occur. If you are using 5.0.8+ with patch 722075 or 5.1.1+, use: snmptt-net-snmp-test --best_guess=2.

Q: Which trap handler should I use? snmptthandler or snmptthandler-embedded?

A: The standard handler is fine for most installations. The embedded handler was introduced in snmptt 1.3 and is recommended if there is a high volume of traps being received.

Standard Handler:

The standard handler is a small Perl program that is called each time a trap is received by snmptrapd when using daemon mode. The limitations of this handler are:

  • Each time a trap is received, a process must be created to run the snmptthandler program and snmptt.ini is read each time.
  • SNMPv3 EngineID and names are not passed by snmptrapd to snmptthandler

The benefits of using this handler are:

  • Does not require embedded Perl for snmptrapd
  • Has been around since v0.1 if snmptt.
  • Sufficient for most installations

Embedded Handler:

The embedded handler is a small Perl program that is loaded directly into snmptrapd when snmptrapd is started. The limitations of this handler are:

  • Requires embedded Perl for snmptrapd
  • Only works with daemon mode

The benefits of using this handler are:

  • The handler is loaded and initialized when snmptrapd is started, so there is less overhead as a new process does not need to be created and initialization is done only once (loading of snmptt.ini).
  • SNMPv3 EngineID and names variables are available in snmptt (B* variables)

Q: Are there any sample files to get me started?

A: Yes, the examples folder contains a sample snmptt.conf file, and a sample trap file for testing.

  1. Install SNMPTT as described in this document
  2. Copy the snmptt.conf.generic file to the location specified in the snmptt.ini file (probably /etc/snmp/ or c:\snmp)
  3. Add snmptt.conf.generic to the snmptt_conf_files section of snmptt.ini.
  4. For standalong mode, test SNMPTT by typing: snmptt < sample-trap.generic
    For daemon mode, test SNMPTT by copying sample-trap.generic.daemon to the spool folder
  5. Check the logs files etc for a sample linkDown trap

Q: Why doesn't the snmptt-init.d script work with Debian?

The snmptt-init.d script provided with SNMPTT was written for Mandrake and RedHat. To make the script work with Debian without requiring any re-writing of the script, copy /etc/init.d/skeleton to /etc/init.d/functions.

Q: I have enabled net_snmp_perl_enable but the variable names are not being translated into text using $v_n, $-n, $+n, $-* or $+*_. How do I troubleshoot it?

Q: I have enabled translate_integers but the integer values are not being translated into text. How do I troubleshoot it?

A: You must have the UCD-SNMP / Net-SNMP Perl module installed and working, and must ensure UCD-SNMP / Net-SNMP is configured correctly with all the required MIBS.

For starters, make sure the SNMP module is working. Type:

perl
use SNMP;

If you get an error after typing 'use SNMP', then the module is not installed correctly. Re-install the module and make sure you execute the tests while building.

If the MIB files are present, but translations do not occur, ensure UCD-SNMP / Net-SNMP is correctly configured to process all the required mibs. This is configured in the snmp.conf file. Alternatively, you can try setting the mibs_enviroment variable in snmptt.ini to ALL (no quotes) to force all MIBS to be initialized at SNMPTT startup.

If everything appears to be fine, try translating the variable name by hand by using snmptranslate. Get the variable name OID from the snmptt.debug file from the second Value 0+ section, and type:

snmptranslate -Td _oid_

This should return the OBJECT-TYPE for the variable if it exists in a MIB file

If you are using UCD-SNMP v4.2.3, then the variables will not translate properly because SNMPTRAPD does not pass them correctly to SNMPTT. Upgrade to a newer version of snmptrapd.

Q: SNMPTT is not working! How do I troubleshoot it?

A: Start by enabling enable_unknown_trap_log in the snmptt.ini file. Look inside this file to see if the traps are being passed correctly to SNMPTT but not being handled correctly. Next, enable debug mode of 2 and specify a debug text file to log to in the snmptt.ini file. After a trap is received, take a look at the file to try to determine what is going wrong. Disable both logs when you are finished.

To make troubleshooting a particular trap easier when working in daemon mode, try the following. This will prevent you have having to continuously generate the trap on the host.

  1. Shut down SNMPTT
  2. Generate the trap
  3. Copy the resulting # file from the spool directory (/var/spool/snmptt) to the /tmp directory as /tmp/test-trap
  4. Edit the file, removing the first line (which is a large number that contains the date / time)
  5. Run SNMPTT in standalone method using: snmptt < test-trap
  6. Troubleshoot by using the log files etc

Q: I have configured SNMPTT correctly with an snmptt.ini file, snmptt.conf file(s) etc and it still does not process traps. Everything appears in the snmpttunknowntrap.log file. What did I do wrong?

A: You probably didn't start snmptrapd correctly. Make sure it is started using:

snmptrapd -On

If it is not started with -On, then it will not pass traps using numeric OIDs and SNMPTT will not process them.

As an alternative, you can edit your snmp.conf file to include the line: printNumericOids 1. This setting will take effect no matter what is used on the command line.

Note: If the UCD-SNMP / Net-SNMP Perl module is installed and enabled, then SNMPTT should be able to handle traps passed using symbolic form. The Perl module (used by SNMPTT) in Net-SNMP 5.0.8 and previous versions should be able to handle single symbolic names (eg: coldTrap). UCD-SNMP may not properly convert symbolic names to numeric OIDs which could result in traps not being matched. A patch is available from the Net-SNMP web site for 5.0.8+ to allow it to handle other symbolic names such as module::symbolic name (eg: SNMPv2-MIB::coldTrap) etc. The patch is available from the contrib folder, or it can be downloaded from the Net-SNMP patch page. Net-SNMP 5.1.1 and higher contain this patch.

Q: I have disabled syslog support in SNMPTT, but my syslog (or NT Event Log) is still filling up with trap messages. How do I disable them?

Q: I am using syslog (or NT Event Log) to log trap messages, but there are two log entries for each trap received. How do I prevent it?

A: What you are seeing are snmptrapd trap messages, not SNMPTT messages. SNMPTT trap messages start with 'snmptt[pid]:' while snmptrapd messages start with 'snmptrapd[pid]:'. If you do not start snmptrapd with either the -P or -o (lowercase o) switches, syslog support will be forced on. Snmptrapd should be started using 'snmptrapd -On' and this results in syslog being forced on. The reason for this is the original design of snmptrapd assumed that if you are not going to display messages on the screen or log to a file, then you must want syslog output.

A workaround is to have snmptrapd log all messages to /dev/null, or to a text file that can be regularily purged if needed. To log to a text file, start snmptrapd using:

snmptrapd -On -o /var/log/snmptrapd.log

This will cause ALL snmptrapd messages to be logged to the file which means all snmptrapd 'system' messages such as startup and shutdown will not be logged to syslog.

A patch for Net-SNMP 5.0.7 is available that adds a new switch (-t) to prevent TRAP messages from being logged to syslog, but allowing system messages to continue to be logged. The patch is available from the Net-SNMP patch section. This patch is available in Net-SNMP 5.1.1 and higher. With this patch, snmptrapd should be started using:

snmptrapd -On -t

Q: I set translate_oids or translate_trap_oid, but the trap is being logged in numerical form. Why? **Q: I set translate_oids or translate_trap_oid to 1 or 3 in snmptt.ini, but the trap is not being logged with a long symbolic name. Why?

A: The current version of Net-SNMP (5.0.9 at the time of this writing) and everything before it does not support including the module name (eg: IF-MIB::) when translating an OID and most of the 5.0.x versions do not properly tranlsate numeric OIDs to long symbolic names. A patch is available for 5.0.8+ that will appear in later releases of Net-SNMP (5.1.1+). The patch is available from the contrib folder, or it can be downloaded from the Net-SNMP patch page.

The program snmptt-net-snmp-test can be used to perform various translations to test the functionality of the installed UCD-SNMP / NE-SNMP Perl module. The value of best_guess can be specified on the command line to determine how translations should occur.

Q: I converted a MIB using snmpttconvertmib but the OIDs are appearing as text instead of being numerical. Why?

For example:

EVENT linkUp .iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTraps.linkUp "Status Events" Normal

instead of

EVENT linkUp .1.3.6.1.6.3.1.1.5.4 "Status Events" Normal

A: Snmpttconvertmib uses the snmptranslate command to convert MIB files. With Net-SNMP v5.0.2 and newer, setting the -On switch on the snmptranslate command causes the output to be in numerical format, which is what is needed for snmpttconvertmib.

With Net-SNMP v5.0.1 and all versions of UCD-SNMP, setting the -On switch will TOGGLE the setting of using numerical output. With Net-SNMP v5.0.2 and newer, setting the -On switch will FORCE the output to be numerical.

Snmpttconvermib will use the -On switch for snmptranslate only if it detects anything but UCD-SNMP or Net-SNMP v5.0.1.

If you are using UCD-SNMP, or Net-SNMP v5.0.1, the best option is to modify your snmp.conf file (for UCD-SNMP / Net-SNMP), and add or modify the line:

printNumericOids 1

This will cause all applications to output in numerical format including snmptranslate. Note: This will affect other UCD-SNMP / Net-SNMP programs you are using, if any.

Q: Does SNMPTT use DNS?

Only if it is enabled. See the Name Resolution / DNS section.

Q: Is there a front-end alarm browser available for SNMPTT?

A: Take a look at SNMPTT-GUI. SNMPTT-GUI aims to provide a web based frontend for SNMPTT. The GUI is made up of server side perl cgi scripts with client side javascripts which interfaces to a SQL database using DBI::ODBC.

SNMPTT-GUI questions should be directed to the project admins on the SNMPTT-GUI Sourceforge page.

Q: When I convert a MIB file using snmpttconvertmib I get 'Bad operator (_) errors.

A: You need to enable support for underlines / underscores in MIB files. See the snmp.conf man page. Support can be enabled by adding this line to your main snmp.conf file:

mibAllowUnderline 1

Q: Double quotation marks (") are being logged with a \ in front of them. How can this be disabled?

A: Set remove_backslash_from_quotes = 1 in the snmptt.ini to have the \ removed from double quotes (").

Q: Each trap received is being logged multiple times. Why?

A: There are a few possible reasons for this.

  • The device is sending the trap multiple times. If the device is running Net-SNMP's agent (snmpd), make sure there is only ONE trapsink, trap2sink or informsink line in snmpd.conf. If you are sending the trap using SNMP V1, use trapsink. For SNMP V2, use trap2sink. For SNMP V3, use informsink. Having both a trapsink and a trap2sink for example will cause snmpd to send the trap twice. Once using SNMP V1 and once using SNMP V2. It is possible that snmpd is loading multiple snmp.conf files. Start snmpd using snmpd -f -Dread_config to see what configuration files are being loaded.
  • Snmptrapd is passing the trap to SNMPTT multiple times. Ensure there is only one traphandle statement in snmptrapd.conf. It is possible that snmpdtrapd is loading multiple snmptrapd.conf files. Start snmpdtrapd using snmpd -f -Dread_config to see what configuration files are being loaded.
  • There is more than one EVENT line defined in one or more snmptt.conf files for the trap and multiple_event is enabled in snmptt.ini. To find all duplicate EVENT entries, run snmptt --dump
snmptt_1.5/docs/faqs.md0000664000000000000000000006175514277306025013716 0ustar rootroot SNMP Trap Translator FAQ #SNMPTT FAQ / Troubleshooting **([www.snmptt.org](http://www.snmptt.org))** This file was last updated on: March 25th, 2021 #Table of Contents ## Installation * [What version of Net-SNMP should I run?](#What_version_of_NET-SNMP_should_I_run) * [Do I need the UCD-SNMP / Net-SNMP Perl module?](#Do_I_need_the_UCD-SNMP_Net-SNMP_Perl_module) * [I want to enable the Perl support for UCD-SNMP / Net-SNMP under Linux, but I installed UCD-SNMP / Net-SNMP using RPMs. What is the easiest way to install the module without recompiling everything?](#I_want_to_enable_the_Perl_support_for_UCD-SNMP) * [I want to use Net-SNMP under Windows. What should I do?](#I_want_to_use_Net-SNMP_under_Windows) * [How to I enable the Perl support for UCD-SNMP / Net-SNMP under Windows](#How_to_I_enable_the_Perl_support_for_UCD-SNMP) * [**Which trap handler should I use? snmptthandler or snmptthandler-embedded?**](#Which_trap_handler_should_I_use) * [Are there any sample files to get me started?](#Are_there_any_sample_files_to_get_me_started) * [Why doesn't the snmptt-init.d script work with Debian?](#Why_doesnt_the_snmptt-init.d_script_work_with_Debian) ## General * [I have enabled net\_snmp\_perl\_enable but the variable names are not being translated into text using $vn, $-n, $+n, $-\* or $+\*. How do I troubleshoot it?](#I_have_enabled_net_snmp_perl_enable_but_the_variable) * [I have enabled translate\_integers but the integer values are not being translated into text. How do I troubleshoot it?](#I_have_enabled_net_snmp_perl_enable_but_the_variable) * [SNMPTT is not working! How do I troubleshoot it?](#SNMPTT_is_not_working) * [I have configured SNMPTT correctly with an snmptt.ini file, snmptt.conf file(s) etc and it still does not process traps. Everything appears in the snmpttunknowntrap.log file. What did I do wrong?](#I_have_configured_SNMPTT_correctly_with_an_snmptt) * [I have disabled syslog support in SNMPTT, but my syslog (or NT Event Log) is still filling up with trap messages. How do I disable them?](#I_have_disabled_syslog_support_in_SNMPTT) * [I am using syslog (or NT Event Log) to log trap messages, but there are two log entries for each trap received. How do I prevent it?](#I_have_disabled_syslog_support_in_SNMPTT) * [I set translate\_oids or translate\_trap\_oid, but the trap is being logged in numerical form. Why?](#I_set_translate_oids_or_translate_trap_oid) * [I converted a MIB using snmpttconvertmib but the OIDs are appearing as text instead of being numerical. Why?](#I_converted_a_MIB_using_snmpttconvertmib_but_the_OIDs) * [Does SNMPTT use DNS?](#Does_SNMPTT_use_DNS) * [Is there a front-end alarm browser available for SNMPTT?](#Is_there_a_front-end_alarm_browser_available_for_SNMPTT) * [When I convert a MIB file using snmpttconvertmib I get 'Bad operator (\_) errors](#When_I_convert_a_MIB_file_using_snmpttconvertmib) * [Double quotation marks (") are being logged with a \\ in front of them. How can this be disabled?](#Double_quotation_marks) * [Each trap received is being logged multiple times. Why?](#Each_trap_received_is_being_logged_multiple) **Q: What version of Net-SNMP should I run?** A: If you have no plans on using the Net-SNMP Perl module (see the next question), then any recent version of Net-SNMP should be sufficient with the exception of Net-SNMP 5.1. Net-SNMP 5.1.1 and higher can be used. If you plan on using UCD-SNMP 4.2.3 (provided by Compaq for RedHat 7.2), then you must at least use a newer version of snmptrapd. If you plan on enabling the Net-SNMP Perl module, **Net-SNMP v5.1.1 or higher is recommended.** This will allow you to use all the features of snmptt. Net-SNMP 5.0.8 and 5.0.9 can also be patched with [patch 722075](http://sourceforge.net/tracker/index.php?func=detail&aid=722075&group_id=12694&atid=312694) to provide similar functionality. The patch is available from the [Net-SNMP patch page](http://sourceforge.net/tracker/index.php?func=detail&aid=722075&group_id=12694&atid=312694). The standard way to apply the patch is to follow these steps: 1. Download a fresh copy of Net-SNMP 5.0.9 or 5.0.8 2. Uncompress the archive (tar xvf net-snmp-5.0.9.tar.gz) 3. cd net-snmp-5.0.x 4. patch -p0 < /path/to/patch.net-snmp-722075 5. Compile Net-SNMP as per Net-SNMP documentation If you run freebsd, you can simply copy the patch file into /usr/ports/net/net-snmp/files and rename it to patch-snmp-72205. When you rebuild Net-SNMP, the patch will be included. **Q: Do I need the UCD-SNMP / Net-SNMP Perl module?** A: SNMPTT does not REQUIRE the Perl module, but it is recommended. By enabling the Perl module, you will get the following benefits: * Proper $v, $E, $O, $-n, $+n, $-\*, $+\* (and others that use textual names) variable substitution * Conversion of numerical OIDs to text form * Conversion of INTEGER values to enumeration tags (for example: **Status is now OK** instead of **Status is now 1**) * Conversion of the enterprise into textual form for logging to SQL databases * Ability for traps passed from **snmptrapd** or loaded from the **snmptt.conf** files to contain symbolic OIDs such as **linkDown** and **IF-MIB::linkUp** * Variable syntax, description and enums when converting a MIB file using snmpttconvertmib Unless Net-SNMP 5.1.1 or higher or 5.0.8 / 5.0.9 with patch 72205 is used, some Perl features may not work correctly. The use of Net-SNMP 5.1.1 or higher or 5.0.8 / 5.0.9 with patch 72205 is **highly recommended**. Note1: In addition to the regular Perl modules, Net-SNMP allows Perl to be embedded into the SNMP agent (snmpd) and trap receiver (snmptrapd) by specifying --enable-embedded-perl during compilation. This is **only** required by SNMPTT if you plan on using the embedded trap handler (snmptthandler-embedded). Note2: Do not confuse the CPAN module Net::SNMP (use Net::SNMP;) with the Net-SNMP Perl module (use SNMP;). They are two completely unrelated programs. Net::SNMP is a stand-alone SNMP module for Perl, while the Net-SNMP Perl module is a Perl extension of the [Net-SNMP](http://www.net-snmp.org) software and is included with [Net-SNMP](http://www.net-snmp.org). Distributions such as RedHat provide the Perl modules in a separate RPM package called 'net-snmp-perl'. **Q: I want to enable the Perl support for under Linux, but I installed *Net-SNMP* using RPMs. What is the easiest way to install the module without recompiling everything?** A: There are two Perl components for Net-SNMP: > 1) The Perl modules which allow you to create stand-alone Perl programs that use the 'SNMP' module (use SNMP;) > 2) Embedded Perl for snmpd and snmptrapd which allow you to write Perl programs that are loaded and run from inside of snmpd and snmptrapd. The Perl modules (1) are optional but recommended. See [**Do I need the UCD-SNMP / Net-SNMP Perl module?**](Do_I_need_the_UCD-SNMP_Net-SNMP_Perl_module) for the benefits of enabling the Perl modules. Embedded Perl (2) is only needed if you want to use the embedded trap handler (snmptthandler-embedded).See xxxxx for the benefits of using the embedded handler. For the Perl modules (1), most Linux distributions provide RPMs. For RedHat, install the net-snmp-perl RPM package using yum. If you compiled Net-SNMP from source, then the Perl module should be enabled by default. Typing the following is an easy test to see if the Perl module has been installed: perl use SNMP; If you get an error message starting with 'Can't locate SNMP.pm in @INC....' then the Perl module has not been installed. Press control-C to exit Perl if there was no error. For embedded Perl support (2), you may have to compile Net-SNMP yourself using the '--enable-embedded-perl' configuration option. To test to see if you already have embedded Perl enabled: * Type **snmptrapd -H 2>&1 | grep perl**. It should give **perl PERLCODE** if embedded Perl is enabled. * If it's not available, you need to compile and install Net-SNMP using the **\--enable-embedded-perl** configure option. Use the net-snmp-users mail list for assistance. If you are using UCD-SNMP, you should not have to re-compile the entire package. Try the following: 1. Download the source RPM that matches the binary RPM you downloaded. For example: ucd-snmp-4.2.3-1.src.rpm 2. Install the RPM 3. Locate the installed source code. For Mandrake, it should be in /usr/src/RPM/SOURCES 4. Go into the sub directory perl/SNMP 5. Follow the instructions in the README file Note: If you have installed Net-SNMP 5.0.9 or 5.0.8 using RPM packages, and want to apply patch 722075, you will need to re-compile the entire package and re-install. Patch 722075 makes modifications to both the Perl source files, and the main snmp libraries so the above steps will not work. **Q: I want to use Net-SNMP under Windows. What should I do?** A: You have at least three options: 1. Download the Net-SNMP 5.1.2+ binary from the Net-SNMP home page and install. 2. Download the Net-SNMP 5.1.2+ source and compile using MSVC++, MinGW or Cygwin as described in the Net-SNMP README.WIN32 file. This should create a working snmptrapd. See the question: **How to I enable the Perl support for UCD-SNMP / Net-SNMP under Windows**? Note: Net-SNMP 5.1.2 or higher is recommended if compiling under Windows as it contains the latest Windows specific compiling improvements. **Q: How to I enable the Perl support for UCD-SNMP / Net-SNMP under Windows** A: There are two Perl components for Net-SNMP: 1. The Perl modules which allow you to create stand-alone Perl programs that use the 'SNMP' module (use SNMP;) 2. Embedded Perl for snmpd and snmptrapd which allow you to write Perl programs that are loaded and run from inside of snmpd and snmptrapd. The Perl modules (1) are optional but recommended. See [**Do I need the UCD-SNMP / Net-SNMP Perl module?**](Do_I_need_the_UCD-SNMP_Net-SNMP_Perl_module) for the benefits of enabling the Perl modules. As of July 2009, embedded Perl (2) is not currently supported under Windows. ***Native Windows:*** > Install ActiveState ActivePerl and then the ActivePerl **.ppm** module included in the Net-SNMP binary available from the [Net-SNMP web site](http://www.net-snmp.org). > If you compiled your own version of Net-SNMP, see the perl/README document for instructions on compiling the Perl modules. ***Cygwin:*** > Download the Net-SNMP 5.1.2+ source and compile using Cygwin as described in the Net-SNMP README.WIN32 file. > Compile the Perl modules as described in the perl\\SNMP\\README file. > The program **snmptt-net-snmp-test** can be used to perform various translations to test the functionality of the installed UCD-SNMP / Net-SNMP Perl module. The value of **best\_guess** can be specified on the command line to determine how translations should occur. If you are using 5.0.8+ with patch 722075 or 5.1.1+, use: **snmptt-net-snmp-test --best\_guess=2**. **Q: Which trap handler should I use? snmptthandler or snmptthandler-embedded?** A: The standard handler is fine for most installations. The embedded handler was introduced in snmptt 1.3 and is recommended if there is a high volume of traps being received. *Standard Handler:* The standard handler is a small Perl program that is called each time a trap is received by snmptrapd when using daemon mode. The limitations of this handler are: * Each time a trap is received, a process must be created to run the snmptthandler program and snmptt.ini is read each time. * SNMPv3 EngineID and names are not passed by snmptrapd to snmptthandler The benefits of using this handler are: * Does not require embedded Perl for snmptrapd * Has been around since v0.1 if snmptt. * Sufficient for most installations *Embedded Handler:* The embedded handler is a small Perl program that is loaded directly into snmptrapd when snmptrapd is started. The limitations of this handler are: * Requires embedded Perl for snmptrapd * Only works with daemon mode The benefits of using this handler are: * The handler is loaded and initialized when snmptrapd is started, so there is less overhead as a new process does not need to be created and initialization is done only once (loading of snmptt.ini). * SNMPv3 EngineID and names variables are available in snmptt (B\* variables) **Q: Are there any sample files to get me started?** A: Yes, the examples folder contains a sample snmptt.conf file, and a sample trap file for testing. 1. Install SNMPTT as described in this document 2. Copy the **snmptt.conf.generic** file to the location specified in the snmptt.ini file (probably **/etc/snmp/** or **c:\\snmp**) 3. Add **snmptt.conf.generic** to the **snmptt\_conf\_files** section of **snmptt.ini**. 4. For standalong mode, test SNMPTT by typing: **snmptt < sample-trap.generic** For daemon mode, test SNMPTT by copying sample-trap.generic.daemon to the spool folder 5. Check the logs files etc for a sample linkDown trap **Q: Why doesn't the snmptt-init.d script work with Debian?** The snmptt-init.d script provided with SNMPTT was written for Mandrake and RedHat. To make the script work with Debian without requiring any re-writing of the script, copy **/etc/init.d/skeleton** to **/etc/init.d/functions**. **Q: I have enabled net\_snmp\_perl\_enable but the variable names are not being translated into text using $v_n, $-n, $+n, $-\* or $+\*_. How do I troubleshoot it?** **Q: I have enabled translate\_integers but the integer values are not being translated into text. How do I troubleshoot it?** A: You must have the UCD-SNMP / Net-SNMP Perl module installed and working, and must ensure UCD-SNMP / Net-SNMP is configured correctly with all the required MIBS. For starters, make sure the SNMP module is working. Type: perl use SNMP; If you get an error after typing 'use SNMP', then the module is not installed correctly. Re-install the module and make sure you execute the tests while building. If the MIB files are present, but translations do not occur, ensure UCD-SNMP / Net-SNMP is correctly configured to process all the required mibs. This is configured in the snmp.conf file. Alternatively, you can try setting the **mibs\_enviroment** variable in snmptt.ini to **ALL** (no quotes) to force all MIBS to be initialized at SNMPTT startup. If everything appears to be fine, try translating the variable name by hand by using snmptranslate. Get the variable name OID from the snmptt.debug file from the second Value 0+ section, and type: snmptranslate -Td _oid_ This should return the OBJECT-TYPE for the variable if it exists in a MIB file If you are using UCD-SNMP v4.2.3, then the variables will not translate properly because SNMPTRAPD does not pass them correctly to SNMPTT. Upgrade to a newer version of snmptrapd. **Q: SNMPTT is not working! How do I troubleshoot it?** A: Start by enabling **enable\_unknown\_trap\_log** in the **snmptt.ini** file. Look inside this file to see if the traps are being passed correctly to SNMPTT but not being handled correctly. Next, enable debug mode of 2 and specify a debug text file to log to in the snmptt.ini file. After a trap is received, take a look at the file to try to determine what is going wrong. Disable both logs when you are finished. To make troubleshooting a particular trap easier when working in daemon mode, try the following. This will prevent you have having to continuously generate the trap on the host. 1. Shut down SNMPTT 2. Generate the trap 3. Copy the resulting # file from the spool directory (/var/spool/snmptt) to the /tmp directory as /tmp/test-trap 4. Edit the file, removing the first line (which is a large number that contains the date / time) 5. Run SNMPTT in standalone method using: **snmptt < test-trap** 6. Troubleshoot by using the log files etc **Q: I have configured SNMPTT correctly with an snmptt.ini file, snmptt.conf file(s) etc and it still does not process traps. Everything appears in the snmpttunknowntrap.log file. What did I do wrong?** A: You probably didn't start snmptrapd correctly. Make sure it is started using: snmptrapd -On If it is not started with **\-On**, then it will not pass traps using numeric OIDs and SNMPTT will not process them. As an alternative, you can edit your **snmp.conf** file to include the line: **printNumericOids 1**. This setting will take effect no matter what is used on the command line. Note: If the UCD-SNMP / Net-SNMP Perl module is installed and enabled, then SNMPTT should be able to handle traps passed using symbolic form. The Perl module (used by SNMPTT) in Net-SNMP 5.0.8 and previous versions should be able to handle single symbolic names (eg: coldTrap). UCD-SNMP may not properly convert symbolic names to numeric OIDs which could result in traps not being matched. A patch is available from the Net-SNMP web site for 5.0.8+ to allow it to handle other symbolic names such as module::symbolic name (eg: SNMPv2-MIB::coldTrap) etc. The patch is available from the contrib folder, or it can be downloaded from the [Net-SNMP patch page](http://sourceforge.net/tracker/index.php?func=detail&aid=722075&group_id=12694&atid=312694). Net-SNMP 5.1.1 and higher contain this patch. **Q: I have disabled syslog support in SNMPTT, but my syslog (or NT Event Log) is still filling up with trap messages. How do I disable them?** **Q: I am using syslog (or NT Event Log) to log trap messages, but there are two log entries for each trap received. How do I prevent it?** A: What you are seeing are **snmptrapd** trap messages, not SNMPTT messages. SNMPTT trap messages start with '**snmptt\[pid\]:**' while snmptrapd messages start with '**snmptrapd\[pid\]:**'. If you do not start snmptrapd with either the **\-P** or **\-o** (lowercase o) switches, syslog support will be forced on. Snmptrapd should be started using '**snmptrapd -On**' and this results in syslog being forced on. The reason for this is the original design of snmptrapd assumed that if you are not going to display messages on the screen or log to a file, then you must want syslog output. A workaround is to have snmptrapd log all messages to /dev/null, or to a text file that can be regularily purged if needed. To log to a text file, start snmptrapd using: snmptrapd -On -o /var/log/snmptrapd.log This will cause **ALL** snmptrapd messages to be logged to the file which means all snmptrapd 'system' messages such as startup and shutdown will not be logged to syslog. A patch for Net-SNMP 5.0.7 is available that adds a new switch (-t) to prevent TRAP messages from being logged to syslog, but allowing system messages to continue to be logged. The patch is available from the [Net-SNMP patch section](http://sourceforge.net/tracker/?func=detail&atid=312694&aid=695312&group_id=12694). This patch is available in Net-SNMP 5.1.1 and higher. With this patch, snmptrapd should be started using: snmptrapd -On -t **Q: I set translate\_oids or translate\_trap\_oid, but the trap is being logged in numerical form. Why? ** ****Q: I set translate\_oids or translate\_trap\_oid to 1 or 3 in snmptt.ini, but the trap is not being logged with a long symbolic name. Why?** A: The current version of Net-SNMP (5.0.9 at the time of this writing) and everything before it does not support including the module name (eg: IF-MIB::) when translating an OID and most of the 5.0.x versions do not properly tranlsate numeric OIDs to long symbolic names. A patch is available for 5.0.8+ that will appear in later releases of Net-SNMP (5.1.1+). The patch is available from the contrib folder, or it can be downloaded from the [Net-SNMP patch page](http://sourceforge.net/tracker/index.php?func=detail&aid=722075&group_id=12694&atid=312694). The program **snmptt-net-snmp-test** can be used to perform various translations to test the functionality of the installed UCD-SNMP / NE-SNMP Perl module. The value of **best\_guess** can be specified on the command line to determine how translations should occur. **Q: I converted a MIB using snmpttconvertmib but the OIDs are appearing as text instead of being numerical. Why?** **For example:** EVENT linkUp .iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTraps.linkUp "Status Events" Normal instead of EVENT linkUp .1.3.6.1.6.3.1.1.5.4 "Status Events" Normal A: **Snmpttconvertmib** uses the **snmptranslate** command to convert MIB files. With Net-SNMP v5.0.2 and newer, setting the **\-On** switch on the **snmptranslate** command causes the output to be in numerical format, which is what is needed for **snmpttconvertmib**. With Net-SNMP v5.0.1 and all versions of UCD-SNMP, setting the -On switch will TOGGLE the setting of using numerical output. With Net-SNMP v5.0.2 and newer, setting the -On switch will FORCE the output to be numerical. **Snmpttconvermib** will use the **\-On** switch for snmptranslate **only** if it detects anything but UCD-SNMP or Net-SNMP v5.0.1. If you are using UCD-SNMP, or Net-SNMP v5.0.1, the best option is to modify your **snmp.conf** file (for UCD-SNMP / Net-SNMP), and add or modify the line: printNumericOids 1 This will cause all applications to output in numerical format including **snmptranslate**. Note: This will affect other UCD-SNMP / Net-SNMP programs you are using, if any. **Q: Does SNMPTT use DNS?** Only if it is enabled. See the [Name Resolution / DNS](#DNS) section. **Q: Is there a front-end alarm browser available for SNMPTT?** A: Take a look at [SNMPTT-GUI](http://sourceforge.net/projects/snmptt-gui). SNMPTT-GUI aims to provide a web based frontend for SNMPTT. The GUI is made up of server side perl cgi scripts with client side javascripts which interfaces to a SQL database using DBI::ODBC. SNMPTT-GUI questions should be directed to the project admins on the [SNMPTT-GUI Sourceforge page](http://sourceforge.net/projects/snmptt-gui/). **Q: When I convert a MIB file using snmpttconvertmib I get 'Bad operator (\_) errors.** A: You need to enable support for underlines / underscores in MIB files. See the snmp.conf man page. Support can be enabled by adding this line to your main snmp.conf file: mibAllowUnderline 1 **Q: Double quotation marks (") are being logged with a \\ in front of them. How can this be disabled?** A: Set **remove\_backslash\_from\_quotes = 1** in the **snmptt.ini** to have the \\ removed from double quotes ("). **Q: Each trap received is being logged multiple times. Why?** A: There are a few possible reasons for this. * The device is sending the trap multiple times. If the device is running Net-SNMP's agent (snmpd), make sure there is only ONE trapsink, trap2sink or informsink line in **snmpd.conf**. If you are sending the trap using SNMP V1, use **trapsink**. For SNMP V2, use **trap2sink**. For SNMP V3, use **informsink**. Having both a trapsink and a trap2sink for example will cause snmpd to send the trap **twice**. Once using SNMP V1 and once using SNMP V2. It is possible that snmpd is loading multiple **snmp.conf** files. Start **snmpd** using **snmpd -f -Dread\_config** to see what configuration files are being loaded. * Snmptrapd is passing the trap to SNMPTT multiple times. Ensure there is only one **traphandle** statement in **snmptrapd.conf**. It is possible that snmpdtrapd is loading multiple **snmptrapd.conf** files. Start **snmpdtrapd** using **snmpd -f -Dread\_config** to see what configuration files are being loaded. * There is more than one **EVENT** line defined in one or more **snmptt.conf** files for the trap and **multiple\_event** is enabled in **snmptt.ini**. To find all duplicate **EVENT** entries, run **snmptt --dump**snmptt_1.5/docs/index.html0000664000000000000000000000255214277306025014425 0ustar rootroot SNMPTT
SNMPTT Documentation

SNMPTTCONVERT Documentation

SNMPTTCONVERTMIB Documentation

SNMPTT Frequently Asked Questions
snmptt_1.5/docs/layout1.css0000664000000000000000000001136514277306025014542 0ustar rootroot/* www.snmptt.org The layout for this page was [borrowed|stolen|abducted] from the kind folks at the Layout Reservoir. http://www.bluerobot.com/web/layouts/ Here's the permission notice from the page on Oct 23, 2003: Please feel free to borrow, steal, abduct, and/or torture the documents contained in the Layout Reservoir. Though you need not give credit to BlueRobot.com, a comment in your source code would help other developers to find this resource. Enjoy. Some of this layout was also [borrowed|stolen|abducted] from a kind person (Nathan) from the Nagios site. http://www.nagios.org */ body { margin:0px 10px 0px 10px; padding:0px; font-family:verdana, arial, helvetica, sans-serif; font-size: 1em; color:#333; background-color:white; } h1 { margin:10px 0px 15px 0px; padding:0px; font-size:1.5em; line-height:1em; font-weight:900; color:#000; } h2{ margin:10px 0px 15px 0px; padding:0px; font-size:1.4em; line-height:1em; font-weight:900; color:#000; } h3{ margin:10px 0px 15px 0px; padding:0px; font-size:1.3em; line-height:1em; font-weight:900; color:#000; } h4{ margin:10px 0px 15px 0px; padding:0px; font-size:1.2em; line-height:1em; font-weight:900; color:#000; } h5{ margin:10px 0px 15px 0px; padding:0px; font-size:1.1em; line-height:1em; font-weight:900; color:#000; } p { /* font:11px/20px verdana, arial, helvetica, sans-serif; */ margin:0px 0px 1em 0px; padding:0px; line-height:125%; } /* #Content>p {margin:0px;} */ /* #Content>p+p {text-indent:30px;} */ code { background-color: #eee; border: 1px solid #999; display: block; padding: 10px; overflow: auto; } pre code { background-color: #eee; border: 1px solid #999; display: block; padding: 10px; overflow: auto; } a { color:#09c; font-size:1em; text-decoration:none; font-weight:600; font-family:verdana, arial, helvetica, sans-serif; } a:link {color:#09c;} a:visited {color:#07a;} a:hover {background-color:#eee;} /* Moved to snmptt.md for Requiremens section so that it doesn't conflict with snmptt.org web page table { border-collapse: collapse; width: 70%; } td, th { border: 1px solid #aaaaaa; text-align: left; padding: 2px; max-width: 50em; } tr:nth-child(even) { background-color: #dddddd; } */ #skip { display:none; } #Header { font-family: verdana,arial,serif; font-size: 1.5em; font-weight: bold; margin:50px 0px 10px 0px; padding:10px 0px 10px 20px; /* For IE5/Win's benefit height = [correct height] + [top padding] + [top and bottom border widths] */ height:33px; /* 14px + 17px + 2px = 33px */ border-style:solid; border-color:black; border-width:1px 0px; /* top and bottom borders: 1px; left and right borders: 0px */ line-height:11px; background-color:#eee; /* Ugly brilliant hack removed - messed up Konqueror. Just set height anyways otherwise ie6 messed up */ height:14px; /* the correct height */ } /* I've heard this called the "be nice to Opera 5" rule. Basically, it feeds correct length values to user agents that exhibit the parsing error exploited above yet get the CSS box model right and understand the CSS2 parent-child selector. ALWAYS include a "be nice to Opera 5" rule every time you use the Tantek Celik hack (above). */ body>#Header {height:14px;} #Content { margin:0px 50px 0px 200px; padding:10px; } #LeftSide { position:absolute; top:100px; left:20px; width:172px; line-height:17px; /* Here is the ugly brilliant hack that protects IE5/Win from its own stupidity. Thanks to Tantek Celik for the hack and to Eric Costello for publicizing it. IE5/Win incorrectly parses the "\"}"" value, prematurely closing the style declaration. The incorrect IE5/Win value is above, while the correct value is below. See http://glish.com/css/hacks.asp for details. */ voice-family: "\"}\""; voice-family:inherit; width:150px; } /* Again, "be nice to Opera 5". */ body>#LeftSide {width:150px;} #Menu { padding:10px; background-color:#eee; border:1px dashed #999; font-size: 0.85em; line-height:1.4em; } /* Again, "be nice to Opera 5". */ body>#Menu {width:150px;} #ModificationDate { margin:0px 50px 20px 200px; } .Copyright { font-family: verdana,arial,serif; font-size: 1em;} .Email { font-family: verdana,arial,serif; font-size: 0.85em;} .NewsItem { font-family: verdana,arial,serif; font-size: 1em; } .NewsItemUrgent { font-family: verdana,arial,serif; font-size: 1em; background-color: #ccff99;} .NewsItemDate { font-family: verdana,arial,serif; font-size: 1em; font-weight: bold; } .SectionTitle { font-family: verdana,arial,serif; font-size: 1.2em; font-weight: bold; text-decoration: underline;} .SectionBody { font-family: verdana,arial,serif; font-size: 1em; } .ModificationDate { font-family: verdana,arial,serif; font-size: 0.9em; } snmptt_1.5/docs/snmptt.epub0000664000000000000000000077706514277306103014651 0ustar rootrootPKХUoa,mimetypeapplication/epub+zipPKХU META-INF/PKХUtMETA-INF/container.xmlU D~٫i+[VU"ѿklqgg88'+H%9K"*Zʼnv Zmj+ ЙSYϽ".aZGihrW"wk[#.㕎 W䎥6yPKХUA![) content.opfKo6F6Y NQ`2@wGmNDO_Ze{nHH،c]E?m?Y?4lSkvǃ&ϛUgQh x}̲ԕ6m]|6bqth?6mgWƛ(Z֛xs^?7,]w諱MYduiXʲ[[ozcejyܻ_ŵs可&qMSUNW^ӗ+w훩S"w}Y,|_SoӏfoU6EoO(Xn]e&yi&mb>VcsKi,Qc2Ym.exsΓ|S/^ \3)yFWfwKecxjq[ +oapk'BY17),OdYNf–Z/$/-kzxjz1+4n&p/ n/ϮSJt8mLߺx:݄dJ_.ԯIӉu>t] GXVSe&My(W UC|7.mZ5:&9*cW :̾80'*R!L%t:j;0@x0d'I 0ɷ`f q4rJbfkpLF'7%][*?+0| Xoqt4bAWhxltʸ1l ZyZQS*p=~G:K:C]Əa(zN>?1/$^yW_r> &Z1\:T jdÌ' ;r]34 6m YEbAW&WksOC3s.O?^ʾ>Jq=725*(m@`@^N^c+u什'o6JǑX!J85%i/!(@e>9hDjK&  +I~F}烲zSd)r8d{,WӲ.z<;g~T/{*]:Fw7 tj2)>rj#PRj'.ERxX] n*?wP8d$ځ셵*nZ~[=ϦJ-ԕͦMLO"WG NeIaML%`͘l#&s>;v7(],'2Lw)s0Ja 3hgvh/rIaH|*5"@H`IAPM%sc{N@:Mpa&.QmbU> G&loEI 0iP]ٔǵ02 -t1Wy7eCm]jz2d5O;v;Mo`c Rl|MˌPf!RW*w=!̣9*K/Q2rmܕsKŕݾ1DX{Jᢏ2 Iqh!CW4Ίl%0:lEfcZ,5PY6J fLYiޝh #ky5.`Sɺ\]";$RE-3UrJV!qVݔ5;%lГoJ΁3b ޯuM-G%.UVP,pؑe"B_L>wؘ 'n'tg747 1 +iHwSbh}08\{?K7bPKХUљF4F 1snmptt_split_056.html[msH_1ź N\eMvjkt3Z@~uh$CXx\*֋Q3J$ ,ǛMg{0 X 6>y+XK< tLw TN39aN'2bETB\wsMz7'Cӿ}Ϭ\dw+<ɔif,F Fo?hc9xG~gR瞘J˔tiD?t]JW*`'ݛOpyF?crnd!BjwL#Աva|@"WLSF+e#6̯p.XX,KoXTB,4`lVWpۯ]&OHCb_ 4rv/뗷w7= Ԡ'7'[FHѓ;EcnBArr1r2`D`1EKsVIÎĒyD # V[,B"@Ff!|8 E>>Fqcb8acv~Ox !c)#3APY$J*m SzL## ß{apPϋ1xE27|5p U'*#A $p>vZ/{Xuϳ'琢# (q(Iqo\0F~sG'0? TD3EaKs"瓁|7Bd ^i:;P͛.f) vMV'՗QH4Yz C|!CV\bΦ:T/kv#∓݊ W*: Տ非Xp Xo~#=RWLR "EzdCh'⢘=ҹXST>ok\*( IB]adž`*`w ` 03r娑+êO{.<|X[ :ӷ ` 8 BWJh]cExTa@?3bMP!/Jlz?17kMaPK)Os X'b /I y~#O|MBeAڥCsi]^Qb,.t#_˳ 8t >vTb [i]\ F1RHn%- Q \ܓ; ;G1˸6zW-,r$%L>j{%2W2C|%# bfRr+%I%rl}%T{-y-JSBDA4J0*sHHid#wC:3: ݔdN^99Pq E6vj7ƮT={ww|.eYt7]ӷD8<3]Gʶue7DZ10R4##VQj Ie"7^ٹ~N\7&&*gB w>AރW=HFJ=>>P(53PX(#m( ż&|x"C<rF95,a$\[ʼn? G͸h)B6r6Go|-&? 8hȂZ w.JObqx *?:O.Ĕ@wسK]36:J)XRʵO|@@ǃi; h5@LNHl~x0kѥ["~NT_;'YJLs }`Q98ysc HNZ=}9D^kV(kV3~*zȴՑ @EB@!B jRg"`LLJˁ..8g$MN,`oYzK4?ɍ PX/4{Dl7s#NYӉi2=%P{V+lhF$+/u{:0,ryʈmݐ|?$H)6-0-yG{ h9D\$hO`MDZB&jKB<:lEHEcTagVih%%xTꐢj ZU-G* 5UqB4F_AbZut%2PpTk~/T (W}T 巠_vߑڳ2o )&`IRcEiDt([8rE&D(GT=N6ţ>1 0ٶ'RUM\W1<7Mx+Nt_rKDD}PIVl!0AխwϹ D}.G2> {>е.#Z4vLF_ PUp7yp`!كA0d*Oq{)\EXC RB.  xYsڒCM!#SY+m0 ȏy)j|8S u[xRHt_k|,%= T`#狧F6l CFh,kp#}|gkޥ 6V A(AfO}[Tjt]`"S^fo%|AR[V"0=-(P.0y8w5w!f< g{#gp7N)p\Qfi AQ+|!rۮ:+;t]&$D)*vzwŢ/$]05Z ^ ` '1VQ`q}ijQ./R[BB2?2 WjJr! hV03~M헝No73 5 ; wNO٧)!IաYGKsQ+#4%<^4V%?, `< djV!]S{޾x _vehGAe1 .ŭ24g߹Bñs=<6ZB!#T^1U5T(ii}p'VqV:ԅ.J4Ԏp`c\:X9_0:P8k* ()mİMDIc Qt~]@NIJR2Kjڄ ~-h1fBU1nE21@urAm6ؤp'Qc+ tAj,ߐp¥*"AnӐEWE߼;n:x!J/cmvXJ5R.[AHJ|}R*Pv_c#e]ݍsupbvodj#F;:qqhM^3uWRƭj!>{M֮RL!~6>V֧u>cKH+Jh=UQi<2uDT2z#-Y h(+!Irv1=t %{^e]G?t=>~jjhegsES(vQx {]Ba) XJS> vWC's.\v^E\Sl$hpze#m ǥ ]6b}8r½"4= b'B-BTaHȖdW*p3)ӁZe* 8ADNKNh=; `m.J6K<^FZrsȎm"Ĉ[uygLmt cv޴o;BGL 2X߮"kW\lr6X3!gHcsFjc{1_0O-SW^ev ~u1%4 X_9}Vx{m>CM1+;}V~jWӉPKХUSj_DQ snmptt_split_069.htmlVmoH_1tJ+l@*חkF JWUbxU{׷f֘\@gmY +P:j3;ݺ3:y=6 wvn]m6a<[ 9/D'@g ʡr#,e6wQ a7 cYgm6Ha C|E *[ %y^/MEZп-}>[~|u8Z\\҈-8i<;E࿵oX:]GpxB.+Hsa,o@pZH ]6Gt8ؿZ@np}lS+~6uԮB L޽8p̧5O` IpXh#!b~~y3z.>{{$nmp1{ ,MAԅ0A'-3p2t: =,c#OWG\[Ђ P8uPp@xЃpLn(||[9 R6i^M0w?PxM`O;QUH.|ҝ0SK}E)>Ү} kK2"iX|QDiXG%GoW áROğg^f8ԁiBXiZi1A۾YeuB7?M&W:n29ٟRg55DX24%ͦ,ZIXTvg4 .m(%݄)T*Ե8]Ԇ:]wb ;nh daz#fn$qG^4 Gq'$&ğ//Qh# oq[͡VbĶ.Wdjzbd:E| ކ򙐔yi>V>~nO wWGsL͇#ġ Y&ͻi%_EM{Y܎R(x-t'Jĭ>p5+ٰ$ݝ-Y*oGzCk{J{$ԯM,4 u$Y<|/D!8KEQo_DlI$#KV}kj|E>~D^MT(mxNjAh{SV~%҉V #!!1b2<2Lid69mKF%>b͔AS2iE~hAs+~ܸuGzM_ ~o£o몢^Ԥ2΃&vU.?? PKХUr] 0snmptt_split_060.htmlXks9_P &6yd蕛F0.frWݹDys/˙Y -?dtu7WI$:7١ꈛ1ajLoaUw#'\'LCQ [%)zN5RɘaJ5-Sf30!8&!E3<#=\N}(ip:ʷ3,on>~HelWl "@hEPʑ&# vNP-RfSyZ?WƲNEDַ* m']jlvv3kufGu|ҡ`ҙ4Yl4qT:FkO:0H$jGAbaMpmwI88qҩGkonixLzE@+]-FZ6\4(Zvo'rVzp8;+T پiZ Brv]՟YlI[mco1k)Wy1h81i\h6*s`A '{jwqeu7LPKХU̪* (snmptt_split_037.htmlZmSί8;$t] tX2K`/Im:b+[r-9!=G'$v3$}t^G0Jlv7@BO63ӫtx`6U^FAMtOtxYHc b~RsSfTZ@Iå9|n7-1T t%dojJ,p}rFg.oDw._ Huw~ކ߯KOY涐ki)Kx\wkiUg&'sK=Nx?:9G,ܜ #!!IEqsS2WFWQOX߹ۊyP DL#n+{a.į+'/Rj0jPBQuvZ^*5vUJf4گ0U6CaKy" B5He #AgIR<0!|iZlPn/CND(+`2όՎ *Y~\>̷Ln4݂I'5.l=筮 ʉcBB ཝgk&" [ӊjTmmW+#%9ĸSѢEESKo [lOjo#E:t!xꎁaCEjD߰[U|J!,2`ϘͬXsx;ҀА(ER:ZEt? 301D,”@99a.[" &ך4yL/cI*abwI’3AB̦RUF(J`n}{„e^Tv\zX!pQlN2"pYE&!oK]Er_yÊa.͛ Tq/ks,8+^Ӷs/0}VXcWݣxRXCS <8H@[gt>hu= EH}z-)W'E\_]ݜ]5?_^7:s3UQNC oxtMś $!غIZimʔOxPKwifTy'?qv-z(Ojnզ '1B՞yCدuMTr Nm?êU~|{5~8Coz4ܱ8 '8Bؚ7nO8}+ޏHVR\/BR9Fq>:'10m*>,JJ*3GH. c REﳮቾ:>&Ӟ0b>ډ7y;dD\~S2:Uuiѹ\cZ/KɵS|4^) GC&5̔ɔiOY3re~;И;eoק: N9Rm+|zK-®K)㎠l{b~ZƧcŖLbWNVќ7{S=ܿ`+C㜔b-%>2~ki9ot8{:8*p}%$3D᥆ިoxں+58B$v_ܹьj$qtw/ſSf -gʤ&)PKХU.rrsnmptt_split_046.htmlX[s:~ϯ㗐N!I/@'ә!3dBr%9ge@N&ĖV{ݕi~ܢ6\f (#s9lmvPY#QiZڤɤ6٫)=  YqdclCU:HIҶ{g J{{=rN m]m'U5 QHzԎz3;Pe&{di#\os&b (~w\V`z%4 ޒ}*Jm+8T{raqjCď6h[`Sp4V`L!,ȏF08XUR  P}EpEBk EvWxvo&+Ē|PΟrj8$F^͌PCvU.y8 \0K8'`(`| 7BG1`$4GLv`ڝb}uATKm& 0+fh *+U$`Mc@nWiՅXU<|%PmJ\<]U+nމ"\Dg<}%U˴͒րҸ< 4ncԀS"b0" 0l{PǪ[9sWNPNNx7(m".]>x!'҅1&FcbEAtyƒd7 HUYyG.PBj\J1KP X*MP۴NUvQc4zF8!&3,U7- 1f't^ݾ#kμk+JL8POg4|q< t`]\:ozǗ+#L\L eCcl\SȢQa<'nVzEQkB]H(4vֻݺh}',e}!u[)|q ˾Zdk4rfX⼰x䷾- Uji$D}Hveo]%J .S?_ȦJIMW1yR2Ϭ^#itx9'/_tV̍G)=7~R{E|w\\X6zw==^oh>3~OY|1Fn1>h3XJߜ?:/E.sܤ|7vWhv.?<ѿT:v{prNNG,˽sIWG1*&?$Kk TbFx.H_1z8t?Ўi?PKХU~ "snmptt_split_006.htmlZyo8?0v۱M2hill(-=ۜHd>Hr;N^AwަJg۝,ұ&ۅ7mz?u f1u.5ki3iw޿߾5-! cL1 t4D3;h|߉6^\uMkѮ(%p+~+gtT)ryx[%lj}'CxMe[ g#%яˣˊS.ë?MHWO5B^.n]@ 4gFH%`![#kbj`\_ѢHIs9_Vo?x\Q"-B$52 {iWw|4|8ܗ<{Q>VWķtه9oC9J!{\inpioׇb8*&uMwDwۭ!.r.{^7{OQ8X\\X"ϵqlEvy&m q HhMM)4JX!^؇."oCzXlJZA!5 -D < m%s-vB<S0sZ$sq A& f|Y ;ki' +E:?ZFb^d%Hި<Oqi,t~N:e, <5QRH"%OL(~ 6:WII[[k6Їx8VB~f8i@o67H>=hXi1UjpzX C6&af~E wg$dF6Bb3E Xb9Jdlgǯ_u_k_I[=\VG?([_i]' TwXia-i¿ݽfruĘzTףڹװ3$➾|.~ʶqtU$||6ҾMl+r!,6H - fzfޱ4:DBeӉ\R " K"5))bD!p._)go~8`.$F&R sH1db:OrHf\X$/_7ˆ`AhHnI1YY+LӃ2vm>c Qb,RGԾ dYL@>8dx+D&##B%a+ڀ)JemAdu[8*& nXbXɆk ;#>q PBT7@c%"t`W?czo y-PHP1 `bLX禸E;^ϩ\}\ʑBF^dCeFs\?Z}Xy8JBn} !E(kORMɈa{DZzxmT%Uo5-N\ȣl*bK EŔk ϱܧPR{K;x4OWՉo=Jߟksl&WfCCB,F>vu%tOஎ ^\\'^DYo` 6O^ g&аե>j(x؊/sI@}rC,wˏ̓5qxَF '¶%eKإ%dv6`P)G(LώI>X.b67TE)}ZKQHO!."U֏xѰ3Yc.1~94cNޱB]35uT*^P{.:E-ŦȈ\a"ޤ`&Wp9dP4O$ئIr%:mN*b:e>O Tt}Tr=Q~$AJ?AgQ04i8g̋93@_RB0}r4R,6?\ nޖ aJdj&-u0ذiA^k×+JK OZ+kޏ"ޮoiFdg Je1|sb2Abu / q)&+<*`2`6rHH#UgA`٤+<._GaDg? ZɁL0Rـ N BW?!H a 軄VxjB(۬IXg.өgazbQ1fآIUOߌhZh ǨP,W W2ܭUh<1|cQyszb3{2 FK_xnq+O9(Q1-׃Kc;,T00a]M$r49s[ͤJzq"ѩ*&*wջ{S1v?:W:|6Noww`TK'܏&F8m"Jtd3tIOg͹uwΪVa&2 a妰WelogY|OHu|!MD)4J<8>]^zr<8%(luފ}mS~l?: g-K ad*n TG-3iRtԟ&F45vU"U(K1_X} X#\I&,L"Tھ}-_=HտwYjjm*t;۰āhע xo*4F9TAWd4WnJn* J>شE^E֟>; A5;M#rZ_5{S RgCNnB-]Ju{bIP<JL%1+BQe*WNt+lw2vd$@%+/Ҩte-إu2tLIGGGgGUUܒ`%p.uхՆ,*ÐM:a3G6uۘF~_[]cIpCׯ) O+"lRfX phV2 w53P9NioE+(`ڨ 钀=4\9{a:> _ J7WGKn=VV 诫ᬷḸ OG:ܪBՠ̧RLŮQ NX%cV TReNmUjĞuFjJ9k(ˬ5D"!2=+咗}%FGWUL:y ЃÙ() *Dk8! aZ79TKKegQҨ.vP~ꡧ`ydOz[on]uz=jAze@ݭ61t u 㦔 6t~v>Nr@b monP܋zz1ڧ\w߮loHB^EYD?=sB7+0zh$S!:8k://fbb\=SW`r֢hܵ^6! b7/BvjJ-3о8r^B#  uQdz355eM /O/2uHRip $mN+wX$H뻑+zVD^;۶[Uh=rnpb {Οcf2S3肱(:쯨;ftkX`7h^M+̺c9uח|e8/Bvx& }wȹ[cCMp+?>Ba|sn0j/]iE5~! "@dV=K>B;E4zMɳGXa44rK/w$ ?yO^eP~ݟ<G(eG? "|T0+9 5F=l׊?&? ]lj1v>_by40,ZzVߩhodW /Df5g,n) pȧ$G+9,(Ɏgo02t1L./CXOS4'KFO95]XSe5Dnt g`# ;㚒"!Kvpi7#BkU+wH{d;Ǖ 0u -&zFc~.7r. hV ,*8c1$~M+nl1%V6u7C#>utuu'Twe(k~`ERjj}5;w)8PKХU6pKasnmptt_split_036.htmltIQ힆4v%k㡤<}=BnD<&mW}Ev=YkLg8 #y $rdA6;\JmJn'Կ_^^J&z|sq~Enc7?p ,>"NHAR}h>"wXPy< X48 P1Tֈ9POt`+LȽw[:az8@./plH{U#OE^е"ĶOfPH&_C4|3|p0ֺ_@|!n,M^qaYwT1 i8gBlj5ǘ7!FAxȬ:֫ɉ!#DF'`Sq6$!;8 = az$ j4d@6WFDT NxA0{@L?O5fe" u%H H@A B܅H;L5]kAAĦ5I}~,!B I[fxD8S;6cF"pACL&}^"om䯏djmC6=k̓l#]"kӝ=8RY1 &ƥl]S+C Xi,*}cFzp$ fԠ3TW ƓD43OȍtcLyJ#|7VJXwsXȐWٵ2| rnк"/pP?W6@ii#m ZPp)36PUYsR\([E:1Ar䠏8[dr(yK(?I6x+ZgIC$mB 'ce39`M@NEΘU`g^cAO>0R9Lܸ- )+c9KuٳnϠRiS׆q Eu];b M e0{1NX.eOHc T7Om H_`<* ).X_KxZF4|ӆ69q59tv*SXTCs{!&UueNFo7,=.E*t +MPt b5(|X˵:UG_snX~:L-6ZyyYrzwA Z2x5?0L5 ՍbkT f9I{ܘTóמ.+`s:ngqӶؘ woPB8A% |L=ruv#S1b-b@܃RMAw {V0r{Yqv%[fC>=k[X:q69ᦑ"'TH!. XV(Q*`Dy-1Q!w15Deʑ.?1#R H%fM^g^;f32Х4?2兇 ( 9nSܦz2طӢ_Y3 +!lJ2NC[@<꺳Q7s۰ 1bQP=U؍|B]P [ .((AC/j=,l̴ߕ# I4iWρe`? 2NDdZ%ֹJ,*7մnnsT-e!}4/Nl\ 1UyH,&A994C&2m qeRPƺ V*U0O?],hW1O`\(4 [!{S鶝Ogd肓`#fN[ßb`qcsz>T"֐O.Nmt@uƆ G=ASgn8uZΚsSq/U)HlӅUvgCJ L9˘x53D?0 j<=9տf.k@"1;{%K%k A!%fjFZXgUtl\RJg!j;Pda  b:3Y/:ٰis9Lɣ`Le/uX ˳SL0R:@N0bU0S)><јtf6Wl@\^N݌\1$!b풦B<0o/aS;:24xdi!wJ $Cpz&4;Lqdͽ|PaTخ.8n[|(fДd -ds)6TZ-fp'~0OJy0y$@E:  ڙ!rRZYϬ'+畔fW0r]GaI՗ .^t7bN#d٫vtv#ydS^V8?w_,79L^4,34,Eujk"-(~PE+͖V17fDex9r%r(Gň>~9FSeR8cx˹. CZmosŠu/|L=pDrʖ=fVXPm06zzm y9GCTEIq>MӎYbg˽/8E(Fc? &8s@X _pyȶE0Cƙi>%,2>>{ORI2ˍd缧,Q. i[z %5>Geft%Tk)eu%{_Q蛍_T+[|R*{}ruzgjwQS E]~ D25dFE{l2,Ĥ3JPgȭ|;+8FsrxTV};^n} L"K*DK2ho[Vpy+7 o冷rÿD\.퍴좒6߁P BF G-f. 3*0i,~AMg f P`4F?=1kKbb.#Kͩh OFmKW6qGU6gc**K[WՋj1k1@Db*eLPPN{Sͽ^g.qZNك{ 1j@j``d"H_&YE wB#Ё͞cdH.#:CtgFِM|,2z3wovʔӸeۻNo$'VVS0!MbiBH#Q2@w^皉lx#a z aiA 7mhO=AVMHK!%nV^kv96]w`oԛzJܷW=-^>hֱom~k؅&9qVVc.ndI|$jmt|b } I1&XSyjy/'QE36abfkZ+mpNUa+; *_́fxK J_WLTMnOluŦTbS^ch5\k!KF1/G<*بx>]|\0J}<҇%¥%¥>Yx~Buh{J\9Ci)1AB7qV5$:FtTn FoK2H!ʎTV>J6vZh[d +1a7,sxi~Xä-RcB2cXIr&;1 \Z.y)k7bxPKХUsnmptt_split_017.htmlUo6}HԒ,m.YNۊ0h$I,{ [:޽{H߮Vhjp~wUPvyYtFҹ$i&ncm6Yhx/q@Kt [ *PaN2*7>H@ Az7˸%ǐ{5MjWRoO_BJz _P}߄⺱0MS |/St6c~݁M'!5?J~֎uZ]I7%~o +a_ ȺD["alK3k#( *X󐶯Ov$䅌I0yNWWիhطhUzѰ@qajf6ps u$ D:&j7x} *%B"HaXmrX y 2:Ck4t$vX|p4iӮPh)QpIn]jD4j=hX[{s'9lA \a[%[!x[) -ol~:/hom(!31]TAlڇx{UG g)0\;y"I^Lu 9; Ά#,)"=9~2-4H )T@c#aͣru,hYϧy˦5kS1CW#-N<ǔl0awF ٿTkc%ML42 ANА=8ljt  ,Pr/T:QT~@zd Y#4e JSх ].pQf0c',fbD2JJ\b8Fa.φY%/ehB$n&rOBG}yw0s" 1s5!!W^B:*=ƉʌH_t^ >t "a"$ʒc20xgcfyτZ͂HM][W$AiVX2YYᱚY}{8"onPM;KdH%Xڨ-&4uXZTY|z|ƛRSiP H)eL?T*iX|b=jÍȻ%rI0+mo3T=!x!,sOLdkbfzD9G[-`D(|Ob9vh6ZFX6ջSne/uK `R -AŪaTE?pss{H`Io֌/S%1L?`ϾLyB<鍖enP)Ǚbἓ\#/drmk`㧭Q*( fŅ+޲W I}AV£Ü!' H5*Ts4$]wW<Tb S8BvV(wML{.v֡(Fx\QpG]Qqfu`uFf@s_x`,=4~^|x>:ޜu ;V ޻e@,}s`׋!<|a狱K?PKХUj _Usnmptt_split_049.htmlXmo6_q0()b9IqlwN9J-Z-(ɲY[lC1K"^x/yn9,h-> OL[VgWvh܆[jUF\.s[E쩶+̀|gH5ABڪΩ12'Bo`&c0kDr I^VYhpzWI<73ItVv?|΄/ FZ:#0Zy$J{ޤ'^4819`JMjK̗h) %xխ4&g3ĔJ8U u䢞RUb:+ |xDͶuRS8QX lӪ)*0X}qQm7z[s kY+K4EkoU'UH=pvf8ibbVTMČL1DR"`0czd}a=#J}?e.FyeCҥe$O^" ҵB.E(.+G21Q#cwFR #N1(<8)h[?Ӻ۽4Limn*\QtwmEKMS>Pnj,6X3ugo英+,"'2^*~#srx7:-Pi?U_}l5&+Dy޷y!Cyp*:PЇE@9U21Mp $Wuӷ{zsW|D0 ŸdsԹWLe=UH{j0]6(:U>i4z0kz7|z׽33pX鹬-.Q\8)quV(7\_/ڕ?PKХU& ހ"snmptt_split_075.htmlRn0}W\и-+[Y ւFT9mb8vt_4QHsm_ssJR7ʤL̂qmvU8sclɍP+T-KڸY [hHV`@2) ȇh}O+٢n^,ޔSa%vDuȅ`;;iװ,  {כUv5 l֟!Rr]Ч { C@uZ&ѡ“0)&h }P(A c1G w[tbnkXǤ,ʹ3\mt-,Vo em"j^;V& 맡ϵzI3 ^WJ5 h&fa/ tK3}y}iSyp40NWQDAL'tn \L˿d̹$x@z^{CAA8½oUhW_@i'ESFBtEc^Q$D7$wgкM=60W4< m :B_PKХU -L snmptt_split_039.htmlV[o6~ϯ8Pdb)nIm^nH\- Z:ФJRv{)\s\F1M"u?.ਗy#QA}~E2\ƦQ2ZL08e(^r^s)j@lG_㛏0f]R\Ai0T? "٭ 7W+R'f`4&Ooou"?ߍǟn[^zg[vEنNZr+,:Ǎ\x\LX_rSR߃E_+tZ5v.lS"]2pz.RTb~75TX G %nf7_|:9 =ш lW LuCSlEb#PjIл/߂2 ) ̚"Li)$&.+.!t HOCz=筡cgg`P%3T|7 DEI`f2KvH1qơP&04W)( io!zƹI>*OQ7ۍRV~OFRylP=+_ڝYh˒\鮫n8jRp4*)][;[coJs'o^f 4jV=#Ӌ#9o3 y(|.tD{v;d`:LC _8&.WONB~u[Gش#"mA  3 °Թ?Cpe=f! +E|OIpA]TA Rb^tˊ'XT>G !aG(  4&e6䱕ymV!7>D]I?,iަ9>79:Gvc$Bh(י)n]e4c|:3z3f)# j'xBP|N¿6TMO!mnѿPKХU}m snmptt_split_023.htmlVQo6~ϯ8%했'M+Hb+xCaEI"BI~xB7[bI}rnYKGVa<8T΅*'Ǎ/N_KS*7*Exk[%[(=H*y=kD%*kAG'/O0 m!<ۻOB5M]0Av!W%nGtX )9;/^T_Bz`:#[%|w7u釛ٯ7[\/7Ynr24l$t2ǥgAķU:v:D u$r~%U>O̹*nDV Y醗xۅmfS$w䅌K1zD}5ѫ(MZ(Jx <"xψҙƔ5 O`pnd&a0샜E4(A#yxԇS~[%,W 5w.1k<Ӌ YU~\Bunz5ue0)4E+zơeQ>4zNkh1V+G(n ϡx){s?m׀|pY3cP{rVa}U(`n#:}}3"Tf;! ZOCӟ}hg@(N,"[ Ng;2ڟ] 4' ,>J( '4MDeNa:=y 7k=ff7}8Nb<ޤ;*2Y$dpV~X:]ǏPmo7k a\ȭ*Ls>#=PKХU<{[psnmptt_split_004.htmlSN0#_$% jE4U!'M,;;lV[zI3庑 ,NUKdҹUp>L&vE}߇IME5/h٠x PNVKؗ2QJfu#TyxNYOcxդw%iP=J[Xf4GDg Hzy}}:$ߖw_0=}ߴv%aZŽ^8\7. `P`P&̺WFt qlS kWG[^GP\Ӹ -yRMp@S?4#1nRlM("Gwm&z;4rjQhB?,+Yz JU5‚zjžL%,:1C΋瞛؛;K8gj@i&&Dzȫ/~b+z!$alt?OXfG>]gߩnh{B3N~PKХU6W(titlepage.xhtmluR]o0|_Z'I*KHU9`NQ{mHמ7{riZ'\ټ--zϯa*0ŠK1?ǭ+El0@i=n~ebeC!Dwoe=:BȗJ!"n!կ=bV70׵yqе ״{ L,CMA3Ja8Ss8E/JcSw~iAЏҦ{p/bL.KYzLa[Y?䝔Rx.t[FyƮz` 9A1߭]x mʶvah\`2H]϶Y̎O8dmY@?6F H9Y0Cta& ]h$OJwB^3`2ZjLU!+%, ϐ QU k@}-P |;el_k˫wZ`Aan 9%E]ӭ:-$(Td(Y"[>KFԯ3n]hKX(~K}F+k}.yljb9P#W4 Q%7+Ond3'{Fm9dMC gTOZ/) PKХU@7 snmptt_split_028.htmlVRH}+H @ Hk$)F3ȗHq9K^\==og jÕv.LTe6حd< sKj*˭-O`:Ӟtt~0s:^R'Xe Y/CY=H({x{Ca4Q-pY͖q췽`rp!pΐpG. ' wTM S ݇owhMJb~k=~fFAsߑv?*>xX׸8# ivP'q .Axz` caAqữ.e8jV2a(]`j!a=W"ݍx:x=XdzѤ*PA.^4TPf)X[0W\U"= ؄8jZ6>{=%χ"#U4)T7(xfd]N:`se; Jap ZD [?ߘ?R"9~/H]ق+urg_|pI~vs +NӤoZi]u\zM{ \SJX4 tS%`Q"GB1sT؟2Jo]~,jhX\ ^ȅ#o YLJE˃0- Ta?L.)cC.ժ(txp:&k6Pm 6U[,b/jlx!6x6V6A2c+I&J vtZJ#0!mH(Z*%ks.0BG+SBA0MGyv=g X8<ub}Yyak:XyZ셅=dz rgBjk޷c;*6gerLëB*^}M \X (dYe )܏o,/kmQu)-_.:`o0236a*RaŶP*wf?PKХUTݹsnmptt_split_021.htmlR0+F$'lUA+@vatXN'۔nhUU\X8 ^XYtPZ2M T۔UD{w]{Ԧt:ǰlT(>pI[LY0(jM)e=rMmƇ^;*?<1v5%:^hRT׿R '^z],,r@hHnq8O$Iam1 ?U`3}?jK>}g !@Q czDI Y+"1 񷴰Aeps|Wy#J\S=?.5Yn]B(2/>ݖjG,K,Z8fYD0YchB0Nx_b'Ӫ1;^싖$QXLGMxJ7lPKХUʈsnmptt_split_038.htmlXn7}WLqaM+p 7-8  v)Ȋ.^_3$bǗh^l̙P7뒮dm';IBN/w> Ir:βmYjevիl2 d%Eq- -$YJ-kLPnڝ$?J3UliajoGӯJ7/:JI6Vk8-~hꥄ`q+X][ȾM_?~P0,|Gwsl.*gE)W7ѬѶ'Yg_:I~ ϶ae wDm; 'ҟIbݶv%KAG<[ЪDʫPX ֟ϺNKa\j^˄)rxc0!U.峗t":*PoOQ;75(`fPiFa‹$SKr+ZȚkE-1+Ӕ%ՍqT<Zie-UR\Fk6ui_sh4dYzhQv%mƲƅ<&jKgHBtqw؞djGjA[Ӑuv]P!ʚSPוs;#Tp@V&릟%p4\,LTg !F_6 Q䌷z 1rx38m.TȅҒ7,!(Ia˻Sp{{^{#Q:Mn#5RsҢxf.[Sڽ$=ξH #@z-ך.\KkLfHBըM o?ġЮV7JubW)8o|zTAj-5[+nv% ߳0̏?XޣV-1Åŝ2S7fMy:1cÙs#_a,ύDе[$Z^!2b}4>7uaog"ʫYxcT<_1rQ:,'_VE<ȥ'A_"2&|FxN"HT~I ]CÈt{wuyxYyp]{z0[?9ɠ9&qc_){pmxÕThߵq= ?\Ɔ*Eqc\Q"?w?uq+,cA4D]ď٤§xDlv+Զ#.7 KĬShapo9Fw\I/ݠ5u,{*5\~sbˏߥˏ77WrӐؙ[U}(z1'{m~ȣazU}4aeL䍣uHq)޶iAh@|r&Uyϼcr1O8Cұ%6艆x񬶝V (w~fydk#V>4 -_ؓ 1̎1iPe 9L@@q6kL^0)}IbdSPjq1& KHvh=zG>W#PĈ NeI]!:Lb鵵 Mpxh,[.CJŘ݈ Co$={bCƞK᲼ =. J!pTo|GpY־Uߘp .eٝgi[1U)\g8YT~D|BrԳS Z}?sȭuZ/6Rf.J.d(g,I(H̑^h.`a5/"Z_M}@K sP.,T3֟DB2.GY[`YWiB'EA/hv[_ܰefAxPI04[36-JО} p]K? h65O5xJS,[(V"72?{GTE2DPfUZ(a7wz6E{׷DggAK,uxIt_1ՓO 0bp5U 6lU)46sv rj}J%CnkUi*ti-٠h,#Ѹzxw֯ ,$rk淿 e7OT6=oFՏ>d:Fd 5ۯB2[!@@kc[XN+x’1YmU>g*M(X{B])E`?^$˰GYRriC.;i l- sܗ-%$ YiJvݠ=@(fFF*ȗq#a-& ٸ̼qM5 TL[JU%VOUv{m*$ -"E WӸUҎw \^C#ZKy~"cpJyF`)pf/2>,6rw42Q'R<=s\K#hi$xzo*H#Ϳ)CCmN#GV"qdk}H}{kV`,Vn~z^xp\VMÆ/=}?!yq\&@0!"{+ Y_D0jpu>/:0E_*^`dCE&p +q_?EK 9*Bװn]ȧ6`㴪Q2C#1qVv'+a Prc!rhoh2>m7]QE3(F˧ƍ‰xxwyLyyq+xLQD8nȋh->d IbĒy@}AiB$ `Rrɴ?^NZmldS9K( yNk; S(e*H#\ a6D'g0 &clz~SVH3sӬ\\''6˚ىŘ]QK,-Ix ZӲ(yCƁpb8imK-d 8+)fWCDh2~aCnk*U@j{a !pmœq1u߇'vL,҂؍P90:/JhY#(H"Q^%LgɷC~C#2 |&iI+iӜAKxq(- |s&e"oGW0:* S.|> >dRVbH63eQ,-n& -ݺ">.scUK{?EUj( Sg&n҂: !\nZSFxR 3&(Wei&~nk9p)\ ,E(;3"LbJMMĎ?YJ)ϐL8[~S%AQ33Եe}>aD#قhUJNOn:aiSPYJ86)RHcpPrrʔ>)K?i1-GRSRqï: '8FJ{qW.0Lj:aA՚㫄S[aT7 ƚbcB?A+&l y@w k 7U{5<.}Ky|Tŧ`:b7i2]ꚵ\W# ޕ\3,\4<:ꙛ[Xp9EO n:ntı5b )|){=]Mt I1d(x7q PKХUr{p snmptt_split_009.htmlVmoH_1gD" i6RC\ujhuw1ܯ54lygfg{;pkkN4M%M،MGQ:jU-q0M4xP3$FͰMѠSd]5чGt@텧ڔ/kj1(9n[:*)(?kpđof | }6<>]^qISOË+9U\GE&~/}?:4GiA8XD|IG{6w0G9 " X&G91v}ۋxޮ C{1:QvpEEx+:Va_Uz)ֶ "a"迦10-;\T֥>裒Z|KQbJkX)RP8mE“r1m޲"`6}hScdw|xɣ)NTv LƬP qM<1z^S5,yv8Sw(lQy#[:zjT.)nB!K0$/}L&<y`-$DƏT\he+ZӐЬۂj:J-@m%lP Sì9~ ϸ]8%IH\&aK*J ypc91nOǒJ 'IZ9K2Be'/./§j::;?9_5Vi]eZ~r M\ rSQe:Yg仁/PKХU#G:5snmptt_split_020.htmlRn0 }Wz,6].M6Pc3PEr%*} `Es)$jÕL jB\ViҬs޾ZIMҤ&jޅr 'UcXHjKp瘲 %꜔fP(I()er 3zK,Xx(W\{+tBK{PBiP>«\ji`4v  #$S >D?^o㯷{]$0 X?I};g)<B? (\tmDpE x05:7y-lwKMʭq^(rH|26i8bYbH+YaeQCXnD(N->s ĮG|({ƲFPV34(K$;tcВÌ&y~,N88TJ?ZYYC+'༑~PKХUiݽ>Hsnmptt_split_003.htmlSMo0 Wd;j6݊fCa(dɒ'M_?)E>>OٲVI8RɨlW|v /hq[d2æ;q7k$ZԘ5ZA2ȍ&ԔRY`n,ں](z1 A8W֖qB-UTJW_.,Nk:Crh~]ޝ6$I#V4ᣝ Ƽ۪?Zwt,O!uHz]XI,9Z)t"1 ѧ1,η#/JoD]&K3ו+rdf8 d9=4bj <xoZDn9<<)֛IS!C(,`nM T!x2]a;kڙXbxc7q놈ϥp`tLCwopXAsh'H=ZcQZcZt *SpEkֿ؞c Tm^^Ju&(3"g߶֨4{k@˄e4iU PNfL6iR-=n{[ Yb\a *fr  0]1 !Rn!ܸ ǻ<#9nTK$QXX~?i=Mϸ{hb:|uԣ]Zϊ؁8cJp r.yf0Wc=%u`K|={ Is 1Bc-;yZ,^}STq6NbcXJ'ib23l4fل  >ѽ.~/5ղT1 4Uw>y-ϒ^\k(\*Z L͆)NT$\AH<a"R$dJLӖä\N"#snmptt_split_063.htmlSKS0Wf(Md{5(d뻊4C/joWH. h*.*t)v}EQ_BJNN>ԍqN#<ѻlHƣn녗ݏo~@n r2|0[NP5eWm.N-<.}xEŭCLP`Q7B i Ǜ&3M*$w4 bf) D}Mlz|Ȳo`fm=fy]ݎF_FWl%Rf oX8oY(ck&IlgxpX״tU% P:,y #̤._jqyg쾾ߧO&'?@g5wuHP Zp1 @(qJ2E$N TD'Ջ:L0Y7PKХU%[snmptt_split_041.htmlXkoF_q_hMۤ 1't0F409rp(MPsc<嶮[998–,O:tdq G;V7o|^ߍm̏_~ʙlhbU>k$[VyfTX{rC ےzk&Q$V|Kv!7*: Z?WWʿ/z6];:Ayῒ.Ng/^>ѬUc\%N򸷣_6;}sm6 %?SRc '6ru9ح}F2sZ^*|F-* R\(T-gB3몙_}M'*kMi`Kv#}lB4~SS%i2bClFQ҆C+&gr `vURHWʔr^yXro&y#_w!#ö%/TWy;nM,q"nC"}k'\\R/=a(iɾF d 70p]~zHGHojc\m `(JH*P:ULzC6mz-X޲dWV{v;NdžwǑZGkEl5€ITF!Ҳهz6{6pHH8!b)p 8_rđS=nQqwa=Xq"=GQ|sKwH|E'Ȟ췝اb"g^񋽚-.UqM]#9/J|U"g*Rd:d{KvLMۧj9>;KbtE ={kWQu-61&p qDMo- .Ʀwkkﵘocr{9%쓗 kLn n?l$ݣ/e6te,;{>Kp5m}L%Fce(Rf;r̸ՙkcצ?"#DTpddx7c-8j J6t$T0$'I_a:D짫ummJ"nn6n$!e)8`wlDfk#O1Q-vMb\74iE^/hc;Є ~P("U#'Uxⲓ*>30@zvǕP(v /c')s1&!}}I_FꕿLPKХU`H snmptt_split_022.htmlVr6}W&,vcbi3ⱔC&10K RV#uԾH,ΞݳX0y(:itja2ONOܓj׏rc<[(=HrYа@A5Z@Q~dfb .,bm µ| qt]췿5H8;| _QWK_>op FG;LXv+9G?5\NG[,$[_ 8Lԏ`Q# ]#:* 8i[a^tS|h6ZdlH.Z\ɱ(Hgg6rFifjVW^nF+/Ptt3%Vົ!C0q]INkϻXґ0TrS 8jv/K~o~Cs-n#(ao"t cz҃  HM+܃anx *:ShOca 8 9o btlN41~;3)pj lo cUf,z ޶ĥzI;v:DN+zcDYOх/!Cd<̖,Pޒ5Ǽ &Feh; +"ԇQ~FmDUASh=Ru]R,d9|Xö /83f=0]^ևD%ZIPnIځ-YH'Y[c>8 σ!h3A&65~VJs74}wsג␻6^kFpIM_OџPKХU3snmptt_split_013.htmlTo0~_q@Zc -*c$Z` 0M_ ?9'm L%9ɫUa+kǧ1ɬT&4p$nC:VAtj jA8%dpi4%L2D}H؄fGP8# +9N[.>65YnWYȄV3Q4%wi5Eē&?Ѱ׽Hvwu`bX'vq,Zj}x5%8>km%zχ K`g 𦬈*%AO\sHoF4J~ |4mxܱ&V7eP`+ wA ,֊*+ g-1 cr st<^ 819Em9p/F6<ژW[6(D^9 SKU t|LV8w Ȭslu[i\!.qj|8y(|Jhp"FI}Pb%btƓru3 ף`YrRsz%H4 e&no{\n~GPKХU ^snmptt_split_050.htmlXmo6_qb+Iql 9ކ+Zd4(Y/%wI{sXP!Pˀw]ѝCQ{̘岽|Ֆ*rO/..g+%}95ӞQA1R9Ka0=~ <}23mJs`!0"bǩ* w&JQKXxl+'k@^2MA64sq9AIi :%i =YpWbl̪e ۵s ;& 11XU ʣmC2:"dN .sv~~tthpkCx$\ԹfEK(џSbƗgQuAr2*is3  rIw:n\U2(x1' dJJCk·*bX,t`L:yW<Ԋ"m{i-ێ$3EF~s|I8*O)d G_Orɳ fgʄl*EV ; mk(b&^ m+'ꋼaȄzmA nt"m׵fe ":ɿhiJ$B!x{ CeFBG@iȠ>Է#]d^odXV}|O 3~]ipƮeB(HUU! ')cjjBY+n;ޔ_FgmVmK$M.*LdlCNɲl sUZjs/Gnf8y)_}s_ ֒#0)x$b|(CM.w:xKtںd9mzɲcWϖ&f\-XUQ:דJ2LΓ醙DLdKY<ݑ޲qM:nw!S(w[<ܲXIuV=dtƂ2:̂;lP%_aa&z&ցnlیP EdIg߇ ׈<rn(pk؄NQ-4qL i^lPOQ_*M@=%fR'i&}lOFy obS. ju_`"|eBmE_b+P]8yu=pbdXU^sopEڼM^ͪP^wDԖztπCn?[S`I Ɍp c;1ju+8.CE.T)9Lrj^JOs(mRlnJIٶِ57}vy:Mn&Lg3P X=jls]z:# pb\-wa'(P$r8hW}>r}c W_PKХU IvXsnmptt_split_031.htmlT[o0~߯8iKvnIƆ&@bu&Tib͵- ^;|* ZhPJ?9<߿ʥ\l6fH*x0,Hje0ȜSVB˽ <*'ENZP| N۔ksilĻ tӿ<>x]R rtt _+kuyKw_ ܄r2$[Cg#Rn0_ܪǹC/uӶˀ,ʔ9jDʵpAmq)nj2ԮBrGPp)Ya(7_#3:,KZU;neOG)x >K%,]"7xSY^"L¶,t֍,/Ǭ-)ˆh$/^cm|*~yv}yv _ֳyBa BCB`xH]Ib) ?qjj2&B2i_u&9HƂ w";l* Ѕ>j侱G)?KH/\9:ߪ-pYt?oGyDS ]0F U *0n Vau8RWՍќtz\R%ZYɅXX4ag*X l7PKХU&F"c snmptt_split_065.htmlVmo7ίH6B JhxUUEP]e-b{勵3_*E={^?xֵ%:Mi2W6,={63 yjz0*?>99lM+_ 't"XAiM@&ׯ`z^)Ӯ{\phtÐk뮐Dm(?Jk<1|@RFڕٜ2=? /E?=:~7qz>{N4xioI[d%,\\I| e%0H ZOPO26}2s-ϠrZx'F\e2}QDžrZxB)*f,/3Pr).=̦cћ5tWoߺgstFSxȁ1h? T P!| BJ .ĥ*<}\b/دqu=A- u:l¿D (C+4DQ]L8Q-惿L}^"EF9Yz^zMhDq')(~ l!%f^N]氢NctPcͭ0~:ݥ_ vٛ;ZBEl%6 {RQ8'3kp]bɢL un*p)d@H^2"POxEsH.z sڣ9oKe[Od[l 8 )ܮ†jWs{%,QGAONQb(`*IV :z|"o~HlkՅTwj(뙭3Nf/u ܌ ΖFT#L(L{lʙ5 w1RS4ڴI(+e}k :L@(Xp.vݨrF[4ZT/w $߃mtR:e ]4qm˖nuaeݧ9,< !hfqvArӏ>"KӊkIW5oOWn~TȢ Am$[D̓GaŶ$R#,oiU WvVtl5"}οPKХUቬ"]snmptt_split_040.htmlRn0 +]l%]6"ۊФEaɒ'u'Q첋LQ=nwWԆ+Ol (3sY6Du0&&5]ׅ݇Py I&Q, k $1&%J*M SҢ1! .= .1O.BVNÅ` @{B8f4cD賎V⦇ 4hܸ2ۅ H$x>6vs>#INN!:rT&PEr;(KyP ys4oàԉI"V`;'nXw2$r!L50os様LPKХUpsnmptt_split_015.htmlWms6 _iK8Y:|׼qz-WTI/(QIw/Dx@@~L:iFcH=:vkU+$FGQ1NݺF1c H^h1hFQdCcYԷF‰vd:}J(.aJ/ l~:1S}B~ %z Fw.;UoiK}syq }+2N[bA\N—Za%gA| q*CP_:+t)j\aUΫd31»Bl(JW#.BɁň)hL_wஹ[[Gdwvv}(v۰Z' >]ʯ߂{FC^bhM~'g@$WL߁TCfqBsb A0Pzc,%a g,?MQCxv>u`[ %Wxy$ \EJFgm+#rU&1! %%)71\ug>dǤ-R8?xH];?=?:]_?|8{GgJ)aRsL( LRwq'P*tkp/;74$4h)+{$A8:Nܮo7vv_oqIЬBO.SzY-?ٙqS q.n ץwU8B@wuJ+xP?))=Z3!Dvd9pxGy:R$\fd?OЄjt<:fdŘІj*3[m(~:̢B=&z4J<ڱ,VAjCTi`]dJW :Q~h_sd. уBKr)fpODx1n-aMMl^e$ .Ue#$ -<,] 2'/Qe6E7lEΊI_9􋧵%㇆;v`DIOTgu{u ,M!bUTCMG.2+ra6NՍL8C̊5JU(ֺ7PjWSG4R]iB3CPgֆR/2]?eg4Duw\:^[I},,̯ap踄ueQ\yMǪ>u4AyXx1v89F]9Vr'uԐ%e=E1…<=;Rw;]?PKХU^|hB snmptt_split_070.htmlTMo0 WZ;֮ lhPb3PYr%:$CbYz||z$Z h4zNM&u>4?|1 r.߅Qo0D+ FGq" ^H(q 26hBM{2Dfk YJ]<AOcVϵ͑yD5mJ}Q?o3t0M8S#<)KEO[o:q%I G$VTO;/G^~or1 ZΒu{^(&~!  c% XTZ+)bMh\>"L_mQrJ8T(9l=Xla6lwՆVq/8fho|A~I?=ЙPF#&C0ZsϜa`0-ϻ0) /f"}gQn l4 SOMso.t;1fc$^pO=(]32͵fuXLQ.0;L`s8 S!P;ZFyƽ} whgܢ VU HmIr K:o)Kʉ8/PKХUu snmptt_split_068.htmlTN0}+FZ@ri b$hQɲ!76:'8iKAT=`JG<@pu=z,q+X0@( 5:ayvpٿuaCǐц֚@(UŏE!ŀṷœWuu{خh*Vc!wfsE x*傒 `O` 놟_}~\{X+lQ [Bp!]b` la2JCE9CNNZ4~k$$8[sK-{tqe;VM[_y `uk3i#$찌MBX~ o)G5u.b8"YJWb.l?ڊ/ ɕ* Pri1`MkQ-4۰_}ۙWQcƴɑ#V 5YYQRHLw! 4lC!cX=C*^ K%jASEHLQc$7e)tD0r92<-[CV&".+OQ4e3rUSsL kQV 8rX%ه9ZøK{Y0ݗ>ʍROHM2fvigZcL-bi(vvxoVҔ, 3k>Ut3%H[Ά M4Cr NÀ`GuO2I^ҁi(|A G#jk!!05ٚ%Q.rٹ%{B*x={^"I b2!Ʈ&egESnIƣ`-n|L{a%g_22E;3AB9qNg2} )56ӝIA}/w@Ș:?vfr)|$).> zp˶U1,A/΄Zh++',|heV Tug }Ngz7PKХUo'x snmptt_split_030.htmlVmo6_qA)4Ix؀0h,H,{GI~f/6E>ܑ0Ccnx (rwK7?۽Ĺ#32QTUUXBe{~~ͽM0sdа@@A s(P~#x2࿎G.'Am\y8wɐ6JSo?\gx',܎(oلitax;ݯ:no>0qԬm%, .]I$gƢ hƂG0(u 6Gt8nM 78ݴ,9e8nVeR㉢t%YB D&t5֓q %-`0;O)8jm`0RP̰ajT;P%\"}G<((iٚ> K,sd)U$IW TrR%S^`@ n#- ֋SN/cR:6P)^{{q$@%I4;k_ݞbYޓ8P&zHXVɯwH!{NCfrA+ Zgcʳzo"TS2#I"=Z6@xZKwDJ[8*ӗ+Vke+R%R<ˠPvC=-Ad,U>Kb{?|hNTS@@^dΨyii|h9T*W]W.˲R?'V˫W JIv}Z͕u-P&H̐V|3N=CQ./gGo[G c@h:MSdqk?0K7+)P@ʨ2%huEHeAmh!Sq5qޤeVeF99[4Mvu/vcO}T~k Yu*jbepFW*8cCK JFwDz(*4)1k2SukcD%h̨Rr=: }AE?:{nrpuka& lO)?g7 PKХUY2snmptt_split_016.htmlWn8}W lc;q6i.rn$>E@IcE$~Pxy["s=3} &h4ԉIJ?j0xXUF_OϏ?1QHY\a*}҃nu!1xQ Gh gҳEp%iKPX#tpUdހϐbBx-H2 2%bCE{iT: tdLHjڕ O7z#pš 0wwG<>a}tNneHa^HcNӚ$XU+!_C'8v!^px &Z}}).ۃhpXܖm}S31EDMH%xвTEy@htnT*Z%S]^$oA(!u 0̤ U ٬=&{ZCZ"k]+VSS*}&EK5_ #L*1̙,9^P8KкtZUl/-t'~2e r`QRP;~Mn}9̡;ka:i]:|UH튣ʹ׿/p|Zp},4=C.MMI9756#5KCV n@"$@ mF#u jSڍw̬zbD߁5FNMҦ8ފђL3jvo:3QhyNL 0xVEOHM3\ NH4cgKl͓bNVe*&[uy ayȾ S Dv'!?h:R KTLfdTJSET.KC{ )}}w63{}Z{m a@ !!/ع{ qI[9)Ňx?(()(/RP]JOw = =#333-*3E!$d<)(.31>K*HHHH=@BAsmt@JBFFJNva5n0>*{O{2 K̹w`/ejV6vB7ed?xe {G'go_W޾ !2cRrJjڧyE%_W746uu~OLNM oln'ӳ Hd$uob EJNNFNu 髋 )ӧzxN%9U$ ϼFY7Pcoȍգt?6 cWE-27r57G][Ցaܖ5HOҙFi۵#U[LcU)KcC-djs[&=JG_ GBr?ӎ1 qxFt@_ɒ4J3fnlyAn8*S ٰJU)Uɑ$J(7\\D`N.0k'!DUh,c[ŹBC{ COyE@6*iqY "o8] ä%.-ɗn+-MUFޭ > 9ԜP4+Qvr;n'lQ(r:id:ԧ{{Cu  ; K?vDNw|ʟSXջ)7G2Ц_zrחhJ$50%uEՍd5U_Vɡ/̅J KAS׼3?fCTa BYدI72]xOx|mP9ekx1P2^(}{ EsEAȧ]%jhysI]C{/a3% 6?q:higa\W軐{88s<:rq6.д>EN_=Xz]iѲ\ǣ T\0WSyl(ǃ+ |؉;uynr6+JI4 uVP1 l٤Q;/~; \s;n05;Ωm/J}2H žq: 5)[Ž:5[B#cϠD!#VpW{:CwzT阕5'qAԏƢ?*!,xͥE慤K'Ons+i1Es }4$- G]NTDO;C{{PS]g~o'&!Ҙ2@kҎ RPp?aTb  .\n!Eq.'K=i)|.{*B>30=xP߹acUi۳ _T%3WmYY MKv&=eTuIMrf}ceZKo?(@*X5BWkR09b={enqL^91KNPOf>&u9CGi6i~x}.nt29rL>3?m}gHaRj#|#t{;$-\j5y֥{9^{Y:^>VHJN9gZbV+|gf[X1F[WuL8:gƥ/RT{t"VBrvm< xVpM[{l22Y+`|v|qᅯr o+7^h>K[ y0$q{hrSGGh}iH̱)㾤lbS*$eHqn(0S2`|Sa/c 2?"I-Ja2_Ze80I/O gX'smhGgJ~%6TtęFўr*Jp\m4,h,bͺuVM1zF RU"R˱3̇KV \Sّv̓D h75,u4JxD?~_Fg x2z"$HJ>"xС0vu򳲁X p^#jKr$5rqkԁ6w,u d҇ .L*c< f;c=ָ?<4nxV+M Eb?±v]MTqϮI|| 42=ON9 '>noZ\80)AopaJm[.=mDao&VFg ?;&^./@NVt:Y%nZm.57xڌ8,|?8iS"^ʅҹ7e #ojVħd#[+}V#UܾЅJ0 u {Hρ!KC϶3n唵: JJ̸x0fh߆zQΆKvet͂zv)gWqF#D}Wvl1+wyǕ`aUҐѦLsbqpCց j}. rtG*Jg<wFgW+t`gC,r<.yr2knῷ5&s,kRQ.+}[ \f #jlCj[fب^0 _vِnAe"N u5!(P&EdUg-/(_-@ÈT_AOGݺ]LN#_&G2OտX)8ϕ`廴F^D.}̞=gyPmD ?RMl|9ͷZ*[RAtIIM#!GȱֆC%XݱR~%J|wXNpn dVքou,xY`IĴ8bɚ٫  F#ĜBW\bj=SK^YNֿRu=7/3֒y< LVтUK&7H"&޲!c_,J$ҧ+ʵ7^*a/xs$ע B /9a)81Ż OIԳI\$0(x ̜g 9Wv˰k3NS$?e+n 8(9RmT)r/=%v 8hV9,V4t1@RUf>sg wWg'wm7 En6]6$Hwǃh^>/7;u)qљIAG iNӮƯpwgcϧԄ'>T*`3~OQx[VtRto|}T:5ܫUk1HpN8?d1h@6z佂P9%C8zn* onp@bJS'K 9z-T"(i^ 75':g8^75( /hj }2ԖD `W&7]ܖ+  l~ByV|+f '1WLj X\0 . VCG70 1 1sڷg7!n7џ1g\\{r\ e[?ǾTwx *qDb'\=Τ"IEZ$bE7<󈎅s-ؾԃ HV{v0_R=H Y3]aW)IZa}* 5nsMso{ӔFӀ֌3~E\Kϩ7n0?Ê[+tF OYٵ xOݫw>o j2PsiWyQIQN5W>znG)&nj*\m OaJJp.m!ܘz<qM] w@WaG 5uq^|fkrhW4V_BDIUZKTiοBLlbVp JY8Y;y#He>  /V2!Ovh3즎>d qk.cGPOK-D|ޫ!,&g^`*ȣ$J_úѿt3L5DE1Q 1^,Z Dt'i`zF BDAK\rh\KDe| }}iŹn ۃ|֝pV|Wj2$ƙJ/v7.j8L]0!d5j{{@]^# =D~R'W*V9&R:>fQdz]>{Y+T`\YTK7KBhR ڄ.֥FT/C٠fWF\UN_zЂ'RevuH,PCáv~%oo?2~]K:}ֵ8KwԵPz o@',nm/@+~FtzBw;Hi>2k+]tz]$T 4DFb Bw< 2L$W14#~nliҏ>G2k'ѢYpTR1ugIZdmNn3Y0&锬ۚYWRbZ-{u%DpvB!omKp!Ǔl"c#extAcnn.C]$caQBlT<4mQpw2C1΄LKxx _!dV4@=dQLC2 XeNBhJ`PϩX|.(+:bvp8ii)mDE;cS@\6s0:p!!{Ѭ͔ gӌf]SW`h{API攰/{]EOg$I黊JHPOSG1_nwpX,Lu,6kxH; ml]\m'ۧni^mБ|ksC5"\+h.0J|٢ł%t_F?3֛%8\m僻-ʸ6qC7=nbl;cS\ф tTP"u:C t<5ePRyG%]b`x| ;`䛍) i=!F@DW@']4c.-!X2#dF^uV{ewzI3+w'i?7 _:3\rm,!"(`XBr[OB9gY\`UizıX*]!F8ߧ?c"Zn7V52 xG1 2 1fPChטnB!)NM>ӺAD楇#9Q,9)AoԒ3}+~hV8ZY@<^TW(+(?X1Uwh!d> ҉tiA +"?]AY0c"i$}0t1N )kMFOM_57nAUmp?w\ǭ}q)ag(Ri~ƁY="A:;ѿd&O*|r.85g7Qo@ ?$RV |=VffxQ5H7O<F]Dwkq?u:@;]ƃeKnSD;;cGEO9a ~zaB^LPi?뫐ȽsQ41jrv^,DŽg?;_ޟÓŗjV/#i 5h!4vmDH:>Xǣ D !T0He2H?z{]jnrc) Ҷ{Ǧz`8b?uL׃J|. Ë̸́q3^aŤi>B,TDv:~ \"H q_7`8Xu2f8"6ܓu dBJtU[Ҳה?Tk,k@GR&+,~dbѶsP"A9Fv8!ZI~uSeCCy^:l+r(3eָ9EnK8IŃv#f]epyy ٗ9~y(ac*^6&(Nxk|\ L*4wMn|hf"6fheA4 WtCZUBa>&'k^.aVO3|* 饶`Lr刲ƮG뱂StYԉ\QCA*A5Hݺyr'I%9H֚R/!քwi䎹MkC`/ήjA{Rci;ȊE ]IUI,~FwqUUwop҅rEzj3^|>-yV{gTʍׂ$+/j\}42N &I+q=<ȾD`\T~;ٯ P܂ MNexݡ73&)OK+]q:-xvVlYȋ,׽S \ E ռr__aP 5ǿ ɯgI2?Bzuc/[Slõ[bGULɦbkNQ-T϶{iP0t-3y:"}=HnYɻAH/gJj  q9hk`0NA95%ii?o@H·Hua.₧gP\ ݵP#: jıH&??dD duEAޛ?DU82VH7RhK bGg KBl.&l)BTo͟GTtJ-Ww9K|hA VcZJ:Fzz6:P[N}zw%1;|,=ш .WMos+nx5mkP5O-QPe} %uQ݁k)tKS_OxKnk?.Q[zjs!RF+3\eǏ:xWU ČбDXZ`xmʘsRr {9|kU8&xz䊿{|MЫuR6.&I]WO~[⛹p)!Y)2kݠ(,ŚsE;*AY`@\⠍oO3(Ey#o!>dJ 0^Q n"Ī^)GJ3\V+_=ݍl/oyPz{ĉk{s;QW0+ ֿj:A< [Хek[Ƴ}wt1]ˎQV%~NgPo 'ExPUM=X xxv} ~lD}e]9k3 ,btjHK1˪-vG|Us .xmضna2ТX Յ?93+/:`F͸U}lH[~WbrUin̬Vsg^ՎI"U %H,YHD4kcWLJuo"`{)T ^`;N=qݣkq)AD(:6}cTHʍRV-=P3@-41,+D]Qӥ'?wח比KZzHZI:+Q348;NvkCν]ӢukZy+ F'JL{M|TfoI+E0l"Hxi 3ԩVٵl u; (USW=6q>M5žT,.|[WJQ޵lŋmr3DNOHYN(sȭ <'uu#t6{-|n^:=f xa683ݝ 7 ̷M/ޚ3e~?"[oa+oZ$m=Wk xL`76}KPHWLuՀ?G֢k YDYiðx_U˂}& |mv7Eb/)> SP5c@P^MhLgX1Fy ^];W[WJЉ.U9ϲ`*S[J' +w#Hg{atkY=!ӊ7edLrv?gclKS}<Gl}qᱺΏf8,"kׁkt @6Y-h%3m%8WR2Ǖ3AYf}vܼ7gw]rڌmL2s;N <ux99%Tjdb幢KI{N?. ? rWV6mKFx)D} c^3nΊ<=ovb .[ >ܹ*Q6 ~0=4\; v6tޡ0}!cks]g0'/N8||GXaCwL=x|-*iOpklR(v*:2:&v<*yLʰ*?CcU9mMts+Jw18ne}\Ԅb}0DŽ$/H 3_ons-a͍i7b+iFK9E-nty|֓=8\uQ"(TWI3v%XOsx;5=\FQ/SCgfߩ.!HH45wxz,,kxKбI6>=7wËFU9bZN^ЀcUp iô6J,`ZרypThM_|7_# g\dW9(JFc#gY hw]z='r[)e>@ymN+ {;Jxݜvݨ|cئϯ:{|O.Dzccyma% O18A]H`'IoUo`L{`9(6`I*_!ۻ%EL7zPԒ}/5+FWN'Zz/1OZo,$B0,cAu5ilR(cmFkk_46J,|NmRP+z1!@sr~\{Kb02kqjRg3%-0t)ǰkjL}mDi .u=-iW3̵ uP&]ݾ9qX)BIGA.yƐ*/O(4S3}wMվagaq1eNlRycs8dF9m$o$%݉Ǥ9ch6XI˧)ӹIR#c~$ =f˛'B"$]k \it?z!jR#Y Qv"P]@iPҹ2\5& ?v^lX3v[#h`%Mr(xG@CJ}ܣn"%wx; #xVMQsY7 RS)Hcc-D]0lyhP(Ť^70Rm<@@Jp7DzlܭȒ\٭lH:٩]of9{x^-(b-v/þ!i9V߂25ɗVMge_+c h|ڔl)[6Җb|m<Em JXn> נ]"♮%ڿ?# (> 'EG<ۚ O Q/ u ,0TCG vͣ}$ބNQKՕvAvlG 2SE_?R[Z!RWȸaUd ܢӍc%Sh3uaAi1}@#tN3 EK]LfW߾VpU<  ߈bye$ϛ<8V֯/-+t+dva,Ip D֚,S]ݒͱ %l*tB*q gmafEȹpT||0r3KZg]|6gtXjZ̓e554VI^lbȱB\bl|m} eĒS4 Oix]$p2\(ӛn{|.190;=rਃǕoH/+;~{oEtr%5MPg4". + pzPf\` 8w폹_ :e){ѩe@(+CRAg͟ ^p$zUYP˗,Ҿ5{L?R)T4]5У"Q'"Ž1 p4vv)"ZYE?|Dz-ƛ!Mg( cC rcC)F[5m}={>H<ˍ3/<DP32S\b4ЕEwe >&JIIfnJ v=]';JXtYX/YYe*l״N f>N Uf>\Уũ0rЛ57fAa^?l}w8~lUj*TQ5k5b<1kRj**VQԦ*]jګH 7}>"q:yv-IP.&it 1+;`_"_á5E Ka ׸(;Ȣyfזگ!46/'(ԧ͏J:y w)r$R ˿'_W۱0_n詢a8P]_ ɊT!W/v.|J? tɈklb\baJ L11K&XWLG V7ރ"hI>XB!1-N}WNYffí7k̸ mGp)j(r< {/Pvw&I8N^݅3kHUEnPX1u"'$zVM)ܯA䛆R9̝7K;KU[VdCb!9^UyKOV5_lᄇbu< +[ rZ]yG]W@ȋ~Ìx;nnVt<*Ad+w؝#K3AotEފ0/fOˬN /9PvGL*dF," Ը˃ 5Ͼf򅓥xلUO<\LSs'|}J)*2?ƙT1*4m͠qGAΒpVa˓Ev7x*M&&7gwaoelj6^V}QK !qz~2J~Y͏d\MWw:3^7^R /-μdڭL (FLJ7}inz@_8Rh2F>4C6W1u|A$EN?7NwHN4YQeRSӼUC-`4g؋XGli3DZyʽi:U:}.^P3[fU!|Gl㉱t[mM rtHOdD-h_ݩ=y)J[`bMcN2ԟo?X?e߳4C8BHwij-hQ? h1YJ }-!(]8ҡ!~3:9:/ !2s&"8M%ei)?ߩ./{k|ÔAt5C]N_S~;x'1JL;l깴ɀ$p~` پmqGp8J+ϕRLBN@f*_V|F_.ntn-V[Y-?߁dw\f@JаBeA;jWbmcRxw`立,V֥W 1(s~`0^Y{KSQG ƜDGoj2C,rEˬU?VӒ+s. waѥM,0ǹ~bYTm./ݎTƚs@y Y Ah&7eڮ ܫ(ю 'K/NB̧&rE!/w%Loʥ-'2د<~+S` rp 5^h5[<3N &_>p;)GȺ-l&׍`{+{]]xrh(jmp:pm *а]ޱZpsjŇY(5J-3yfZclQ%Ge&0Yqk<K~:QІ9T<\ms&\B\amӠbs9Eg|Y/甲!Ao;%tdE"İI#_#l2yq /)ן^vZck^=n7{\9.mhbp n.E]#Fj DiSۗKI}gOyɮ`]֦/ƫ1VRW hehk^H`WYd<쩡*7EG/ ;o w"5߸ݮ:vv}׌|Xeod$C"*xqqwR|AӰÐ9ǒc/Y>5Щ@db=вiw=$o8=ʁ3k`#`)ҷ1:8jCM~Be율Pǥ:L/Tu|suǓn⽓70_'رS y; P.+s% RsEy (E}S?3,r]tApZPeq;gj8@?!XrpX:Jdu۲4;gH6?Yrcҳ춶~l9us$ 0~nXڞcm<Ƈ.aΪF<(~T7kޭf6hd^zgZKQ'x~yAs41^=J9 Vfԩ1v62Ǹ5;!/K?KvGaٲ;7=>h+RF'-uvxsewR٦O[ć:tҏ`m*#VmfPVП* KsS%zdBbizʩV_ͥk$eɪq=V`@Zc<7xr0氹JsXp]uMaFh M:fܛa +dB 7h@cSi#3Ϊ]u<]HYƤC"ȢN8iN6="^]'6^LyEۙA9H-JK\OЛ Jp:y{d;_~? m uh?}rہփBQjW%9߲\2~6<,$)U=Ԍz?Elcҋʬ kM#<(^uv8%ӓ[!ŵ^So7_JdHl"z`(:=?r"Ze|n??6L:9M8aa[xN"eڢ(瘯X IEGpzhuZks8 6q% WG8lۭM'CtOiȒCx o_!f_W==u jroto;.zT2`_؞l9c/B\C[]g}#(ܒsj=&,KIPö9gg.&^aZe$+r&'/\T%YS98->y]>r.Oh]I#CO İׅov,K0z5Yo ||5$VgU3z| P.6MZx|ptޓV=գM10s@6{ ?zIfDWOnr>5K 9Uֺ݀ɵY'|atޢyx|1Zy\iz Dyju~N:']^?5L]lTw慓_9Xk-OK} s|vTdh-JĐhIl-mv|/}:2_뛟:.wL?mh$Hqr}*1GC,[۠9YZU]g>XS+0z"Ӡ+{ҳvZY |m N(3gk~IP<-vj'$Ī(#+׮[tGV%\FMVlq.ԥHpoa]n*ys3b!,@&M֭P2cD$4Tk a#YmuϪKZePS?/+]13Ga&e.]|;#!5 X!Aܹ\0 yDL4K$|i)娜p;QԟJ&ϾbgN"ϼ1wgE]p<BrCnVGKnмȤ=a'{#XGYlL.AEl!2\mҚu2i[9:df~sm z7Q^04A,HynF =/Zvnr֧ OyI t);Gy%ATP&搱 <`{1pB^4vf:]F =~ *~b,tA";[+>6M* ?x)RD;Akܺ=^Yj9X:M+YF=ˣLw5F|{9 ׋?HUshf4ᅎ7Ǿr: ֩.|Ɉg kp@DI}>s^i /c%o=֮\ol  ֮owI9B#H&Yq۝qfnȻWPvMҤfgyxMisH8jC}WV;7y*F@5%;q[,kNHdoXCK'O+ڵ΃x#b_D<<&YUBgg-gV.yK}+LwHd;qo4fN;oejȥif@i=G9;60LNLuU~dCA$zvۇسՕσ?svsZfu<豏zlCiFE(WĿqD7@+JvvMXH!LȨ2;OP>u&vF'squ%"_(w瀹1XcC1=K3~ZVӄ^!aMC>Y1(?^ҠN}H Q R[.u`ՇY_˂կ(jq7*@\~0:BR$PN^ޭJuX4 YxdJRcmff¾h阴?('YJwY)G{3T) Q~"/.(vj` K@Ї#;ǴBNWt}2.*w!vV,ځgl|v˭7aü vQxDz~ek6}ǔ76hqܺgͺӌϝB>&lwXH۰s@XK]hěɩ\ARP'MyHB ׉kiYS/WK4[RʹiqĿs<<'8\ v2߮Icy9:e̩0ϔ[p<{ѕ,BXN G+n!i&G]ȓ>|Gfu|$oƟӜ [ kǖ;R=KopPJ G\ǥ}i$hCQs&YI xgh1’w#JsIE`Z(p([#I\n#]8 2y Fh\}UZxP' US*$Hm bEo9.7CBpF'Q F/T''pI].6lf.lcC… !!O\u IMn2Ko%%ܶ%=V$׀pfw/r?A*ozE\1| =)8oΛ\[jy F77(YူPd]M`AZ}Yq{u\QIq&3!i)†Z$ɸE-N6cq֝{VbCU-7?b%'Nyv@I70cLJ'.A,4D ו = @,u!Mn=Dlo[֦$ZwLF[1B܎qp1c-9,\N#?h\rq.2/fXhUcF;xqm'd%~Ie,8ٗcht\9c6a+yk܊{'hM̏HDM |uAF̎h݇}/ 8"'-# tsⳭհ_^_i8ݹ@@Iڐ?) Aщ025V=LL88 aV0DZ%$9~ТXӒ6ǃMy+:{{[IBJ-noj,nܵ(SDBw`~Sѹ-ĔuaK9.}:{>XsMQ`,`#×af)_+ MyBKeHV؂yZCuef(݊QT&[`#c޷t0]G~Go.-Dl=5i3۲Vzrx%q%f5.VZw0y񥖗5lfSn/YO|=D$8rvH'=*JY;>ӌ7ku3 FMoE`{20;9"_w<+5l `{JZ:a+4,~6Wvb3Ir*[.㯖r)G2 %ęN Lϻ.45oЄ[Kߨ(L'Lg2Oמ#MKOſ N3?pcsr#yE/©z_+uf2$bjb,/dLSBAXIҔ.cwIu-HL Z>EKj|>@A[Hå|{U_ DOY9)i"2sTPs?za- @|֍Tfݰu{P|ޢXvSF+\e!(dD^M")ݴB^0A%}%465V~-jeld؊5Ϋ5I/ZԖLJVbz͊SIxĺ(`sghS/"!{M`MkS޸3wH@ªn$7>#frKG#-ߖ^Y(>\coyHfwع4d~m7F;z%;*,-R_zHZ[L::+|7â/ݫj:ZAV7:!7*Ņ.t/}:[|;$Y0GV7m k2t8Ӹ3TapTW17]8ŢN 9u,M>URW  43wz-~#z&W]jT$p3Odvy޴14_2ر B6~iVRHdb8D nZZ;Z?{Z;y84$D/lbI#@ Ƕ$/D 4Mrte^wsln!ۤUC??^`'J2{O Ug-4[.KdmE2t#oI#bt}^\8H2Gc-ÈI3)12Pܪ|6^r@-+I6뷇tU8(\{,wh;[.,ꚇ"OJAxWѮ*UJjT\捘0xOݢ,c,kwS@T{/:gަn7'BM*v9 Nإ{,i2M~;iBψTE},\cvջx['zIS;|}m,& K5× T24y':zm((ghǦy(1P3vMc)l™ޡEgʗy--*ΨΧZnjQ3/n]Y^n14q@J/XPxBco|E=f()Xa|NgaR&)C |/> 8:q-8Cwu*^s(K*3¤4~ƓJdTMx.qS|u/3G-'>9-i/r ݇z|?; =0Bk]yC^Sx4+Н@ub-$>V@6 ˚ !>;A7Bnm)=my L bUM,(^dNN#utSĢZ#1ry~=q s$DGS"NQ!w Ka6]n /_͖O2on^%ڿ-#  O3iSWB]dT Uc{C5B$]/ŲtriFL4c'[nٚS]K999?[.u= j,=Z6,K2䱱lo 2<- ؗ5(AyY9s p ؜A%XvB oL JJ1zמ\y$'K&O.w/a|kk\?%'n|mVоK;Y'5k PǷk3nB{d2~H>׿*{%KhAaCt}v=yc8])^(?_q;xذN-~JGB}?K\`O@C W:Uvt0 ;w0i3j/M^~o~3ܗ3 :hgD-f"J2I{w+sobE١7 D% ֗K/]3d–!yYc>ʺ^ijgGwiMHs&>N񐧷4E@PN }YAKT0חeKװOAʏy%A$"d:7_.V9,O0Ek,wwљl,J>r TFvdeA\>O' .}8y 7:יT@QV^}Sp}GhMi3޺} 눔GnaK-8<2Vkm =_uJ e7e1!m;zQ$BϓQ|U2֭G-4`U"ot{#X"/7tcD4GYvt2jP>T\!5ء5?%B;KIev#/ UXH~vM0ep,rh~=65SorI34E܏7OUtae3 ,L!~oh*,w0KMxPȢG#~A l+n9@{} gO-:kAKQ2],[\pwOYWGdV7'TK^ ˱4)72GZFMi?Z:aߛ,I:wXp _D)Lu] G8օx|?_͈fL,6u5r !n5Qa_PfD,veKkʭ]JeGw;l^lT| ; R'6dvo0]Ƒ' LpQܯWnv+/,+x!7qe+p=IajS[Bö W,5'̶`jTQږ#ح+>\Ƙtݚ&Ea;&0 ;[e:M.} o~l5dFI:v&G. A1l~ yp=5JD۶N\te"i|9IxǛx=U{I@~p`W!d^ա Js֣3V ,(}9RPX1mSd=:)oAb|#Ӏ~ a[ϖYSH)5gG&Q js5`'\p-X)?}m}AZ=XmdʣWk;m?}TGZ-:!{[ FmAyNFK0ueڝeV^#LfLd]p7Brt՝]rma3]_YHpsmu考KW^L@ zZZ.R7w)8ە Q^طT"7^lqi϶LkPօy-hF;^t1vٵeGTZzQ\/g~rn"nCz}vCeIr9 qde7r%˄Ҥn0U,zvCh8NBCfMXnl¥<+V8Խ5pMQcnFPO4bO`NRrQ !RK9b z@}!Q=y18,5E@K5Mlza: 184#ͼd!I3|JU EtS*ڄtl>^Pu1_D0'zO/dq^R]{.Q 3fRVGhpc+AƉ!D{t5AbLXՄp726 %|,OPվq]88r 6>}rA/ B[0:5>{¶2;J=s#Kg]@aM*%Yk~D2mv2̰n5qaUe] c K'y;?/DBZ؝,ȧta̖GGd>o.rh)nCs"tn-)wꄏD 1;: aF/̰D疽*zjuŶ-GO%52i}CՕ{2mV5@Ad`45y2Ά ʵgO_Oc 4 :%қ*`cW3g-+=osk4k83mKɗ vˋ>V)V J%Ob?zA}+R5́FcV?7qђ w;r>n>^[t|>uhiD2ts6k?˨0c <"K;<824zL>R))H.ՠmiØF&;0IJǖOD>'.,HbK? Ρ*?gj$nݮoyFBQ rkM%O7ruW1#oJ֬Ǘfe,{8o˶bMQgD.9>.&Ի}EYX1xǦA8wޔwCn'AXF4T֪/ .|\{;4w`~/~QI`drfʯs̢;G^5_yLXPKZFڈ55sLi$Us,1PaĢt۔w%U7|x2R3CD(7~T c9.~˾t(,X,&y2:8~N2;4/gG4…67cwK3Wz 3c;~0sr/૗83gŒw"Pg (|[Y6yB:-8~ eھ VÝ?XGBnq"R5z~LB/WB֔\NrE`Zba^Pt7jwD%2wGk:=( ou C.pi,SCvj9[Md\?ȹ'669\E:?iC,q ݂<'s_V[z^CA)%n,YqA%.o+X16'pֱ# 3CUuS٦Hwt#'Oc4_/ ^!Lv?ٝN_u2,w0G<vW5X9Woӣ惮;M'}Moy X|6c_LHc0gp:ȶܕ +ʬn[x7)ۙ}zⵁbp&$>졟}{Οד(Sا9f]O% ?-vXMX+!AL]{eIdy"謿%IQb[E3/$(F'0XQUTQ"pQ; AwS {"QMn]⡎8VjhD"@ gDKt9Cd' ~ťE٢'bExfBB=Ep/+p!!zԐ$fi$\5\Û>HDMn b*=w^o rr q9F;U}M2w t$~ʟ|KCd!nN0ڵ#} x΀ߴ .4Z'h p@MV|>:fg*3^Eu#}D7^72{V2bO!Lը䤗Ƚx̹C$v9/pز8kjd,~WU=fk58۷o"HzJL?#p[Gpx51Pw LYl"^oLzRZj?z9ΈVX?L#͚C-h%FQ֔-{#SE& EXӦ$U=6}?]_5gy2(|Bsy~ q̸ү@IN,O*V>iEnƷ3}>FF1mΫN44͛OL@tfEPյ1`ilwAc  Nqr<WQ87̴(aɰ2r<σ;A<ͼi%k:K:kqA8I4Cz\8,Ae/5 U7^2#b#Ү;oE(hrڂ 3ff?|=qQ D1ŷS}z<r 1{S^"O(;1X+VYtJ#ff*[Zq ^]~KX|`tiX( ZFt)$^/LٲI3 @s΋\(Y3_eb;raΗ!ɑuӠt!({M`w&!M>1T_0rQ<E,NBHHH٦Ȟl}-K Qd'2d_Lag~y>3s] ,-X/bUƞxzLTZؿL"7a㳗F75b<*;)tNDZ,KCmc}dHHK29[ywKUMh-ak jAͷo'.y'QA{oLʩ-upw=0H /cnu7hO|Θ]$"hFSĮ˻M`:q8#e т[UpL+?G/"vah%znjH9DٳəH}JbJKTqQZJ{v#qf"D~k{K'29lSf8a!7q F8Mb{yP"ؾ,%N恏thA[,k!@n?IWɧ9-}\y/;^4hU]r W|mm L̀wاMV5IEʖ=JY0 k$8oNi{[3hTw}.,&yA=)  9`I1s>4܂x6kU)J5Jd9{V+yʕ/ĥj.ExO3X*"@mi󵠗띰\TrT!tw~iCH!UhcXO^mIEBY; dkH6++Rʒކ)VOmJF:sr># ^A>`ѣ)B N$/JPvx'`Sw:SKl@Cu(-p?v7RҐBQxNb* 2rq37ɬ ŮK1',/;mQo˨Z""_5&NJc?;}!N#SAtc#D9F35` hR?#Tw8p .C,y @1Fo@Pd12W* %0P ǜYQe>ZPA`nVmkZѵ¶ON(3@bq` wmjz t @Gjf?շȝl("|_mn!z'9^ [( n1| *C ,U:@"sL`]Ȃu 9۟k;r< F[RC ?8=|gcZcB)IIO3)R?|-ÏP}=D`)+CC!{~$sbDE)Mi]MV#3?DHw6䠸% e"+@Js ?ei$=Vz]Y!J$|Y]XZxuSl.kZϽJ*jlÊrڵP>FlNLӹkTRs/O2.B.ԋ?_UYa:9C뻑rp% "_kGKR# V hѵ+ ~1yξϝQfqr߽=uHpYmX]/zg,mG'hUU^#o(zv#Oo)Yc(ϙ9N+@e@0cs2"TN;W13G&TpL͠^C y6ʼO*^#Dm4tyJ"$s&VOٵ/']L0͂[PyLz\bDT.'}ofXs] n59j4Z,``4ծK?vJh|sЮ?.q\1mm©x}PnzK;rD [觋-tF O|0}YL8i&&o>'dCq 5<>GB/}gf1n#dQӥ  w-Y$Ͼ+\.܆&#οIzzdEuW< LpqXo^MdĚ'HXiup?eSQܛPjUE3,|^ֻ9>L/S|m35,.w{XiĖY\v}E}&[O΄9؜A[OC(;a]P9t!Rs1:y J!o.'Jp]+LR{yymp,1'7ذPyR_@O02~4V\{ϛw_|5 sE٧ É!7\z\+}t>)Ӻ }nc1@2iT|̤͕/B摑7;e9"+޴:,^JV'2zGE4׏mw٫U6ql/k u}6%#őky[ QV"eODp~I FVZ"׳=V-DaWCmv-N猌Zj'G\ɜɧ|Q (KK̦@8A'H,_^|KN.9[uIod͙ @_?]Խј>w/(ڧh~2,,Z/Ŧ N/t 8λ5p.uJWI Kޚw܊%d`ꓔSuuwXx }┾x>5Xltd7weAm3WG9 ZQO>ϔ:hjwX/5Kod3OINCNe-J `2뗘s̓ Ԟ1\Og]|F[ؑ7wύ~De'V}* SC</~1w{Khyw|J5O=Ѐׇ=PNzѯjF ƙ{vqQª8X/Ӧt+>;uƗY#yV],PBF~̱u{MЮ*p]?{x!K7G`hlto[6nZyKu$Է~&=Ig.`j 3Y ho WA ^Ơ4>hd#6%Nҥ^aڽ"$dmwj'O"Om*4oP?op'@WF .͕ O\&l>D4LQ}6Xڧ Ӛ4x.P1YgR}0?~ Ġ}7aԮо+ˆ>Llϕ"iTIA:ϾF@Dc ,WdHL$6l̓ﳓ6mrl nSv`v#p{\ʘSx:\GQ'0FMTj/xbb'; Lw4_۴' a Pv͓I'#Uώ8H-oLzK^/4{[6#s.)MȁOʆvjַ#a>\ -~K3Hk{⧩/y[jݣUiuT.8Gf8'HoDx'ȞFΞC*C O"s6 ;xHyrf!ߣv"N%vVw 4D)/`emL ҐSCJZl=d}*|}̒v]4JhDwؠ N| 7? lmϯK#as@׬yDOO`s|- mRVo1,/58.qai ǝONBfUvaTnCț4v4!瘙h[Τ;VE!ǚAGIkijOKQ'\>Bm$D &Dv>hqz XO l_~[ӎ>yDJAɆOa`d.8\R/"~=r<Ɖ_he՘zr,qwqN{VZǘoׅ^]yB`#2qͲS8E= Gi}rL1X t**X8qG<+\5Rn%D.wA찄[6Ht>R3?BnQ -T,D.^-7* w 16uAMFuN:[&] |!clTކ2ន¿88I>E+s*Q"C?Ad#Ezv|C*Ɇ̙)^D /SkJ_vФ fII26y7fݹv/lfV2z$ | Mp?9c~+-cG M_T9e=x*.hC{veZ([^=潣ƒ2g64V'b\ЗX3|3JZHpTA>$;S f?>r @lS}@||$(5[`)[bI{h}}֕%݂Q>d|<JUldaCcNdZ{ bvR#..z̮=VR NU`xJ[x\RAO9GH%0YSe%^օQXXWg hjN7Y*`, 9/e(^OX W@Mb[8c{Cr2=L"[,u娆K|\K**9Iu sPN8W;` m[lJH4-s%g霉 ZSae=t*|ˉЎ4ėWgNs+{MѪĶZ?np͎!qQ65#FEؑXԖSuKHB&-)/ݸZ+?:8l4H,<<3Lg]5W8?]w/Q%ÑNFQMD3~ߚMy ޡ. Dm !Ĉܳ=Eu, Q "՜oZ5\ w3iuI\=Tϒ${,tn_ Y"Q]jpb6.AuyUhO]ov}nI=$1ß 9;鄻|({+,Qhl񡐧N^W b-hAK(G\5Q1Z%!V4XC7uFt摥{V" @Ұн{e0<_ϕ="CB?e-ʜkstվ\T ZQ]ojIfs-UH>όZk f5IQ/!Oĩkëg:>* kk&w^־˼!oʽt8 7܄T:]Oߝ_ok;#S&x&cEJ@GmX8z%='[&mY'*N_\tFY[`[ylL=v3ne_e:=^3>C5fY4,2xMYEW/$ w8ay?WbP GU $ 0.,1.0v3܅9͝ႉ٠wb>_BKh%w]jGߊA=Jrq1fW5NΡť=>!?WQkʢ e|?Ve/.RAV(x`$WÔKVN/sûINq9׌yy 8Uv Wπ| RR9wpԌWypϟ_6,j1j3c{nWy}}slO`. c(9Qghz7 ؇|%k5OMOxnvʐp,EYGӷƟZAGlľ 7GA>P~w.Ӿ?WP{  QU޺1K{i(Q" J ρ9{%(,#{'~C +ƺ^e R&2`WXg EǶBK"'IXwtG+s$todͿrY_/T0~*Fv)1Z9Gg]*(zT%Z4FF8̥Lq(,/rejƥ'Yd,27˕F3x] Xe|4[ U98!L}P]ZHݾvDgH2L|}3H'Җ(VbWLE>:L7x -%8{<7go!uϞи  /I}4hj0`!ͯG D8ʣjWe5tS0gu>E)Q{-" 6wiAꟙ4[!فcXh$I > &'.j/D*ʼMDۢ ,.a7UtD>ʎj›,{n3唾u=^RJ ڔrMeA,7T Nl6@ϡ3Qhf`H8 Ɖְ57WF_,D?"8}?dyl $=cA"*$>p45TzXO} <ʄz i~]k"ڣ<j(/WNZMhOLw؃ƂlJ(\V3) @BѠ#JwHxu\?`? MermIt 5̺OR#iC$gdą$ K7/Z+75ClK6]PɥխfhQm$l~GUa/vqy4i= j:D`z,? -_!h&҉az#gt2wzWvd6 4d$#޼H}N:#L,*>dsТV ) +մf7EOa{s)*:Qb{pFn*'Vvr7U+fx*pU=trWvdU3X,+Vb[`J{̖7lhч >ta ^;`%?SFTxuIBi{0.㵏?[]C&+5Р~8G-b%z=%j^]ucfX*Z-H5:)Ep+={7DhYM'H``406ħhf{fXkC3Wf}6w@Lrx()J]`=s (ʼqRF;ȝTq9SB4@X;k#Fڔ,"NSJaѦьZIueoMW|1>Q^+> $Fģx`rx&{7<4eU.^̵a_DClO7@/wsvuHCUWsv>@?eȟd~?:'Jd7&?.ܤh&9AoIڛ2hj{O\b!c)оTһyֱv>ǘJAu3$bIA$͉??ӛ8]"]&c;xq$Li,c^є $P0HA:7`{R-e4t̽}h꘦/ Sn:Khz }y1|Y(phCI}j{qD)֕vel/j YJu9 h1VodwSҸQԸ^*ͷ6> 6O #fapܙ맜LiYr}G ױo<3bw/^k=<~OOoKwM.-$`9W6 {8M2WtO+DU.YZ?ӯ Ek6aˆUDK!"6 Dޒ\gXt.*+rUc>A5]{&&>ٸJU$ʞn8J]U]I?^l)߀vQ]qBn Jn]A`},< <&I;KD5)DZ8 pr! 僻IvF_ IQ?fi]R7POD 74rV0v:#TX" l#mɴM}{B`# (B=WՂN.LLմ߁YJ p9\2b=ߨ3<=W7rv{o_T#WdeX}~.ivmrPH"V>Y]U;kJ~sl3e Q= Zah^73$,iS]W4t#b ;:[GKz#SB~ Wil0tFJt9L:ϓRQ(LkS iq\UȺ'rqj x ok6MG,?]G˟N߰޴<&i"S}Eg⩙ߨwVrr˅ak/ہG= ;Ko_r9ԕ-]D`8MB'}M'Cj]C6d6h ODǴHbNf77W0A<ςu1gğ/R+Jh*^$✍,j*6$Æ?"׿TZy}|'˦G:[_B<ɭwm% ӗDžF`"wNc376U;yQ*9={n_Ȣ: @_oG+*,澾/l9 ${ K5.|w*<}{݈2>JϲzL atGZ-ɟzOUIm5mVolJ5SL.ĮtJP)!`2l] : v .mtu(6_A/%fjDLoxD&Zڑ\ͯ巊FYJuަ?lZ5&R]2ZH+yQ ^sAttj]̭e];%M)6e2sULT Qy|=?Ei\^9neTKh_BGAkI%ϮlȱƙNMOO3,E5r+5` k(~ιKS=  eѸY8`~EB$vl1 <0Pؚ*\ͻQc/*ZD:J{&g2(.f7ȱ?Zg%R`d:k9o!mlPQ!_ "= 65?X#hSs(7Ϻ>-w r;7R{7"e<,G3eН>'q=G⛌P!9dYd/HòI9yل 5}^MrZh Yt%.-?Lr2=1 ]ް},AR`&y3t.};wnƘc"Ξ1QoxO7;&AJlYO+ȚKm;MD\)*1]>ܔmOYSz yZCU]mgw/b a{&kr |zz2@;_Iʜ5Mx,(xȟҟ,Cu՚r^qot`sP1U|?Jh%F+\tT;3`T&(͂>ѯjDKL|s+g܊4~}䛴JCrb$Mnɿ*a+y%Z-s$ZN:ys%"JSu#v}8 jx7eH_NOV'Ꝙw`Boatm.`²`C]'oV>pӸ@H!@7d.*jGZ7VC Pen94d AaäŊ}w:z5- JkA S]\2T锄)(.0P2*QfgHZԦw*u0Pık}*B z{yzsqIjN!YОڢVoGp;3NBFKMd[D. { ?l 0IP/UMl=!_)j-ůi\ȔsRzĊ`;a ^%D49F"W,3yG7 봲 <R˄0,|DOFݢ9{<"Ԝ>e#RY^ڰ})'}HwZ~ˡwɊN_+R֙LO/]]虐uFJTB+ᝏQ>Ebdq袖e]Zۄ\b#*G: V*Cjd.VʪA/_,C6{CWMW o|2JRSn+9gOs=3.!XT3̂3&0X?ac E$6M.zXLmO6Ldn.,_\s:@79VǍx/]].nYS]|^Տ% ն8Z1$Fހ5C~$^p L-32Z+ Y~SqֹԟktE:@Gt$T? WRQ?756l92bS{> )6{ZZYℇTN ^NwV|{MT1f=MYh4##Wwwv%~+$BBG|%,ychڂOςy=0dn=QU1zd1 A RosO5}suK Lj !փ @ lȝ)(kMSdxx@XGjC2[VR܆[~SPNq2G&I%2h:x\cVxheh'W;<qN))SL$bLlrhЩ!򮝝}[ʪH^4Z KV bk7k$'55\P d73Lp>moᵼF3_*,3h"ɂW廾4Tv_+?= qq=~U|;TFQPRmʠem?%}xID6s3Yt_JY<:M_ FIxqdFb;UT65Ws@-}pu͍r {dÁwN@/=D}z;y&@ИZW kh֐a2:f"lT*6$eҀ2*~^TG !S#iQ0D{+rJo{~ٹ Ws{K<]dKd9r.BGtsvKN!2WǑey<5FA1&Mx.2֓WuIGO)RˠҶ`/*uWiz<} RѼ*zu>B0]&D5T;ju18||=y,3 *A5:%xFDÉp ⬏ւw\G]I|btO)_nfvyhu?ly>vɒldZRd2#Bl/!:vJ.Vvf~?^fWuyuV+#2MwΙ& R*8Za:{ EO-9dV::6|׷/ Fx>уe>V79G/G!&8^̖k6]jym*VϑF23SZѼ&b/ڐ+-E$ J^z(rӔdhs~t{cӔj9Jczq^ cDsH]Yg<|BD5:WIjH%.\$UJs$mLѼs|#flSic\C'u/`Fl-x]юHas8sXSeGƍ(Hsl̴`Z9;5 YD0EOJB{_E GS{>}%AͭYPZacZ'd`@:{ Y6{[w|G@T|X*x"HK9|OUfbnc d;ud5&I1Ao2Gaښn">I1Hi6èlUݓ83Sn=%)YUiVeG.Z^PkBۧRP"o< t܄[jh֚kmt'@-@AN#߫}`G/9vJO[鉤) Mg-'A@FbE>\m,ķ/\Vg.~q?u[#@@ONf"z}Vt1L|i4#T=/{qNjζ$M%qjK:Z& 8%HIkTWI\u Ĭr{bH͡\xۖGN**MոGo*Vk]ҎSv u{1qc@Fcj@%^?@<8k%r_ͨ:t'|ˎbTb4v㺷JYwhʽAՋo2Qn]p;1<&vE-F%TmhUWGcD n sDzIEKsZmh ۖ?jlC57P~5#a? T\^@R,}@϶C'`_v.q YJJY#:#IbN6lLF :s* w'I1ܟթ+)$d8~]Vwn>۹Ej1InɌ1,"{]9s=i"*kbfKpcR,}C Dž%훳FqQ'gV--"t R"!F-(xH?@( @0Ƅ .wȠ5HV[col9&Se>f`{~)vJRj*"'KcBGyv&.1plKI8^ێҷ @fgzkjTLGKX5A[Ns] [IPAMlw7Obשy)KKMUDde~jTDŽE/0o|K͝NXD2 XSz\D)&5۞/< *=5>b^HhluUer%𦄷cupfhO$/Gnx\n9cBC>@i՗ɠBGg3Myj.SA,GЄ+7 jJOmP o| Aњ+J:׷9qQnmfxS~un(Ycy˰6c:cBP\շn37@x WYP=V͆0emƴma^(pfry 9Ia&1`s#"\62TzˏE[{}b8?Ubq4wZw/o1a.qH7R\S } PGU;3d_XU6ƜtOҗzU5ܯ@?w;wQnσX6d_(w{皊 Bd| <lLbnS"z3 wcẁjmqLcEo%S+:*跫F쎸Njԗ{/g9 Cn駂zQui3kCOtDPBgܱT˙!BH q6cNuiԻ-e%:'i=$n%1b-18RȺ$.haC-aDjf]Ը H'UAbag$,8BI@ˑMj]%87Y /iz2zu, tژwO F~ۆؗt0SnT4p}sM^vkMbH˻.g>W>~#n6(J6qƾ9[+7)N)?I_i6aW3}9}֏>V}dmMe{k(%9kGK΁3RhMrߦ8MADzѹ犘THOFh̽rH+'Cg>I~4#r/l?lzcq?Q8Vk±]p#@ܳ?SZXeu-:#(R$q#P ׌V =|urqvʒyOVWH-P VTX^ ]H_\aZߖAa-MT CZ=(Rgd&۸rd"Q@},!Z/ 1`ǝM蟭5Jfx~~-u)'c=Ų}Cn_o])v-}\\:INAn δ?1s/+Jw| ͿC ֜!Le'0&'Ksa*=%c %*Bb/5{Mi_;HVNdDjyHGE1~=#=\IJ9P3 pB|\Wa7ƺ5i BAߘRaB'ݢUGOqͳ pJ/ԗvwdֵ'@)!Nvà. C;[nΞy|OX"M z^q+lG+e/f THYJUmVٚ 3۳ yϿɑY s5rk$"Ib*$!e!ŻўO~b9ԙ+SuQ|y%R} 2FT;[ٛ| =Jr[RAeMDxr19D`)O(ݔޯBn F0#^>_g.ђ(:ts8R#l{?p0򓣸 l|#qS1l؛kgg}Bm;kM;q7,΋lʡ=uˀQ_Ta]vP@}|aoa쁁v o8pWoϽf:삉tczsl~Q;)BsOqkYMƫb͋% {z= ȳԉ0єy-"ԬkA+!WE[Y$MUD$ V|;$~j,g2$a+ɖ2pӺqƏ>a۟wS,RT9,3Q|/^hdZpV ] t~Ě܍xIЄz v%:8X ̹sgOOvYH7dRkzMxҼwz!1jq\܃Bh'+eIe]7E_Lc1S>_qcpm9IQ %϶%ߍHRU?-FR6cVJ.X[EOi.vhZgFZD/byWĈjVKudkrHQ4qs̹+VD ν++F ??"jT|gcX<\hΜYs y˘2EV |zSfx%lyL.o*UYf/z5TEHi^VrL >ENY~G@cxv;NcgW@i g^`@tF3_eЖ2R~(Hs2%v={ m g9aLG^ XUkmIJ~|!8# Xۋ"\ 015" TgUAvVeFtbC\c4+L@Lz"EXMswz]Ooާ)ާz2n͆Xq/˴}I66WpMm" ײ|7ONVh~vuj4=ճCY e+g֥P'́Gs vy#cX=eZfiޱ즎Gc)^WeYxzX66Q,#MAz;(jn*tL' `wOCAAü MH8 \ Q {!( _J+'\! ǀkN=e[@qə 0 VU׫"Wɧ*4[ѣW~%e >*:„N8bh*6/Cxܥv3r7|j7}@ $T rۆ Z[ ~vTCj;k{͡q$'(>q^3 1̉-Zin.E+sӽ}1(>8@u5d#@Y {2u2u՟ &V>0o^UQ}qz#hp^ ɯdfO"$ǬltIYy TEST% a2[P+Awx#c,Y8@j7gvqu6^?4 k)v)]I3^x55lӺQn`!lY Q\Ĥq^ ;.z{iE ؞?&M\moDž%q*~FųNwk0/ͳݿ1D.p$wC:lv2 f8j-f,L~uDRë\\BQW\S̩հ@P6ԝX?tA`ޏh H db_Y\VՐқ0=S @|db/<_Qe)lu0o u3rY3hSqSH{o/hx- lhao;ѨBs4{EeyPse"Ȓ e|gYO33+NAY>y?IkXޟF)FLW"[ʐqB%@&i>[Ĺ74;]'ՉV/ tXxi^30 Z+Zn갧/Wg&2Y VD] s[mkA]bT?C*߿؎=fBF"XjhQH{I3f疴=1*AS?'p$S81PѼT Z"P;\\rZk)(E*AQ}@Eŭӓ(NyL7p4RRU]sB}*T/> !ߏPod`V?,ugm/6ΝP. Z񈑚qPenKr%V%.!^b^.?NMTrʈP)jB*CvvNYL!mף{J**V` cЕ-=NX0l x;]9}A>@ξI@fxԡKe5]{ T^hk!N;8NÚֽi^u?]P7Xf{ߜ,ЪxQÖpP5ښRayנ8`Jdop5ˈ\CO~ؓ({)=t 7[ ҝiN㧊ۚG;W|Dy*3􂫇PaV-* F]WziS^[ l~nDNyev>:%4ȿ]PzYMe؍{a2lV  vfdQf-_iw{7|i.*|DS ;/#$i qa 1N\ t5Do/%yӄk5!ؑ|OS;YPƲE iWq,qV bNj͵9\5OEp't=:lX#,ZFQ6 ZǺ?{73kw;7\#/@{g;q|kFPAC΀O?.UM_5MJN=^ .X c?->%pMVa"xv~6c]fZ^(,)8}ekBV%e1)Vΐe_QAe\{{Ǚh9DRrkSڽ[wO:}[(::9\R V5P X;Qz}R5A- !Ĉ,/KBK,U~x֮?c|b]w8ܭ۵oYlͰķ`U~VWWm2<=84„CgX\-^3 J<ԃN?=ɾRr]ՋW5BQz|G.qCg/_ʵ0`4e[nWi+T?δ3xf6R~wFDl拏3wj^ԏ|9CAN("tŤmڽX [D^ItLWu}E8x;R;3F5{~)sz'\ t8O ⵙp"O-f mG.(oM;@SE+_9|}AlNJؖsb?*'K|OΞͷmg q4K@-$t}4|4Glb7#P>6\^:̦ ? ;/md"Ȇ`94\f41CXG#1#0Ҍq5gD .RwAfKQ$}\YZQgZȅcSH5|Rgé([ed>ӴSpi/tW %epn1Rޤ-[cA PqљnGa \q:GxE`$5(uH*W?96$bŞڪTwBmL#:R9~R?پ->iȾ+? 3B43;o[W\yTfIMgk^inܥj0D\g)4L*_ebe5cJ(1GA2RܩGXXͩ3Ӓi$&.+# S3ǬJFի S2QM{%_8̾HMfjeCoBjZ.%Sgې3?@ltuҌΎm?HB%Q]a7: A$̀,ƪ(HG2*YkI9 :I Քx~9x*]D[%-7abmZX9 sCI$;RsтPv2 Oν޷+kZ̉> .c#O6cО0TP/g4ۂB{#]V&7E#_F{ޅ"Q1k| N!G8:(:H^ ovLdnc< M?LHC86+ɋiO$@]0:eӾh9uC/d^ј!khӷt/DJ?kGs ݣGsu*QN,{vN˜uU]Z%;ܢYJraTPeA>CSaۣ`hڐ^}5P(d0Zx{}>B`δ{D1a\;e X/nhEMO lyesH@j1~snq$?q{͡)K-we'@7ߣt,g#UAL `'Ŵ}QuB_,#1S4S@.{H'ov:*oXaȲ nasr>O+2ΏOՄFLu{ȄL+l5fGp ](>U3ñ byey?]އOѳVv$|tSz DL\(=ny9C)ޙ,FN/g^!Ut.͹0$( I ?f'U,]l$;=Nlƍ^>Z>:f/KPe=iVP0gNmR\H?a9ܿmݻh7~jjZniF 4j'mX:W̝5C K| eYZ,П gPtX?ە6լG[׊7}?!>wU_#z1,Ļj콏oClA 09Njdb}2~%O-5UqwP-!ؓQ:{ҏXkO þԂ.ᦟY&1 9,mM{<lƕ yBĔ|F=DJ*6T-k5HLt+ޔ.[ܕZAόf{~5kg)!.ʾFN+lv~ 9t3Z{%ŢnKPȞIi;D sd^\E#Nj,5sQDsǦ]i0֛$°8okZ:F!\㥺W?s')ϫ7^I9&JkgB\|/!T޵ȅFd]⯖f\z 8?s@]K Q`rT_nTWe.d$IOޟT+a-4()c>o[aݭgW*Y]ij 7[;%b.>0:G9-%t\qWKSHKEez^l胝?Йnh mR;nl.#$_n|> G/Ȕfyk6y 9:]sR$Ũ1ӫ2!ηwlH{2 bR$fu>2 Ӵp%1SvϜ":/a,z=M73f:ܰ3jsjgKzafaI!]@uO1 f0I KH@@OJ8&x&,gm߽ɱ>NK\l]< 9spp%b-ķ_S Hӥ}Z!tLOalɒMcш}+pX+ w!NU9ym3{.㊒J4T g?QG]4C$}~z'yac<$u\yxj K\[_H>xqɪ >' rfnDedtf 7l)CbUo"j !Oķl+ΪL#zc6}}|[~"KKnUG#0@431rӲx.GmYV w}|`US=4fSʩ`^!f4.6=s9Rt-ECW6oxYY$@}sz۽׻DJ~@ -}4 +8;8 !v)6ZqhkZu_ Ir_E\ҥsl,p5aW`י?zI^I Qg__Yj:H x dRZ )FFOjUeFuj0k55WL ٮ~<5;嬫3JK5T .ug zjz{ן_a +IˇSkJ@~1X񾘎TCj/>/e!̉[`O Ԏ1A%y2~GLozbesww29 G#]_"̘ÿ>iU WEX/r!)A|cL[ fU] *s TcنXn^:4EԷNF-FI긅]~U#>>2| 腄bUp/aP)MkΖP.ߣRC= {j?jqqlu9976#I^>CSNcn2:s_2LdA]RFz^XH Exmf/&?bMc_YS X8[Fz:MfzUN/i^CQ/ڤ\"z{5ԠbDcrC4 ~5n~)rN:kz[ G`ƤޚlѧeNֽ߾$-z{܃V`K WfޏkYfsԢ~5|π*Sm")57X57JR짹H 鸌\QR"_5? t=&񅟖#X',!xNmr2ݻejAN2Vd$0 rau12zy2%myˢZ{LŅt?jx [?4ewT ;E!.+˭0X.Y&&,#&B]]H/{CXXyTDkE*EYԓwX/%7?=3[Iܬ[E[MjQ h@.U;x6QQPi-^FQT-"ĐRq:\v!PT+bJ7ؙ9sL6;zֻ޵G3o^c|[ORbɽ\^"_nH/2r> o͢1\ʑ_/ }sM\ ן&tϛƉSm#Tc:]t'(x)-u+ P_7+='I6ŝغ*U=~ePN ~p'7iCӨk?ݷԢ˛, v%NᶞL[LAGќ nY*ZH \[ë b(e\y-_ҾwIam+~7Qvt_xec5kb'ԃOk烯&?\wpY! j47fӆ<^͒},6jIΫq)u~#8M 6Kb.{R"*qz#ŬE=cޟQ|pꭣ 3jOV~:~Ewje\PJa[k?R=(M|]?Mxd_ !Ⱥozgmugϯ5uu3.izo;^ޟy6{=yCE9W$~b& Q6Uzj-US5yI؍}]<Ī|>@=vWGݠeU-7o=>-)tCԶ>jYxxSu}~xLi3sqLN[[?{lwo8$mr\6JbZ?Sr?f ݶz9MxƇ׏ish*ڴfS]U?xG_2v6s}Sn8ocyѯoOg m|8=ΓkX>@%=~յھL)_{xix튁-IźK:Uof~j|k|g3{_nnO8L{,k߶_kYm4|-SI}矾{pyW'~(:gj;{1>o Y{]NO;[znވ8a6N_̻0Oq]u>;1}?D3א7"h_景]oVmS3OߺyfO@qcV,v;:ʔo߿z[y?Pnv*{߮>7沏rzV^AO,?I:km7^{Oz7Ney) is sq?̸1%=~r6Y'?GS"ǮGʲ,9rdGcIș3 _m^5e9 tÈOWD1fDqlԩ;ÝVM]?UdS8R"~]0W7;;G;.n .{8Dn4|qyMgbQwو׾Mdwoľ0SCE>߻O@ 10n?^sQxm®Q/6P7%;R;o^cTmrᬂS˳׫sMN2+_6qU̾x*v~2}lيѣye;kqsb- Ǐ3wz盦 }ⷷ:dy#Loo|/z2;/Wn z`(߰v;йUնemӬ'?Y4@}I8ӴKgVSι7\~TaO.I /NAkK%8"{XiMs'#Zݥp<@"%QcfKL%zBYxeXWqqORe^#}fM K&$F=\h?4hw{9Pn~|}u7S8ɻެg?Tw HRDp+EUD/L5{_*j,h%!@-ʹeͷ(J{+̈́P1֨|YzZI2ҦU8m{Ɣkj9KEF_Cf/Lh)RoZ%lubV5@#T8Zx4CdC%Dm7`; T[p^J[Y86[<49 6%7x pw@¨LzBqU۵Es 5> %UGLTe6^I7d>f'YE7@/ٲ}1ar*) *F4U ݞ~\:"J9ڐ3VQ 0kLN7Ud#?@v۝R=˚MBr/$<[=ÖI}l/`]D!hsLLwi+5 {>dw_YQW42n8ZtB6~ _W>޷nOoÑoR9B+( f>n^^;Kg>3|C˽33mPp<^9D406 ΎP,'pgqOsq#*Q*8ÜOJq'sf6[]򲚥+ ZRu?W s+>@UWDi.&H&N66l›5P\J6K3^4ܻcn iG_l3Rd6Z L"MS$f.%Jd*gVqG_#D]SΆ%Ь _d0"R B ?6c- KWsj̨ovߊWg4_ Jp? a"[pGn*V {2C%Z7q tD=u9ēmD1@B&TڧX5xHAտ{¥AyPr+J%t 7,n"TJgucqDT<ϲ=ku29zWT C!Y gcOH‚=5}zʟg]3>O|nOO'c W{jzUT <] JK-=ic^.?/2K[~v7umg_"Yzc :D-0زbCu)4]m=#"Ce:gr)zyu%g@3d3f dmdӯYHwC1_/oIs-xi#+. 7!k9$c^_.}Q1`eqeHݹK+tl_/C)(Țp (@!wI$X@2 [nٻ/;g7WWy! 8sH&xSk u@"9 zPO@e{VU;Frc]Y3sL ' 4K>rDžX_á 28R9x" >]+K1N☯x ֘ bP啞>qњY;/^.&/-A%u߀rϏxK z`z2]<"uUmXNqKp)kF惣wou/-ŏ΃eua_^#f :wz"Kh{xu$]UP G߬:g)=秖koj32`K_C9LYSVMHY"iM몸PkKg ^c# D~]YI2 }lؗpƤ,ťYQD,p)8RV!Ų)R'h/@g__B)F@C}8̃q_m;3ޗqG\۬%JRհ*yT&[SE"VxS(aZ5qrfRƖ5:3E՗ <0A6*d,S$t޷$z8i5I7vVZHDù͵37<܌ߢjWǮOBq |49yI?MZwaW)٩3>2Bl(Xikw2nAJ5~%GtVz+on3qYR_76s܊1E?_Ef| 5u 前d࣪F͛uΗ&ZƤyW"GBZQH"TY )mPkqqwZ>M]jF-+0TVJ~#1dV%߳q ;A:*IB3p*Aµ``6  . ?|*.;c'ǹ9 Yċl5Xd?۲ (; <(+*}K"s_͜#TSPVLDSdq7.Yi"qCз0k"D~zm#jEvwfq|WlV E>@W[,$B{k=@jԽ} .-=0vϪp&F]C]W$~ɮ Cӈ|r*hvie@%!ÅC`ǼxgW.TI&h |*`4`Ӣlw.sSEqV:R_Ym6-t`XIZwgU#^+f6w\HYyL!'YVEV O#*tHq{*)#UO4~3lQL lQj;N+>l>Q{eoDU[jDk]-8s$d,c;NI#ccy Ht& lrT b /fw1vcr ɋFH&/>`W6;=/SZuU4۝R-ru3^/QtiYVrUxIKiRU,` {w?<>xbu8r+<0g`ϔt޽'<΍HuEd2[KBd ^]@\a[j K;݈,mOg&WW_L`:`"N=OրX $qO YZYֱm ƪ8$() 6rȤ`| #<47c;ЛDwc2_ bԪZ{1myHLqg hG-^]̛ǁ8 ![D/._9 /2DS9VH䲜 cq_ee__"=fձ:ןePm(NzF=/jӇG]o9&5a*Z`PUj RbSN O)ZR)&wcBZ fީy SdD O_}p7wWnA]mfJM>ҷTcnad {M\IT0">J\z 5*OZBһz0gӿFbn_{xwjf\LسȒ&TI^ 0% @lNqt EFoJ5`[s1s`3ՇqaΙ1ڽ?qh.t?GX >jpk41Iq ⩚0r(Èarh玧ž&${i<LU: uD# 5YhYIۼB@z/;Qʄm⚹-VQֶ<|cNwB3`NU"4"b -qH4jQ@ | B#N划Z'"`8aq"0-V0c׬9" Hul?lKab+Aat.I8oꭨ!E :{Y4${7BO0$7v eK8וO;,kta#T H@(Qt 'K.f4r^9^uBژeRBH&& B#x19x|&fbDSu"z09@VZ& ,̵7!8,T@;?( ˆ}zED-9Q7N/ [/dD`]aKWxi4I%&s[A#rG|;Qà>ԺC IyXZQ[`^t֢?\/ בB cUh:Oq .ZuSEe;8l,m*; r,њFԫb-$ù Vahq$;M-|&HҙH b^KYw++iU<+zmP[`a̅{=-vIeJlaCu9VIX@8.J 4%hs"G"VeU9c򰋋 miD1%z`= ~^n܁˜CmmРY[93Bs3L0QTkerSh Cf@\c p$-ܨۢJ Tp- T$;*Kӫ4nu y'qMfp+F^PZ+\;-QۉEK!0y|XlUmVHG=F\M}sH?zb:nZ*U]d꫼t_ʡⰘoJDi)m2YL֩#b6Qqq#uQi蟪 S&fٻl ՂU,K{ߺ%QVkz Y0ˊ_~V\eI,EPi ;A 0MFp3j`CZH@!]O+ua yɞvMSI:챝ڊ\k:œۼ/iPU Tuq[=nG(R2Gm˼ˎ0Ca/”~њqF2[lHBl%x$L }QmnBa#n5fnڦc<ф+EL0Lox6(J5k!G8vvN[0T b:B(Ko?((cd!K},n 6fVjݽ+(;'Z:sag# YGp[〉eo+YsFAJ5eo_"x⨧e!_Nә3u„:Jl硁:lUA1{#sy$4yr0ba<2IX0.jX}1H BÊJJׇVtzHQ}֎N&= b6T;Q q@ZN 9 ].c~be/JNwbI<(oįcBǰ*:/ly?)%>i ?-y<1ȕg]P#`aN,s n6X|H t0՚L$E¶֎> DXk{MWX\}M/\Mu /_8n$ 'Y+rC "jsFgE1?Oc}%J?c+S9"דSˌGt26`8 s{ZԮc+JAbRo:݊h$u`gZB2>s s3A;m6 IIõ>Gn$ ^ #[YܝOpL@Ȩ"ۑ&ȖK+=$&9J7zAV~w?N/4TJ )RY/ ]KR^6) (cg 6Qu̅6+CH, I6zЛ4bxp ' !Ψyi?E ,S,/Gl9QK.pB4%+#U<ݍ(툐e[N5[ViY<+a<뗯Yc_ɳDaf/`n$^)hL)hmq1^o!p;#" (v@.XȯWta2!.1NѿݎֻNzPLN/RפҰ,;F-U*(eJaEFYf"jfMWN Dў l'bL9<EXz:W`KT$Xpj/$ RXe*IKɻ&͘$@s G頴^;x]l\@2ĺ|-X4ȧC?")S먑zP [R'/a/rv#\!5ڛ F yFT+~ ͠AhkO:(M=JcWCS@ ԩZNN+MjB\ޏHZN%46tybAKp+2+)E-w̿ ZEҺv[{M&VȔ@2pzX'\%_$b&%L)[jG%\ްTO=jn\D:LN̝'m$-B<ݭ aI/" i1 еGx:MB݅1r3X --sV`Rj=-nUF'ǬB[e04ϒCT8m^AmcyL\D סV^?I ii2u8UC*g Mz:lPk[}FRz"ۧ<Ax +:>+łWrMSw5KUʠ,ܡ `ǔhyƠԹ KT(@Gu~?u`X# 17{ Yx+ Z$݊^`SL8t Tȳ,LVTIw' h E]?~DSřQ7Th&NaF) bNf\ .{)iwhO:yp eD/qYGXFj'YE##qt~ ӄ5 9 Έ GqıY;1_ 8ndB{Ʀl9ΖL*ԈdF̼Ϥ莪8imxqLB0ݔ<$\W0ubis:@}ԙ?Z䟷BIoL+k1, J(]Rcr"~(G ^iSh:PPd94K+QeqnaI NhzFL&hӅQ?Da EKbD w6q2d3Z$bj6`kv/=NvP!:UȥAӌ/98=(N@M@f)F >k*ZFj%\sI 9PdUQa/s/+(*wȺ]aB'^7qUPne|ml~[ko$K,ekz*>G@pw~lHY+<ҵ! 22t) ٖV`Q.|E`W%38Wo "ZRY\N}CpYV7GAPm ?j[˽%BCO{kڶVˆO|"lgЄ&65s'dyS}M]qns2y]csHm"Bڐ* ` oe䷋SPN7rb1y >ICH lamJOJݛ3c /I0D^QZ#b|_; X+ڸڼ";RGgTR[TIE'*J:R3eG43z갚2iAhuO&nKZ28BeI)ՙDism9}^"Խt\i0^Aj ,wZ_6T{G/ 8/(կPP>rJ2/@" Sʝ%ҫ* | "ETM]&8?(Y$f3 C<_*}r$ߎGcCj!(vg%2ik\^h4!72XkWhf։E쑅83C)Q&'ejYaEFȚVeOsHF+pKaL4Ϗnwïf'-jWz)x* %N%&%$,Z_&Юjt`2.ա}XR<+kfw]Ӱ/&ϸsejᏗG}^Wu\eo1 N=3E٧W 6>/A/p)shlb_m/yAկB7n]aY Dfö l-WT丽6wh2qNӊi%ƃ:M:j41ߘt"M \>Ҭ2!֒߸-@vYnIۧ`tB<9n"DlĄ8d?STodK/b$?q>p YwEp' 隷2jT718:=s4]dfh_e$4 h<M +k@KfXDl72|j)Ș-X 3gL"ق\Nyse;JbK5 T5=~z:?fo4MLM7QgdOEE)vѽwL & _Z _w& vӁT@-Mn%=fs>?m(m(-<)?§p-XV KA&zV'TdEo݁BqgkkݏДI) ,L߂QAy^ 8Rq6/ o" ?Q1䙛uHg_npO3EXNً]~_Pk6M~4L⇨Aӯwn8Yz-:PqN?-+&W.-vl֋nFN_L) (@^хili'\@-NZ%v EwG]sk`?Ό|7t[Z R_f+Y%<#k- FDPC; 0 MFmZQoV*gP=B~mȬ"=c1Ϡjw][s_ruQ^'"j\V,:[ PD*{1`*2# cd/MXgFC-rCOl2`#(R&;H6쪗v]Q>8 G]0>d*᪉f?!b?JV0G$dUO9:y!7Ws5Pb( k[Zi`N՛ؔ(#fAz+[˚w믍|s( e [ 'X n1zs}JF@Z[Q'U`9?Å3R ]QvW|Dٝ6;&PV!k-wKS=҆Di H7CJc)] RWhȚ/]^_DP%h"4%nb2oPd"dh#~UXm%$+ |K:Q >\ig7* .->\gtzpǸLYjR:Я=cCOM5ƛ|ݖ'ˡ)=f(;;\A'hщN M|q89=؞j|cQ>J.d5|KrSmf}Ak0smn)`k|ǎuJcb8Ȁ 6pRLڠTXd8 /jKmf9 ̘2if Yi_{meڰ3 h lbH/ak-I97f!2C˷C+RZ˻{ J7ֿ;x?7QcmG>/>^a>>e6^y)_s\?˿ b'wt~4c7绝vSxt/6XvɚuJ][`l(Sf#I*2,Ly=?$[3n\~/nWT\z]u97_l'3/?9o-8b}Gڀ O}g[dIY9_Xg^zkFȱ?I'>_vM^Py)gϏ)x?S~u!ށE͡#/S}:E4EdAP]cSht6z%ubKџ, 1橺s Q,Pzy)6,͌&yq;LÁ/D?b*+!w j'vWv8]oe?B*[,Y#Kk„!TdoŐ,نk(dGɾ%ƾ3*;3ef~3=4<3s}y^u=P'(L8 Q\)ߦtY(,39$uH )-oV-<E&ϊ2O8yd g>|:+w $k0#,pEe6VlSx1{?|'J>p''%G-bjPwWھ8AdP\N썤PR.Ty+>l}T=ב?VB ogZ͙qkqF`S 1?02ɺ6lN8gGWxBѾ/vՇ (Y[* $S c7Rm߾|fy t~=Zc/pe]hL#lMTڡh>E_t 蘇SP0!kFBD93UM3' (uDǀ,3}SZRvʙP-dbg&.:lR~"`?#vLNsXvw*ߖhApڽEizl]i~T v>U-])rΰ;W3q9\ݛ6툊yX1'j! Q5br_:iFG:;sE؟9:;jQ[lc̆|~*bQi|n!eʿЧ$=2rU.#g17U{+; 3úF-Ilڻn_5K<^3ſɡ/kbv*\ `߯2G#v4 [(e`0njVeyq/xĭrŵnE1/#oܹj>ѿsז{_PV׏\*\ѥvvGQGdC7 AfM@vlA̒RSO*Vl KG@O9R8e`5b@.&Uar0WeL UP%i$8gΐGq ; n&IjAHVPhTR[,9 |9r]9g;h*#)t\FGH\ Ԃ_T5IsbRZ;IswѮ:.\>2Jh&,k?7dirA*0%o6f<<֜8PUzipNck;=m9y~#o8+ge~Fo<$u|Y‰ "K3bX9:τ'#u,Rό:P"%|N h*Yۊ<- %>LJ_iilh.G ORz I1E0|S^6w! `\0ד+u{)(txxgPB3bVtst~fYW]t\BO'R PUv2`S&2^ֽ/2Eb@uJ-n9UWS|t>@YC(ʱ=Y]Ԓ_+᭍שa^?<ѱ&zn I@Z~$=׀I*~qc3W0kpGcWjB_XvL{< :Bىڏ{#TFɼ!KHPk/~NTߚ)['Wn1*`YIYy-5bE,>%yD/H\v9^XTI}v*odcLPwlV@ՁSS6JZp| 6d4sq¼ހʎ}JQ%VN9,pjӣ:5iA1AiFgy "lʁXgvIAD2@_ @ArHkRA%nH߲& n}ly(d;=U@KhFR*9D]2;ӑ7a]Pikr01i.Va.&4I.n9e~,]*(EE* <y@ҭWtA=* 팢(j VΌF|d듪st_p.* y?gzhxDKͣ2Ոl f-*N/4%9T?Ur>R"hl'4L"r0ȡNr.0I8!& 6xZ4jqLbabDJQvBaH2%edeZ\Msldaڐ6QEZȏhMI3TŎ5; N䡑^u%f:5DxqJq>;Pn)'?yYd.-1'mSWg ^O.m)mo~hKTXG_9̷R Wp`I;/_rпãn+8#/>U&ꕩX8Յ ڐ1][1(kMw+1'w[Λw[dcoj~sV 3A*k13P3 |/ ^="сЭf<\z<ٜxuײcH$, d$2#‰AP ASOx:nP] B"p9's_nV^A/ V&]2|Alp&Tn4!ҨhJS;ԠeNRet.6j-bg8q@&(sk=:;Q(B\ W׮>O3z(2cj&vg Cx8$ёJT 癱\VƸ~K( ~q((Iy<+Xqm,7 00e)ߎJֽڇW#_q];sC"W('͂siCc5jsSSWLgHɇ&~#,"r1!n)##9rY56gq|q!jZ} _ŲÿQw luG?r.tWhKqQy&:'y_:0(s*Cl('\V^DžKdi&vsl(-鍚UQ}g d"(_3K_N2UG0oҼ.S/Y~")?f_ y/nAu+ԥ21PS$d7(*/l)?30z/_ZC&2'_9>v\1ZUWӹWkR0CVyHee U[ѱP2TdeK?ŇG2ը9rib̳3 ?$#>dVC0ɞNal#Urr& R U5)s[M\r6SrԗMӡKAY%˷2Li@(+L A'|?ƅ(?H''2-0 czVSW!CЏN-=5.rIH6\oBBs"D.ω=KQ}QY) ;z5+.'։'0!oyG~ŅLꅂXqiw_:.TWk ʊ ֡ڍ/'3Θ5qq0 Ӭ-rY,Wc6bjEvst1# X> h؉,pn뼏s;bT? <*kLH;K$saWu5QQi*\8,5P[=vxd,L#aKT ]|1ت?Ypo/Ciawm.&W>;Qr8\ &>:m#@t;eu(3Q'P3]P9 ^VFPVzo6 o M$ OOL:EieQ Dg n TS-׌Ƙp4Q*`4'\5n= w2PN` u2r@8ؙԡvԹHYB`a\B=h0)xDAԓL ULiȉ*x@/(O'zfwPCeSh &kC /~UBkz"NEfч:1vh3Ql]=?ZS#n,ۍwO`Ej%DhAe+fcxCI "%lJ!ٰ\TJCMBVHøҊ Q{擗E.Ou0<)KiT n$[@2AFqQ&\[98wx3c^W.qmԨlu`ɶ3-j$1_vdE+@\9݋բS+kH1HO͓J/X=̢S/<1mzĝږ'^ f:jI <1;}_ۓ2U 㒩541= "g5Bq 2z4BZ4tIP֒ rC8BoVX\$P (5~gX++ vV(ԎmGp39a8 }̲ cA _~S+J}8{=%'>)VM\3OO:3ǚ"A d/DŽT5&WLq\!eZA >rhuL LV5'R~Bl' VB)Dt73I'bFdijxr?` c<;8#7_(>H@} c1D0] A\ltD_{;6}O͆ y_3ir W @TB4 1k[m"D2iY_h}"'`‡"˦IWNTRa2J-FUώF]چ?آ03vfBdp6h Lܕ69 !j|Rd#nryJ7E}ֳrMֻTHbA> ׂgu׆^ O#֧ o8n#B-Y Wٰ=`5W_ X{۽{NFiti{d*Ci`KawcE>!~jAU)^2-6;0O>ݟ-6P:qRX]aLܛ@ղ)]4)y'WA 6*G c>&K0z7gQ;* S&zi*Ut'='CAB4?$!WS0G~9 4IJS8Ax5RA){/-.o ˌGA,s}dz&Hlt VM8Jʏ5DsXn/U/J.wAC\z3ATNr ;(SK&/O9D,V):aejsa8XV;G%;~h;-l:Ma[æljʖlj YDH/LLh/SzAQ<][:P|ENH{vI@ N*Y2 P }W. J"cINo\GX@ p5kJ RUA&2]G|QbshiߠuUKɁd 7‡$k?ɘ F]_91]Y$5tB@ (]d>frYY%GPG3$57BD@FFYsydgOR|MoTB:ٹ܁RtIC ,n7 7*WfӬy}$ -2 `QoMq]?nOU(w!LT4 !Zb"%ɳ6@#G]&lDȒA|\dθG6sd rid Ym?PCEJhTG dX!!>EO!Ҡ=K+' rQ3Ir 5L$V!5sU (td$q{ I} .\3D=2Ha M#mA:WzHFkfG V+. CfJ%$.|,XŸeT X1~3RS=4*&=΍:i{/YŹ:w:}pS9 +()qU`pVY]SpFdV>^̻p [N y #tԤJI+E+D's?`Upa7Ban F*zk#֧aQr\n\4?kQ^bFrq1vB)N܀i_%|RO֦Wl4 5U`WB?zqu5Xb(c<+iQ%1'߾ mG,Ho‘R">W1ׄl+y0a>g*} U뮽 S-G <=9IdTew Arb0Q&5 "-z#D"9𯴀Y/r!|!ElUU8|xpf\ƈt߮ps6D+~ylhkBaEJqwZ5jyゼgïtEY+LD^L\c ̽Jهg6H2 ohw޷!#j _ 4%yAzK%.v,RPB2AH>:>J3zfH ˥bMs%O.S0CTt(Ed,,m}ZRh;YS”1Ktl#MD!C=p刼`}{tʝZ]kRžƏq[) jݟp+L(Õ+_Kˉ?#u 'q2y֏^T!FwbAV۫q^1"\X"3xIJuyGg.cc kw ńWhH${$ny/Ao:nuKdLndž*=&hq06د̕!5ʚJKf^p^u^¶T˱9¯`.Ƨ*(=Fꙻo &tѱf4&B&ɭ>.UQL25PnA>"ϊ*i:LZU&/ TCyP<_H< 0NJ?E) zB_9{on` ͦSֿ1>>E; }b[3'톇w'켳s8kXqCZ`]5l1wev&֢&\]4wb"Y+ڳ*T2]J)smg&oYsS}Ms[OzPpnj ^$c]7)J4cUcUAܼU,܇5C{/U'*\ D6PM#7c@=z!E?XĪV}fm$_CaOY?յ_s:rZM.K3xZy7qe810L.=g"AOyxC"Jm|ou B8G-¾=1g<) gQěT顢>ļMSwǣ|}E%\rgMX+݃]ܫNd9ljQEVfz?m7 <5 RQKY1)qw^y&!A"p JgϘ؟X t^G~/an.Eȶ}Օc3QV/j֞Y" m\g|4E;r5.- sRfG9>Iz9޶֒p8>_&Q[5 %̷;hxFİ& Do4Z V i|`D?ʤŚQK5CGxȅ|s*+R%i=z!M6 ~È{[ Et:/x+83@5w"a)햗ATɏ*~#||ͷNPj4-Oq!Z)1꺊\$ Gunιb+@jTRG(\qiӫ*eu2dDݷJMU )-=drY<mYj>d.8f#W=tk c}yx^W{z^9 ̳,&Gl|rFLVeeSS?'3K\Y2eY/Ӧ[3&Y2 Nz33qY~t>҈`?S9\Ͼxq ɰZ\cs_y Eŭ]gT~ߕn%|+?v#3^yjbü؄Ë H?QNҌ!#:Wn4:rsp39<dx[׽W{qlVII/1a{E+De}Hx(J #/#H@enkĜ1|rsQt >)XJXL=*^B]<]PWf7EA+HB>+G߷0?TNe[񮽰jkM/ 3B~v|?CAl%#Zzer /KY"d]وr>~oMg U rRVlenµE[qj*NEtӥ޺>[X4htg2i?J}p`ng{pyKv~;V{d[}_pf9zy>_[ҒO\쁽Sᄇ;]ӢGPDT>Ƈ&C!f-1"ooJ; Z:tnrݯzʯ I==Hwa?*bPܞ-pPǻ"ΥLY~H gL5Z{ǹMsV΋c@ٿyۈeGe\RXn;"Ғ-8zhgZ&zƤu 8Qw lY;O1]wœgb^UƸ/s?g}_ςLJ!~+>f#j]|Si9<({t[=:ul}ngHsUNjiلKnhYMEDnsoܯ]I,^I T[GfaN=cZ\nߚ*bx'c|[s3#e>rq|\sl0r#X&?q "Kfo ǡ( ]M + >ir_ke$s%00D4n >@!_0f6}&Atګ*wo^@-nFr$8biH*7E?8ͻPҲz3ƔuMB $àPyoLt4Q|z4gN39YĿ7"#I$x f N)Cڵ7UXJ?>@z|ϏeHDGRzz*!)~՗vK::^{QnG@aGgb08!1I8b&hCi> Sh@V|#4=lL#b~ߎ0 2.Y63): aItq[)+1%Ijv ,OTՎ?v{W>-B>$԰ Ro Z7y(wV+˜fx1JyYH(D =gFtZ,E|,v_mnyK2bY-nxUz9 \S<uݻC”@hweeD"L5kF՛!^m2m^ Hl R.f:uCl @mY%DGGs~GrYFRu|rUը4)*T"{N/% _hBִah5M6[ v,gxˁ[p2{7I@2 Nش .WCFq7/s,:xЌRR!1/\GCރ@jlEaC4`{5a#B]h5OkHv\|#o/<<ه:?:}KӶŸz *_ey l~ ~g;̵eR؝ ]NYWOʕ_Br*/^ |+m햧NDXI~ZT8+Gƍk{eXm`fĆ Po.-33s$ =nSOQ9(4[de&uC?H@k>%> F 8_BS>gu׋ׂn-U`^ [Lð;Iq c#rw#<f`+]^d9;ftnmp8}v*5{#O7% {qs NjkoߤdeTWZF;2ٖ6!(EYj;xo\:Lˊ㍖78tze"09AAVhGlođ0?_J>{j߇wm >Y섾p=?,cߌO>Zƶj]>Th37'cf*Z5LF{y3Xvmc8a#O[=r9R Zԡ9r4`yo?>': +bߍ>y+/UFn,ʈ?euad2E1os} \{n~*YjPsj)"x{v?v?>tzBk't70ړqWa1\k~JDU.:ڈ3Hv8T?rTޚ(Hk|`w9jFUY^oKDC>2Ix@c`ċsq:XB^e"45&R8o6墟 ))RPenvNb*ZjWN ǵ8$)ȅn^Q*$e D\k c?v SstI~4>(2l 6P ̙h2ڏ|ƈ.~RǕ,T y*`%y]Mlin6:XlmߛDv!EH6%Ⱦ)p;^ L8LgHVr@iM2`lu<jh2'dzYoӀ?*Lnx W%*)< >B)d9 9ON(7뚇 Jkej}2ܿcG~U d<׿'t|d$ ?AEI=Zpe%{r}5Tm"zYڐNӽ?or"p==&7AR}ۏ,'IV0bUtXʫ5-VQ7/L}%FW9(G5/ jr6N"a.I ڷP8zjJRzۻ욟I<0#q.Ҧ6U(F^ldV*]'XxܶtZ2(%oknٛ@gbwu+0FXwW,%LNjkAq |t'lZyئӓ L<%^w(}!>=.xAȢ6 睓*F.OM#Fjmnd<~{Hu [z5w$.@$a.uf@7R19yv{^h@I-{۬Z1ǪCdbqK~ʈOp{Bŀ"{-5E~0-a5'š*;"P*d9@^&B|#;BȆk=Ra_t i!c°,LBcPk -|H/+aڐhle]NKН D1O9BrC{I]e@7]@h .__}S*?x"(<$TVv%uܶQ-5IhM;MUS4ǐ/;^跻HbYakbpǥ]e8[#H~jedљn4nty…_T<# 4`uxw6:#%K(SyV#:`OFrCg}P PX&M& mȶeSθQaHge-9BIЯӴqc%@2FEd`2o!B7w+=).Wa#-E64Q9xjώ<ގ?1H5i1dVKR{5%*>;b~]Lݤc&/98vTC]j[]:}u Ʒ*RV Л -(Y]GCT'N âIȵNM'#y'ŤZ1Oc-`?P'G&`3VnfK.>E-wo >O^iD;=^/㐫*/z]6ٰ$ PT+ݓ: ofYh\r㊱~שy1R"KMZ+'7כSL-ڨcS&|{Iat ӄ$DDlF].,qM,'Wvs1TD.2$jT T!Т>Wޚ, P0R3{%c\bV60y [=4aV.1pB#Yr|eqݟ‚k/~Jܕ{)5pRK*$JZy kVHrKQ -L\[@Y_lܯ Fjֆ}aF e\Sp$## K?!!W&!=dQ.bY 3Jw>#qb/`/Fxjs-u.oD@w="P\w[WNU r czE~. jUH l(K`H8E{=skE&b gV"㽢]'ymg9~մ)hvxJu\a2 BT:4e`MVj<ςp}m4+w wFVzEGb36|/NhC'|GY*\vD*X܇jٶ;0і-:ҝK'^!cwsr&w!?2{Grcm xwR6|m=e_VFgM2^u.ޚL@Q;R.ՠ* PPجkAsF>?3Y7q dZygN;AVS'k܈ozt]"-Wǿ1ыt_{PE$Ūi%(J򆛟"}X,sy^=D Ie$!ۣ;Xdh3C|B:aBWܭMzy d~j6QgCo dD&da:Y(=T!]Wv=?>:m50*=K~TF8f_E{JѯXTD]!&0%[:j={ 2xHy pOWD:Vƒ Ŝ_H+dztC =Rb袘iJF\^6 9\ gb|3ʺX0(3oJ~ѹt<|lF1˘P(!;LgBQMvP3= ebG>/,erM>|R7 H r/ΰʁrB 1I"``szz}Dj|Hַ݋lY ABK1D-_bcOQUvCygs*oԵPc-~B-=hTgj'[ *%2O1fp _侸ޗiUo(Trxig}Uw&'q|O)=bEv,^ 6O(23m‡C_E& M,^xVǯHмJ)AD_?0#Qmsߺ.lXzkh7vEX%@nn\}TQ`'mh/KhiE$ᄧK#4uTN~K`%ÂJ&:LQ[Yħl[K?Zu}Ğwr78{d%R|CEQoJ!;[䉰mt+TKHf=`lwRV,ϳ%<}[!5|5=:?Slo1d'A g1;Mm%9@oűff]Kz4KBy?z|H1u1Vu#2!S-'kZ)_R/k9G41Tm̅ADxjrѾ 8Z8]2p꘬Ϯ`!|<``c4\CwBQaĝ(d'? Nr}L yG;8饉o V)K=y*R27<$ ewk4Kwk\˰#SRځPì%/9Iw@)YdZu7-[w<0lgQRgHvڛO=Q )[ep}\JxZ.&;l!cw~+݇*OZ1P~ nL,cQ#!@R\,ǭ"fh 8heW!zw @nk=1|ms.Ӑfsc^WpxO{q+˥1GE/  ѕS0 h`p,~j[?U9٥n"(A8_+r-k7OK3km+)xMo~8 \q]%8ա/1~\3aδ+GKjf~13nq=B\4^[' 2 >[*F3Y2 0iQ$j+u~C L<[Ҵ˗{ɚ\I!{AbV˖V̀Rv/<ݽb߂#}cqOHثj/jkukXBÒ;9w.JjQ> Ȋ f*""'xo4%TFj*'4!{mtG\k@BofDWSQYvP*c= #%' lG}RxR~f)1V%C"*?Z /_yec4ejjsV "$(>oX $ mm 9wФq,脠K:Sy&Q:"w~9|/Zu_\ő}5# $/.[jo)nG ߄cԼ\m9-K%KbR TO»cFelE:Tk@?it|wAΔ$pJ[1svGۡyqE\Kb" dVě9c~X."BuٷkT‚⽙R|q!hdLu%Y]npR2ɬX͜3{xWzxuAHBYFG-y ̬왙@#-s%""3b19PDf S`꣣re:uIX~& U˶/wpS e[|'70=saY@ I)٨D+pV@`MRMD HaKT @]K!3%hhC8%zWQ<}س4Y`hϦd+v.e%UbqtofrUtsqd4EGHŕ:Pmޙ˳pٗ-¡'ֳ'T Vyi0Z`CmgOu7mHV.beoe$cbx!Jܵd /3> u;xc:Us".AoCuƿmxl a0l/)Fv V>(mpd()0q-u볿kwJ) 0!C9wq^0~@CYiAj5k>$1e]=32YDA?2yc*}zhQdTm«tD/N\K= A\ǎ {D~'?y{\A M4q/J U@I| L Zǩ^x֥1>IqC4*5s}' Mn Qe;`(e%.$7;|?NYU}UGBFڄys%>Iݧ;{2 תOtA$$j0q48K8.%UMh'SEʲQjQr||Ć2ne@Х4_7DZXuh e|0L7AlzHM;+&/W`Ѷ{%=.HUF 5p;DF\v%'R~Ml]ѧ @䴼%rv)_M}sQz̓Yً'-6!yTO#A8H\of=4jɵ+FO?^ ~Yu$ TWHgHYSŏnQe4^N`5B{ވJ6A4ڹ広h$}@Uq4+kDŽTyi:(W-@1@TČ)X~ m@tsQMv>~-y' F^i.8v}zt'fp+)a0ӽ?Tw9A0 Y j̻yi0di8̻g עCTccc҃aȳY3zX>sz6'S?nc}ЂPQks YzTƴWw1}\M>$(e1P p0iM6Q|($~ \ _@V{w\4:o@ӈ⸷pOeB]-Ui50* L.Yž'䒗[AҫkcLnj?ZcV`Ny3g[(&쯆w.kljuOj֖n<τcu$xcKs[|;oxr8 1H+j<}ߒ.~Xk- ͏U(a;Ak7 SXvs$XqV'뿒m;vVAGw1%Vwe}Z`%h9lq.RbD.z߲(WH*W|T Gs6n@;LwN3JsJ<#P{#x{;Ιuvn(.Ҷp:b}G`e<]褶ھANx&ҾC)vSkP,p`"qQO/i)TMM\RbAZF}ƯEZEAMw R.Yڿ5=K.9{roȒ E6!P$`Vɹ'MNg}(ʇ(_`Kg~۽Zt%G'ao=_QG9&Nm %rL()3l#Y*,pmmU56I%.Cdll[>7i#ez_ƒgEkZ"AucPͥK{",&&"9(& y,V7*&ZԻ)&q\]3csfEr â,,/a>#PBKmȏ+_>?n|Ӧ j+>rYMWRv{-zmG3> '"m_9VG _{v1|~jzX3^JƘn.7jA2-S[/,pu%q'*ʯ|to:hQp v6=]ˠ/!uon(Ij2K "MЦikF[?ʬV^&'yձ;NKRZ6t3plڗGJ@B%?Tืgu]rML ~Th=-$ڬ5Qx̧I͛\VĴ%SH],6)kxʛ:*i3+ś™c"It~ ?]Myƈ?Fޏ!Y_{@r.-7X & f~Dz6j\[%HH֬oe!ҘQ2v= (ˇ,`5e[?`z:XRZ?(Bg a:ECſ rȥ P&c!f#:ߎW8^oƬr;(y miP5~-Sb8 [Ͼ H '4 /U|(Tg~t^Hf #|0mԇs&˓wD_*?& ~vMf;QGj ԲFyKlTɳ7a`BF)B~oG@=_q]<ަ"5< dU9ٖɻGIa%dTt2D)G9P6'(,f1QfBGk&vmoeegʹYx`H&f+V2@Vbmv|"4=qM'qR2ڿ|PUqNn~C E<50 !_=ckiP͎f/Zg1FL"2Kv E@AM^'S~3j~3V>*n 4`:/ㄕ,Q%|#b4~C1)x\+V'cY5.Ap :N!W8X0'OOe+)Eƺ,L5I7se6'P39]O12]qrֱ-gֿx VǘHuoG%bt2_b4*j2^/Y=^mS.cf˴MAٳ%EMdÞ 6S.qw 卂;6l/xݣu?'QRHkm iL4CO*@3vTK`>"߷G>BIkaqUm 64g4ECAuKDSvwz)*N>+7#}of9NAhDki@_"¡SnۗЋ)vs l^<8#z$bt10 QdTjI -T(y_u룥u'Giќך8.꧗r5o:Dmg,tQm'e@yK(ŅȚ5o&3Ħ,3wGU}bX͵7}vA;>Ug}kO-a/s8jR랏d݈G&fP[ ^W,R" .X\Iy]+ ; tq! ڸ$Ȏ;b{!!؆OhY5YPFMUC"|J D <=?.55 Ea۠`kY#iVHpa|DWe_儸F &LS:4ّA(!7^Օ?d #$h-A\ 9о9 ñ:1)vKeJV%ɦcZy_;SMaږ#t(ɯ&̳"y%,a/>n4נJXr6V;#;-ȁ<]ecb`a]ۈ§aL7!S)F&75i!xu iZM1Q~?P0J\}Ш?l|ue#[v^C l+ &SSG Z:{o%^p, V/,7ojJXKWoE:M^䛚|.1Úa_2/& k )lNkLf:\]/ :"`h}T#+xc垄uZ\~Ϟy9RH 9w{v`)C-7^he⍒f.JddyN߮β~;V*Ȉ4cqϦGl3o>Ė _e/`MAd@XJ$9+DlȧINK\n[݋E Ӛb.F66JqgDmL(fOGKOPړ ls]y>N .vF P>*5gc*%?ONE50đ+QXXbP'DA5 %| L)Çor[Y"`|BFmJhiܯr0>@|6_Tbd55 vF=Z}OSeN֛n47x(uw@pGC,i=9RLcWDJ$9&7SʐSodS:V0"[kΘTY-ggDVyVٌ]< LFɴpP;h5+ (OW/+:*SB)󜊃X֝l iRvzF3OoU,΀|~smz+$!+ߑs3_|:5}>: ޔK_G׾5N<ٻeZSsz;wtQnI{r67 ,TPw,%Dg-e]]JEgs൹#,S?Y+@pvA9]bERn} .|uB? LRvysT4z|:r&c}.صQp"~v-\N 볊D-A3{ur1|s d_\A[f3tX95 9'.{S'1<ԓ)<߁'!qjHqb>\XĪ1 X a2;'1|"=#ά>#,ګ IF|pI-ZlΓn[Aϩf^Nby(0߹3>5zxK3<`? X˙2gZāx\ kTCpt|tzE ˒iљ @oVe*ݣ/YiP= QhW2q{IQ@O0p'XsUKb-Ss҆=q?~/Uڜf@&/YG?,6uX`χHʘms5cO>._]ɥysXZGǸypZ)-) Fi"tۼ?{[ꁂo;\_rWW{5bU.$z6Fz8'*񒒵nUC! &O$]3qUuFf XiY@yZ:p,rS#R/PyRD nOIޡD2zxЏ $NQ/b?#끃=%̓\mA&u*}K*y*5lzO4,m ߂vY[XzfQ8y}~aB@r`mXwƈޒ#g*ř\IG.{Ƶ8teZOLE j%bxƺz ,iH>Fi>愆K& 2cUaǺ魑'o]YjGƦIE r%>l?g~F^Q46AI4ٵQ Cb~vbU5ސ݄c ʔ>$[׭q9_w̲d~0tkv/y4}nJ'9: NZes7| Wf^T.QcLvua4o*8ժ#{/aGZ~3L}$xMzIshtGZ TȢR+s\l G^3zԹTQYSXz6ږnvDKz&>ugy^uRpqn{B(b#4(WpYފnvyP#s\ɩI/Eŷ—'~δy/n?9( \܉d`_z!.W[&;:pU׎IfSz,#ʡ)ȑZ n-Q= ժ^OIAaDҦΥp1`!Bg@ >ДȨzȮ$<%Nmq1b_w!-mhxwm(-mAb(ٱFA\da돒YN **Tm %?6%;v]eY#j;5l^!j#w** O&>G~Cn|gNl$啒e d|#Vo6~IBs3-!:Ωu 4,UlF{J;y^+F!,Ofq8k}Kצ\"*HY̰Eg!Y2q͡prpΎP5!ݧQ.1خ+G-Q4{6cZ}Qhs H^nYa86i.ς<.m]k.M~QmwԵ: dvu>ݐ,pvIW|]cPK"#玪 VImBٹo~ cо(ڊKơFK=;L^ڎ~Wzhғdv9mLw-ufjGid}vwεR@;ϧZ@ur,||S۬-(="K4ח{U9$9?@i ȟT-zC?gsbY @gA3N2L9&nQe+Hpؓ ^{6<dƮ&vX: Pmj%~#}jŕj (]ZD AA@׀H E@@U:{ {ׄB$_y5fo~$n=ܳ~vvВ0|OLX&fg.a0fމ(#ef;W<nfcWFco?Ù-}i,:e̕,&ZpeK^F/=iP/} VDUsyFP^g,| wY\U9wtLZr\~T\AslW$~6Nr0,S`20/8}- 8a_f~z s1Z|.<\F0WUfiJ|z3 MXY(EօdD|`ii|V+Aߜ؍vPU<-Ÿ{TE@_V?!`8N \paG'5}p5g_S4 B8W]ht/3Ÿ́CZ<( wn\,l螡{jUᣚ؝# ݂]rLNk~D70qY ThBǢ=D@˓B٬r$ۯu**ɯߩU_-ӤGBUg:%R! B#C?;OJiS,6񭘄2Ĵ`H `΁BAyo 9ؿp92@|dkSVfP/ 5[ i/fo^(rp#5֭{&~2L%'$4@BT.Rt.JC'e7}w\A$9D2'zB2%K e Hv7yRzʅl7 qnkÎ-Tu[ a(4¨vqO"|s0Aj;gydNCQ3B's:Ƽk]|#)|?S/bRz2iQp8=Z1)-:Roੳ"}p)qٚ_kSa* \Y#QWIPso<(3r3܈j{ fm~8&+/4޲7y=Y[kW>֡zS[~0ٟe(У U_u͘Kfgā2߽Hȩ}h Fk疘<уb,W}FAl(+n-*{Ii e8~DL_8֛o`hSA'9mϾDQ=4e5%s`9\]dt?} |$w&Z)nK<=`ymk$nGzra`@aZ%u&ifceJ+#ѯi~_f2#`fRW7u7,Sb>2ԏ,6_`& p]tݺ${# ?D{Z^CԍZ &;{^੮RmyEx>Z_n /PBnXh#ezU5\eRB/oOúg0*ћ$+!,qx.ۭ{^ng>n4LZ}m%S"w#[uN߾kmXapv10FM7kke9~1j<2-4E~gKr҄n[_gf4i8Μq9ƳMЎO(IiCmȨ 2+4x4orwqzdO3It`VJ8[{jmLq!C]LۭiZr I23*!-ڵP~uJδ2Dv uv'7yzŪ4qFC޼, #~Ix1{/94N g7afuU/( nBM0IIB}),}"xt'l$Xկ`2n:6&]mqK^XUTmsk/cĝgkicE!}%nER@o*SO,J:d89V, B.J=TEu?LFI㭌6ڭc5Z]|*>W?~ڭ c|oen >H@}+I^<{V[YW-k/Fڌb;I5LvU0gzVwN"ۈs-oˋ&i龈B4'M{;߶ ؂oA7[VeeJS(#ءm]"diA^BlǼ<۷a ,DvAuLu?l01%cIѯGD5 KH[Ji_X;PbErc0$b{}zRw;i ,5SFJB#1&>)GELo֘/=&ڐt׋sotO`S\{Lx@=!&eQ1^w}>{0!r]%,oB#YyLLVta+E *œpb󚌞~#o*΄ߛ UlbKuuKuݐݫ?қNSDayr2jM8@ (#:|0RԞAB/M*MgD$ '> dxlJl*?AO2>j! -3r5mew+6=w!sIj701|ˋϽⷫƆ(f5 ӭŝ[tw!hQYmq){—չXp,f."QCN;C?,#&~aJɧJ$WgP@osjg5-(y6l$b7q(m8<,mɮ qP,U-BSXQ\Fn F%V l7q|X;k@⻧+Հ/נؾ%.k+!|̲}$@D ΅r |Zj/44_N9yu8R'x|Mb>m/3Dq'\V!}I&wՇ (ۿGRQ}H _Cv9{p`jZ\uؠb;Bo–kYuתsAS,ۢNDbΏ9[s8骪|y]܏̿NtjWX9~cevO2\-5fF,s^ w^M-ci)R\`WZ@/v{IҋhEF8G0Ԥ1eE˥[ܣ0Co. gq4.ΚV tI90~r+dwhgjt ̣@R>H )jp8&exߦz4^dwQͭ.Cպn  ]só?ag ^ 8e DW]@y~sE1Az^I(=EBk签C{Nb5s8WsysDM)ri6ѤNlX+ ;O a]0e Gv"hDӢїBL}_.+z21oK~Vxe:u<IR<-6Pl2JI) odxOHB+#if "ݔ`(.9/zm7KE[lVlC%bLu*#UȢ Ȳ P+O3>1/iDU/T |Dzpk"ٻ{Ol'ዔQvU\P SX`܄jJNe9&Zڎc_s3W?ڧ9dG iCO7wǝ#Cꭶk/gx?_ ζ 8_Dhek|ޏ,ts1HӢ0=g[,钉 ?z8"r1ɘhp?)dZ#Rt :J V,_Y8|—/#^5"@fq,xdj]ke9\PڋKX0G5mnϾL9{꒤IAح p44ݣӹE-.$\7;wd0$ uegOjf^:4-y?YwjɰLE? J8]N5^I-*8-Z}sbhJxs[ * ?6O*v(_^8䣍%ayɁ-eV3zZb Wmcӂoy/ʼ 0fяӏxWVALnos-v"|1ZK9ƻJ\ .&;6hi}XsnUPB2vQDpCœ.qr ^*f7b0h"H]ukcqOM ".K~sg< ݘ2^~o3ʼn#TW1QB(JDz".,x 2n5VN:vgجxpmA6) ]-D}E;}ߢu+70pip0Xf`{M%f4p{|\Nei~,m#-/+O+_i cLϐA BD@E{m4<7)#|L?|/y`_tc>0WPb)- wfT휂k햾u y@ew[u@Q|h Fp;[r5*lXI:QV]Slu2jX:*RD5Ë|a[sTz&R:21G :sGQ[{+U2I18Qin~\1y+Od3>R=-Tm6;w (OdVV}:hyϮ\LjǾvþoKcf4̄nR6(2'ZK8*XY{",~5!gg̀gN5|$V@C8ne*BX`X`' 2@E@oq򨽢Nِ&vOlH}h$1b;:S cH r 0q{i'߈_f#ےȨ IEMc>*.l)gVjl#;Fԛ$U eޗ: ^ī{O%pr͗A-]=CU YΦ'lNNܩƮ{P1]#t<%2lóp6B:eb*Z혘aLV"Ky=w:E{!Rg]5\X;2.ܹo\~xCʈ7w78Qs6OvFKc], ;@Q /l>DA7i ŇV<=@O8dp2lOȯM9rYh=);kBEմ Y\A\`nOtrH-ÏFFKvRVQ-5G[3̏KLGSZSnKd_x8jڷ|/^/E9W^z)+Z=eEICdG3O&fzqF=E4n-l>jTqDB>i4ڞR! zj5ew~һ7M?г[;߆)請bYgUq ]ks^]69!XwLA{G,gle1FP<_px]FS3}#+{p¸i;G 9a=)He2i_moyxϔ-H`BFo\# 1__@9"8ࡋwn] F=:b5a9!u" m$[[+`gg)c 'YOÇ'V!8UtPXrmG܍BtF?(™^kG6a8!ֹy} lC&#J'O;j[$P-S< ;FqQbyMz>i6+Vu z_d6h@VϺl?a]Ш q'@J:f4B~6 <8@$ (^6 Srs$D9I08 K4n=LMo'a{/'cO'my|ё!~6.ڔm>G^e4H% tL{?„ ~.hĶP,b]?bkȨ^@S.]أieSi ݞ^`>-b)Clhҏܷ2#Xկn/%'Vj1Z!A4^[<cS).&@}<+HEֻIۋw|#7KI9'dARD^qU"%x?ZSF8/yfަVy-9ħaa}_*ņf8!jfY$S?.IfF/42;=ZAvc(hkE6U K0F;[hbIʡҲ@ӊg\vW_~\Rgm !2?2pd\jI!;} dF\W-_\PՓ-Fǂv%M8GJm0vHYޱ) k|{4L.a^\@SӉ&D@+]'^^ML[9,ȗü76y)Y@GP4MdFbف(ml3_Wu6*~f~7j4_ێ?<tjB,&CZFIE}Ur4MBu%^{p}[uXWQBR`TڹӴ̪eϴ8r*zwbKC6$}C2 c_HR [o o\g"F~Xicefof7>HkQQO{-5;[(_Xb ~L Vt|g}QGDVLX=D f&Aqwi~VW<֪*pqҒؓ'E@:R`X7~vKE*!*g*Ϛ:gUPZ΍#GF2pDeÇ)D' TiH,^Wsa1IQpuEϓ7څnS6Pww'!Zj]S}~W.8%=@?zp ʠ0[{`r8V1<23vgo)]aIf \kubn@Ԣ! ֍4 Zh_J?42:{G63[Xz5!! D۾YS*z6\s٩5u@V̼ku'F6,Vlb!kL (C<%Jx^{pьm<ӕޫ.9WP|?ɵ6Gjۇ1h-:U *R}ذ2M}l];kZϞC34hs:GżbhM}'7 vQH7ݫ3 ,WcCRJ}dMA@wˡ Л0i&xu!m^2|yq<^qŠV G" 5'GD:ĽƶQU1oDq$%P>'41BM=^:[?GC D[Oq"5b53{ҭHؘyL#ֱZҮܹ[D$%W}* +,U`ޖ.S[DzQyU+ +"@62WL+Z􆠽Vcf w0"jqndADqC4)H*pTO ќZRҜOC1 ";=:?ryչy!USyv А+42u1v70RB 8GEl sJM{& aJ"hkC𿯓q&O!36LƗ?A,mH MoLbB/ 8DF@?" (D` \PLkdIkd@U!oזQs#GZ+U{$v!~ß$1$}nT&i7:}qɚxub͗ZHjP+!u}UFLaF0s$|XۤBݱJvkPÄN!ÀtdkPmN%]s2/WYȊlXX`2h7yp` j 9 nABH;@ ̫eI8w1cf|fzڡ_8Ryg0E!|NX&))Arh #YLeIEɚIn@hDeL,3aТ@:|ۉpblwGh,5xo:}aQcn_@ԽNumeoy4W¯B-OiZ7RJSlYkc^E0AΤŁ{y:@#;_jVz`Q0Ǎɺ&iyB3fH*tHˆZ1`բvl0p(.eȸ%/x ?y|LwpIe%Uy*\ٷS! McM Z##vg|h9 .Yn*LQt5yʤkdO~ PSP{*kn.͇Pgø+C) |KG7f[H Bh doo*ě9TGx ;Acc B"^손&0K1&%8m4ObmAayy60N>z쀆~*:8z30E8N4a p"_t~aњC6` ÆGoORJӡc4 Κߞt' vp!5m~2r%SiVlS\1בּZQG8Gyt‘ h rYav3_#ԵF|{vv^F ܿڍxQm)BP92j,*>>yKg;FvSbSڹslu `H-\,^Z`H٣KW1H)tPvLs UD݆7?R/9l&1pBn^v^(*_-(~r `c Ft;9+Rhih˵uj?:`1ft)+(3AޡU\\2pZ!5qMZvجbIbS8i*3 $@'keq[yk>hy0]sQ\U&s|Ѯ UÃ5>h;x"-ip+&8c|==<:9ӏLdm:)e`ZnUq^;+%/V5%Zӄ0|J'*7g.BIaïRa"OdތPeedM8ԡ?0Fk;ۉ]2'i2 k/0JB"DyK2d߇Cu]D`_ILK{ 9Xl'⎳xd<5&E HG}f sbiR4-' +r-Óx6.AG_UYZ G6cZ6Meݞ!Fapqtpu4g EJdE;ì q,7ۧoTe]e1yˇF X!-]: D+#JU!'I6\\^XVοq7-Íu!~wybA*7xi0zY6|҂ZgWy-p栱7T&~9xp(Eh|t.dΨFM'ݝ[$JQVjVybAg݀nTkLPaNo"Bq@7yP?r;"~Emk[jQP[!s>/ze*|;|< v"\W9W[CV+ٲ9S:FJG@1iO IPm%pMO~u3%O6 7.ͽo>g?(8 :^zgJM4Jf5D6Uyv]C%:kCb`_ -Hb IC˖>&j"-y$N U<r[.7E5Z}p8 .[8zCC}UCaǗZ>eSwe'Oq/Ρ&D@k5Yb ^*S)oKfkqxhûÑ{2y!H _@Xڠ „n"YzކߐX<9VP9,g#eP0|Wǽ,&c7צW rz#1\q1NxjUS`pAz"ˣ7o=u.&Iz;I0lFqk/O\u`qcH/ӌ59PfL:P{*tKgrIlD9`+Q l94dVrFIip@b^ػe0Q?mm} "l]_Et@MyHZd;3YLkY P}4# f Z9&+GZXr>n(mSKQD?l2(DPołc[oS_H^$eQ$6uyGn}y@Y |OXjdm^QdR> !gȶi.У+՗;QhZ";ou tkb鱵KVfY :;ݾdR–` e52ꇶwV,sD+C9ҏ`Њy)T/d_ w AExA@MM~F) ".72`c #rYVfqu Ӯ#h[, bT7/L.~0R;X4(rwrX /Ȕ$/5<ڒKxޖuK9o4\8qٰ)FDrڥ gGp>h5OpxDž7I'bjwg%"sFE5??&94dqMV>y FΜr|ڈ;݂׭]zOO'*{}z׋Fp9WnWdz An7NdSYc!z6̙̎*jܚ4uۼ->zexyPR9qwO6ys͵ܻPD7suuHBH-QG{e D<Ȁq?y&akUS]X"+-k5=V`s[#NƃF#۟k'[ё #_Aӄj A5PLK,)wl @\2~};CSi-kx{/ F|pa >f" SI4RB(_$6hz~g])I2?Yg< $IomSsso\dSn{*^ \ p$Z(l -&#NR1ieB3(Hrٙ^FRycB'A[+!f[5q}L);4R[ 60Bˆ'@g 'މ1KXpz72w""C` AʛmVǕcCzϲ=V1D{njMuQeC:t;-gKnG,>|?GgŲj]43]2PM!>MkI6e^ѽO=?3ܘ^; ?c|РW(=HOU~Yuh֦~)(8O?re*Qvr mjʔUXƀaźE$[ا/q@lY"`'õCB AEiCݏ>'IT{:84zS2j E ODߠ0(Ȍ7NpMؼ\ Ɓ7=c'*_"Y:|!u} BpSt{؂ٔj+an:^d^ ~Aq{d$1.\!UW<ɹcӾYXg!Si^b.CCVCX/;{(]ٻ6}Q&- ?>M?U\o 9=c~!|uQ~1 9mryЩ2Z}Ɠ1Nͧ\R[A󭁾2˲cKkKH2r) ZEa'y\ (Yy3WUuyMN87p!=TjPU6=^??ebkP\vvY!-CFSպ lA dn` 6G_< |e3F}(-? j_ ئz7W)#3-)2iaK4ToE%ܘppay'$ *7 ^e \CM64oFWܡ\89yߣ4 8 ѹj=+#S+"Um!hE]<3k?R& _Bˇq۩D٫IMdJuSL4K_WKkT`J \GYCO=}} pQP#TVUˇgMlD+_m z'6w|,?`7Zn=qxưtxZBG懼t\У2M ~:2ߌ/7ZѢ@񻟗geʦ8$(k{o=nePoTF/S#f ZE$)sK'0o5imk25`(;t2ӹ̾Em=E,[M_]w7D Q[NO E=˵wLP,'اw{ܑ9}A)?#y]'sl^Ɓ_=/pXth  4%^XygIσ_g.]`]z.9A;&Ξv:O<(Ѓ=ϘˠdK[ؽt` y;pӕ*~wnڪikK%|=_ Xﺽkzx0 xvx4zn1"x(?˧ zޜ Y`1\|0?s)oٓ7A+cynpodKpH*ҺRcWd)AQ.`$KIK'Kie_',{*XV?nVܜ~wӶE *6snmptt_split_072.htmlTmo0_q Mֵ{)K20i+V4UNrM:v9'mW =/9jÕ:=(SqGNg ,,Q4WX[ b/yx@X ܂%Zy9J*AEi#<[Tipq^nxz.R>:Gm]ֶ./ߪ_\ 3xDsј:÷78BkRVr~k?n`Y~awug*NsH ڨ@0\@gn]dD, `q40EOOOå &; E'<͑hcdb 5E5d)ٵ AN1\(\Hy@%e}K*%O' ~H#aH.ZүWӫ]KN7aV?)qXg)ߢΥ?[Y\B_ę(RT]@p~U8Z)t"@@c { .܊k:~1|] %_ D[dҭ`2m,O9f ǥs>H˜+,rRv+`2ː%YƹyJkMAC|'r ,sE<4tnqWXbMݔFOlgY[DA 54G R6 * (|C{C:߄ͥ7a$[xLݽD:js.gm:h=h ʤ/[Znb,+2 TwΛEJQj~{rA!!Ȉ(R$^y2"k [\}3b63Bcų };./&;PKХU^kfsnmptt_split_018.htmlSYo@~ϯh`AJ+&(Zۃvw@ Jol60AmDzkURBeaF~%A YnmtM?xɉ?s9)rP|!P BYT6dtB:\h7BUU^pyMoRzgp^ wWٜ?;*-C-ak5 .h\v!R[@믧2~juX mam5h8F lQmT-C(pzĢ\ KSņfRYN]o8 %C1FXj#g JlGQ]Mh3 ~_/M1ZB|wK5;8qįoҶDVލi<{<UE~_0.?CR\ݸk๽ns>ZU;<4JX>Y%ԿJ+B K2~o"= 5_h1&z9YU(ċEQ݉yOADG1łh!PKХU@bsnmptt_split_052.htmlXmOGίYBJM&HƤQEi}7W۽3A|(iU㻙yvyWPc+4&M |LeNȍ\4ENgZWm;Κ};-B41 vݠvr; 6031Im(u.pIEѩ,#\(\eϑf.,oR)K3}:0+ e::p&ITL3˛ن1"40Ιorh*J.5}pS^ y\1&Է.6;y$/_YiLɄz.Xv)TBD(ADzr(a&>G50Ƙȩ+h%uZu2Zڴ`AdwbvaPD$br u\Һ2s;Na~8leVV Őq@/(v{\߄aTGuaSHJ>a/T칉ǵ4.iB[m<`<.K2$i}1d)R )B1MZHIہLFbHN 6";tS8K ]^^6qZeTsmփ?sOb7Z_pWqnxmow\ TSW\HaRTx:B&R=s'ʵJj/:JL{R)j$s=zu$3N(/FZ{y.V.*\j:c!Ն㫄SG0*a#OGx)-*~ڳ2iOq_ *.>9zHP"Y f4qL=&G(zG\>6@@X%XkC~rf3KڍZ0M@(6`LI.PNk ^JRnHkSΟPKХUV4*usnmptt_split_033.htmlTYO0~W -^P$ZT`[A>The±S{G}=m7|s%U&h*nuN Pe:[ ZWAT91+0NhSp1,9J߸mA cVBIVbv>C*9:Yjfk]p0\KMc tKG KHA ~zBzja:OKv?H65>I?o/^$1y>|/J`.I;ƟRq:M.g$^BVrcE#)+14hKDb@ceYˠ48~#/8Z6Ẩыv$2.ŋA[se iT}DVcɤ 4I.as.%. FWެkoE_}ml_Da%EvOYv4G?"QFwפCi @%uMܮ 3]Ur!1o$W9ZB) [꽻~}⽂lSA"R0p\\B-^}3@ͳWג!`UUBvsLwuU@Kr3rǢh\oM*uB-g7PKХU1/snmptt_split_010.htmlX{o6к%l9vmREymRg0 -TIʏ}ݑ86HIxݑ,%S4s t 3?;x R;AbL~hfpvJ5i4/..s NqˌJX'05R$0a:;2񂌥";w\R/rAr*esP|$ oq>}Fd/rX4@srwD3#J{%_Mʺ7dhNvVNOvWN`/ :943'rqOK;61= O-P^. )F%2/_?!s!"b@hx"lV3'>]7HfPL %5}h RgW1w9 6ەe ]vgCK\ɢwh>aTŚ`zFnn{XPeL` l"5{22|N1baGYE"EV1<9prB$ai-(rͭz wfӟL9:jN2Bzzs`a?,QR ܂ۣq#Vߖu Rv</_V^0QxN=ǍPF{h4y||9;<LStbn-Bq{&B l @翼|IS*"8 mo _w*z,D|{r~( ^x gJw% ?+ ju z5v:1G/bQVc Sudjͅ1KpU,–L&|>IUfĨb Ov(asW+.bBS-)UWRLcO7=Uv<=yvrqzvu'P"8㌑PE4kI0U %>"a(){o\_Xꖻ[!.4V&?PKХUVu\\'snmptt_split_000.htmlms8Sh|3vChNh t2Ė\Ir$K&&Iu߀~]iW"(+8wR-UN"3:$$b៞.MJx[ -ui]/QNQxWtՅĠ3J"]MFC0' dqfUxɭmy$DaˍR|C;x9]s0ȑ*m}ό~gۭ]Ew 0Cݲ.7r~$x.4?yQmDY9-!H!&q"DHx@HF:K;JSiJ=۶rpBe.` y*E/>zϷ=Ӈw]`U-5Ui%~a\0J_Խ.4$PieH@vQ^If}hI oe=f[CMXC$ 4gIȏm*QCg<xT% lX\#YBccH=v /DRJ #{bKFkRG[T1șdOj%a(k9Z]q"W\vEЕP!K-D^k8fKre-]a@e:g'ЩA7p(-$,_N`hU'YhWC٨O1.̿0wB43U〃hs8XNBZqYddFZ<7N5%L>&G &7 $̔XY;i0FLÍ v_1GtO1rZt*1FU綎4jGBRb]Ȟ/SQR0i_nCY8 PV֊sޙnzHa)32lƵZe:g4w0#n-;}v=CjOUJ";a8;8P)?tEӍA,tz W"W5+In(C8Jga 8ss|p{+_.KGӔFvFi!F F`nj{d 5 1`9{,ݺs+]\?=:)E4-5 v{Q-5O2<[wٟ}׷3k *j˸>CTF'SSC}J}} _ֳ[icxps;p02g>rpgV"WVeBŕu}T"g>)9S)o3#9Sm:2Y6QFRΞ4Url?`C@qb駼CZ,5SfxQvZouٝ<_ ڿNSnF>>y_ 4Y ~{FwG<W-럫nV"ζi8:uO9?ڦ }@h>GL|Γf ~ҧbB!q9s@9+*31 0U 1h M4;hT<)Mr;ng5ľiyKϤdKZz S3R_I@mpem"_Qg}V97_jPKХU5w6Jsnmptt_split_059.htmlXS۸I&)ВKr3-tp߇NQ H>KNJvbBK)} XjW] ~}0HhPE:n_iAbHk~.`:]=;; 5$(bZJpغC:oAEe??XKxt>HUoE~ :/lBRi*[GX/ \I/ps_КHdb_+m/W?8(Я:w!;xalFD9 RCe2E XQniAN,1홸[OVNL4+JXD*'98Dzx5&'g@TdY3fzy<]_޹B|B1rt6Zcs6V^~yFy6~vv7cR9q.i'`0Yl" \uI1T*i)hd>NY³)R+*aL3"R $M%H6} E>ӳTi@O!ƺ6mx Dh ޽ B|{5;@+`*Sdv7=.iK hm*ÂґQQKAȗ3J0S'e&l͹! 4(*r:AG mL~' fd6),uA%,V%Atp0ńݳ^=}\c\v"]1+~y&Rp?q!R)!WC\+A1b^>{GG~f';'zcR߉TTSd8l ^?µ%~U:vN/{Ջ\g ثMg0Sڨ̶HWRH7OZUlc пzLLAUvx-ÄT%uEoI_UxSm{IcБEkßJP ZT1PJDZN҇N\wL7fa%\.45?eh24EytgXޒSyXN>䝹H P~)R|RwYJn tSFZsu ?^7Q|kwe'qpLRE^-2vJ=׻^= 6Vp_FxU{,.Vʅ(DO{VثeElCeb pRioNO~auqzs6}L`Lp"B & XuNi ?x98l~jk{k",R:^F{PKХU/9cg snmptt_split_042.htmlV]OX}WH$XN*E*)vB7$^s?H_$iJe ̙33gN- h4dRO;{IɌLGUMY||yy/&$9/@/@ 5Zፍ 5ڣl ScΪRE κ^oB.`gHus.i |:3s1E꽅χuޥo-_z~W84%NqI\?ۈES}ͥ3^Q sB %#XTB#%^>f+mj21ԮT GZHKd8I4HDkV6Odu`ܚ ]L;Ki e4Ob1XCAH[@pܷլKT* *%M?ɗǂ.9w%~1在\6@3W U3w4xr;|VkZ-8g=Ϳ &"}dӶ8Y]z=j(9-6 'Gz!W!U *Iѹ }T~aƿ.FU|2|î3+ јΔཾH1eb3Nz t3e;D5&PLL&<99L`AB.8H_țW V5ɳ4%,Mh>Wa_ 5ӄt(4b};OMQp*/MJP vFuYX)&][#ڹUw+-*4/72+k;>2`Y;dYөX*3{[8I4t:/%xY J---"h5W{fPKХUTc'psnmptt_split_026.htmlUoH~_1K[H&BqU@ *A !m챽z׷on(Exww~l|ܠu0<ԩɤ.Ϗ^'{qIT(j6l_,ZN% ТqPF+F{~|^~V ;Vn$< hÅ%kltܯiJe5@IՔ" KE?N\M/7~ /WajEGl+>v6fs9jG [HKaq׀>`^E5_)t%[ԹJFȧI(p֫muS㹡vJ8B*[ "a2f|6: ykt;07@Qj zW?H"Cȭ`|+Ӏ+M7qTv] :Xv)kHOE8]or)S;;V|]su|RcRwR.j.+!X b'%5 pi\!Q:-_X^?/bD܈{'?_*kBD~ysEjˋO&JLL}So3"S ]XugR;Nen܁DZN\öXR-,h1m1ոKOAI39Q_ݲjPx\a, Xr& VBGջwGcY.'}#'ꕧq}~< /,8YC,9Xado\KZͱk%̘[DYU*xyM }O5RO6+v!I%RsL"c8L*S}w~7yMӸ{dŹ(s@^S%FP2:8Ȫ |S 6ة{]!>Z=I֪Yfi:tN <Ϳ,*(gC 1IHDiXc[JB2ό @Iqsz]LH!Xʗa3[2BפE*imj2:UC&0Ӵ#u2V $ XOܩDO}e;/!cU"a ^f0(4TD/$2P`j %60S:g^jKYJ)2}J^jԕy8շ3^UpfF3r&'058'_H܌LylHȸxai40r"$$[8<0<@`0:.ʄ4d :tRnS}EA(ȰL2Z}2~"':5T {^g~#R@qE((Bu') Ob4sح-f>,WO;ls\yE=@ԭw|3N"Êz;Hm݋ g19Ǜq?T`q'K1*-`/H&*z)VE)xP.N3uaX\Yүc0)JBRF`@~bAgA2dcF'(2V_,d\JߒK'ՄJo8 ,Wii 'gh!N;PYCA)TNekL&yT](gU([mm*z#e|-VMAS4r8F"56yvf >:U/K~zN]󅽗|jV<*4Q.t8AcO1uMTfH4y_KVYf劖(Xy q@"@ `= M-Waٽ-'@֥gÅ\.b=eQ-(cQge ]]~`lXѠnkmKi=5bTxpJauipsP6.(!ԛґblK4\So]R5djjBT5P]AB. a0=rt~y{wG}}gy#*<3mqhGvc;bf&_i*:tTڸvm~*?Ers#4 TqUhEe gNQLhFBQL`ZU~)"ÌBw#ZCJFs"y^[!>R,;t~(@l=8¯%(4ڌel30tS&lWb 1b"1D{̤7Pԩ,f _= !ΗIN(V -@ϪDD7x~N(A ZOR4uؔg59-]ɐ׿ ˄ógWL/o?8=;;K|3tyRjB 7ā%{{ ;ܭsً՗K*y,17g} =ǥI8~ N_5W+ %. ꯔ3~5G0S,,xWXvcόO1[+[d >*6cKrj}}tػ qd {[M^?ty4GW[Q<z>^+\ pao5nSQ,]!YeMj4KU}>{*ǻ '4w׸ؑeߤO0hiK+jQG:WTv8uxYYRXhN EζXL+fmxfT[F:辇QO6nC*_^3OE)Յxݙ];ڌTY:(y|T)V?_C.KfmPJ_a,]:Mjˆ f@yS?n2?*+y|k튪bȶ` ;tT}3(ԝPKХUs]snmptt_split_055.htmlX]S}ϯP 1X0YNgs(yF i0_&aof>Vӭ90V^X'>zJ_23K=fPp/NJ?=l?lhLv{W/VͽM_䋖w`+x_(wm''jqL 7#e7MLr6>1? IG׽O[_?r]_.LơW?TMFB9+-N cٟqj΁z?CdLE92maM $t7JfsOG A?\xmu~1єJR=WܭWY'藪>T"9*BS,2 Rl  hf=IMc'_jB)POȄ'Ď] !1iqC>e~bܗ(G%<`֢č7i"ļ_"WHcWjL{g}jt",}ŒXdBa@ߦS%tD :.zx}VP2Sv+ e1L}IJj5X+dG FNԦF+e.Tr[ Cw+H!>8)??H pzc&BBf+3 SPo8^<^VSQi$; Ac# ܒ,qus{}u -AO[\v?v;.g֋ő&:pjNwך*TX$@NӷrT%@̓-sEg:|(M%xG9 jj) wcA̶>t󱦏ATn?6Qup̴@&fO(R}pz~֭%XY0UjUf-fF:®$`WeNaUft[Ё~#C>K SPJaMa >= SI×6Jɧ PKХU (snmptt_split_074.htmlZmSίث:a@w+k{UN}ٕ,ۘ~33,={^$*K;NK4F*tZ߷~884ojL٬={ΊIٳh !2.iBx"4Y(KLM{ѵgogW\qI{ ,kX^e1k7f]n8 ~/xJGL>v9oĻSitCi?;ebytqaNR}؟?Ku,~: [ye2⁈a솇J?BOX꩔Ê,wOLTw+$C,#{ yhӇKzGgͥ3<MLJ -#C"+ ,$J1 62dB?W uf*m!.Dl+˱ &a::*TnIR4˕q%v&dhē , FYw/iifvԇs)G~ epOI Qϡ~4G K][!ѳN}IWaؠtO}jx%y'c?G;V(b"(g.eŶYV#nHA:ɲR E[*-ml<}R9(ԩ;;Lh3Yϳiq=(`=&PIF9IE3퀒?ѨLkMM4d\j9HXwVw-/BX\ ;ͧì/RڕvmZB(T%/1Y"Sg'mf,)r1f8fF#s_ۜLR)zpĝBA4+gmY"KihX;2pAM׺9v}ZI-@:B 59.k/tmFJ.F mx¢.Jn¿~ )b<5ˊ_V7dnsAҖo.U!&ΰn' yJn"Y=Do:-w~x[[0KC5y,blAʁ2l1;m6c(#ů5i6k"c:b60ǃCJfWx)'{VRZp ONJAw :p v :LT"GzA)Ə/T!^r+w: Oɔ!,7!<_yT6 ~ѦWS.Gzqq;f7x;z֮'CL5k)++Ipg{-<'*܎ (yO:wgPO76.z翜n78y s N{kEllЋT-<&Dk~K:ms޷'Ο~~* 9lw~-4ODY< cO\|#҈EvrذehV>/yxmggPv jSmo2`,R|J_'wn=j,nj>K뱲j-Ÿ664j?=b="zqmGp6n՗p1AU-Xٛ~us}fyۻ,mjKF2¤H_0H؅[f, EV OB*_4t=Vj{}Fl4M'-e4̈́wěŠbpwwP tKr)8~Ŀ?[{o1N#4::Gɨn*څވ_b<߄r{JKN&}-q@_!k7¾9ILܻO21Ulβo.|h?PKХUbsnmptt_split_071.htmlSaO0ί8KA* ƀuڐCiBnri,;N߽w%XV^XU<G@\Esnmptt_split_032.htmlTMo8W xh#v64p0hi,I.Iv}FڢiHμy\:Uz܏NUKQR{R#URV{orH*_^^ƫò#F^z/0e*kˠʣ)!B8ҽPj Σ;+ml9i|M^^߄<>޼/ UꥃqNNe-_hrpO[nG9LmH ߈}踜k{qH5}sHLkF kGJ3+`2 UăNVT:T٘aضm,{ Ky4qP˜p L+k:T i~vԍnjK]p,\\1\Iuc $>UJ:~)yx=%T[ 2_aIfrsw~[:$&?&^0 Z~ons>ZEk^8ЛxYɍEwFR'0(cfB-Gf2( _%my=l?\5j*W&^ȸS̷hcq@c=}$hUl`G#U@#*9 OKBZ|x@f©Wc_EnCJ!QΆF۲^Vet14(55RRJ\z j;r}&&oPIF˴ o+ݿW@lSGvdn;qZcn|SD`g#u4/{tu7uqPf7:OF{4XQcX)>u zdz@LR XVZâXR10ljUvy~JkA@)ld/"f=r-R x:ʨgm,è-,ENߛBPghsA! 1ok7תql|=t/TS mQV.ݯ3 /f5XMUy(ىn9!W/̡2 ̵2\ER.j9Lk4rRuxs>C{\FHx^YbAwA+oj76U1F;Xͺ\D(jH1~I]-^$pcR/ (qABeҦ0.͹VU/b`1`'Ђ}{Dc Y^zz|y/󊦁|Uz?qk ]jRDF_y-/<+) χwő6@Dsmpt #eO}D4i-E ZVCjpHR?gZvjg[v1uulZOX]<\{ oD?m>D4HI6ej9F]oGcKm7-u"hK/v.kKI+ֻ9^Ic^<.:M Z}ח"eXq\+qec Iww;~(PKХU Df snmptt_split_061.htmlYko_1a݆ʒ+'b˰ȕ_\ZV/{fEJ;-P]G B᫖dJJ=x/G/Fd MCgulcm>2s`$EX:C\4w(H-}]| '4HsmsA%T/w Yg9pG:)_+#HYNCEhnoҭLJt\e6GRe׽JGz#)"6i^JpZ]^&BGd'S0y! x32:t =d1R;ZC\>?ZdfӠS+D\Dt8E [pv)i9GbJUoޥNλίG!>[vn pͦ3]h_&~ު?hfv39,#|rYL)e2G$"F$Y$IL2T!u/{ԤίSBOtQFZe$(4$ b_ ]7&TR*|1n$VKTvH`,ZHă41d}1gPƾ̲4}{m?JϿ8˥*zMj& ID PBh*T(W9`.(4J0@tF|/ ^Q%0TÑN|G19y_S* vU攜4Vz{3]¯2N epi*<IJ"g1I&iIq|2O~4\a*3}(uTӚ=RT&OH 80PQotP>Q@ve CeZCpvirw/C熎Aw9˙VUG@[ MHA4h1<7ָcT"Hu3`rM3nȣ B̵6,j$ZΣu{ xWHjЗ`NW74BIٿPjpЅ*Qe-دN9xi!NL ulqtY|ޮz<43ddNyظXts/RU0Qe yW[s O arx7Ww:z7 )| ]/afRDEp!WYj,$1ϛAbkŶ>gtgH@;%cJs)K)֒[HN=Wb0:I)X<;aTM68n3=پ4]U SwnnR;8'fռOXsfD>:zYUhEfM: gBYj73`Pl|b m2QwvKg+/PO9} wxYuSݮڠ2nlYb"?,l;f,24ѯfwFrafBm׌O21oDDrK|mvXBXD serL\ϑ}9meBK+1P:6`UbYb"ӡdpDB`{ϩ6j@i8τ+a] sR:*)?S/ ~{3n׮7k['SGm~oooK0;~UiT9ШߨbߘS O #o@TVpIɻ+LcHXa]5Sv6kf+iM?d3[P4b|EWq2v,eBL@&KO)%R$AYy1$l, 9Bb(7k7`.Ƅwb|!aQ uts´m:?*y [s ηEZ宒a-^PrJ"<_? ltϪ74 [{*7_|/>=5 *Cf``LZ*YK_Pav1B#q|l֤4::ʈ0jegn={l9}1~Y/2&)>&;NY#ҍ'7_"ul ~5>R'>ae A-vr GN6ſPKХU\snmptt_split_045.htmlTr0+vL6$6ʹBę2׶&Br%9|}W0r%W}"\-<6\ɨ7=@"6.'ai)R<E0L&xDhH+PfViR%-Jy<[C4ieS?}•|u]ն$7 P>\f10ipa֤-Uvk8XFàwC㟚?GޗVK?^W7 +!^@Z2mF 43v-ДK]ij|?wQi`ph]`2=gApv_IHƧ4d۴aTQ#T$M -:"5Ձ+P9Rc _0kˀ1|!KnT-2X ԆҖKjU/ 5Ұ :+5[Ѫzq D0rYYs!I"7׹B5iΜ 4+zIJWtb'&!1'ĉ;<ohMuFݯX @&|Aw\n"_tݴJp g{o}e{6&:❭t[SK?+vns4戦'PKХUM snmptt_split_054.htmlVO8-w ] cv lHݕnRUn6b{vRZJ3ЩjO9Ce߱}f/y=puGsNgYL(VK7_9޸CDE {0ʙ^>D=UBr/A?`piN/$79UkkMZd 9]s ABQݱP99wPPq^p2J1ꭉRd[fL2{-/IZHg:F"ɶ,}7G7N'K^39 ݋~徭IJBiƌ*fJ4b{f OLBei#| 3jnds^J<OinƀMNkn  p41 ht14Q\a!ֽYFLjy H4pIDfdSzGAKHKq2&pfJV0K O+n% wXUm̾uIAF"iL F^<Ɗ9I>eN`f| ~V:ʴ &5&wk^ɷ 4DnTmg[A^s}fA`g\[,YҸiNJdj7.!0."5 9&9Y%>7^ J:n#.R0s7^ww WWL~Ra\Ӊ,J Ӌ$kttܜy.j&-Xa1 .~ޗACZ`I8 ŧ\tꇩTq4zG{[0ߖy3פ]YZfycmŴbS=#G8/½xn(Gf6@֩ys"4)lo1a -m+s DY`(/fF#l! [e6#-V2x>__ ؎8QlWe^\뇸K&QuyO~d#X̋Sw'PKХU7 ''3page_styles.csss(HLOURPM,JM/)ϵR0-(F@jPKХUpqM snmptt_split_058.htmlVmo8_1*k+BEs{{Iةr@3<8<*ͥ蜄 HdŤsRqC=5dFBw1Msu6^=Ez0,ǎ7AxF,p XC2eJT0x,3SD!L-|J~x&RuQ۱r%Ӥe 8KEYpHiN2rqӇaazU-WK9( 2I0b31A#31B(8IF,'q +bVJPˁ/O#} % lQ}O^> \AՌc1*1@ӆ9!a?gngniB-VS&;! 4 ՍxvXiץ;&AUˁsMl H]z72UOLY4y]w*}kPKХUOb snmptt_split_035.htmlUnF}W @IJQB)pܴ5EA`ȑr]ZV>3KQaT[9g}sYl`i,yz/uE2~] +-EKBy||J,%|D ~W¬4x g].j݇]\/l7̭sbqtr{S.kKaq̔w`Q#7 ]#c5w.ⲿ"Q Xml?Dʕp\(E{d$ݲz;6ƕv^j@!R1!8#p2kru aQ0|N( 5%Z(VrBхPFcJ~-MU.D|J a4.6^rA",7_G*ҕ,7W$kehwAF,/μDZYdKIŁxPT9sxC re)W u~Ձv :xڗoep ^ MT'"]z;QՊ^AI2iq('_= OG—p$3()2J9e[C*}4v@R\H:2MKP7^XUк9:ceJY HZOHr~J%~y=#^ST?WJ*' +mO0 12]FL-O>D*`6`}~mY^Aa´J% 2Ob{bmxeVAVN.:sΪEngj?˂}PKХUÉ7Ysnmptt_split_025.htmlUQo8 ~ |pݤkoPXCa(6m %O~QvAaO{%,o7N=?LJ:5|mrn<((Z$4@\-*j 5P<{q2k!7[} 4< hrDTw-qD ikTJD¿^W3vpLoKzRQK&w7Š?S8m;}&}ehk"7:y7>aj8j ؆PZw=B^ CWǕ*X Pre1Nٸ[֫4HbGbw6 i828;cz?H"Cȭ`7ZӀ+M8}WAז Zi'AtUAg.dj%iy|rom sN>k\mZb6](!3HrNMoȐE#zKzRG]+?d[蜰Ӯz{5*^ F0"PZP鸺ܺf eނp\se!qꓮ5bJ=#斯rOҝn!GA<RxO)c_"d &PKХUh--snmptt_split_007.htmlRk0޿Зl؉{]m6+]žآI_?)IC_޻{J/F3+zhT.yw5y*h>۶H*] eA3VBIV2yr{Q.a u/8:,ޕ*\I3z+Gw-QtzJZO| c$[>~GW[]$16p(+>r9ck/r; (K"eS)hkDb@^cC-eP"/x5lˏ_BMgUHn[(3,{.νC1UL֌drd(P8C8p{ښAW*l@8lMޤAcG:R]Jo/ʊñE)Vs˵m佸fwk'9-'oR Q4G[q??9ݱ?BqU=͚{^qVmrs9}~PKХUX> snmptt_split_064.htmlVMo8W xI mҦUx ȋDEE0""UG}FM `["߼yÙ'98$LX`LB+z?{QFPet~4p1,J乿 +1*4i ʡrv^~Ts(w"T3[(8`:e08?3Fm WRo;PZHRt :H?%gWtt:$_^/e/0 ۽z;iykgpB + #)`PusDt q,C3kuDWQ5M US[M$4  #Z3ywwSaqWzcXꮖ48%ztz74I4BH%ڡ%kH@GIIpH8}F:QSXPXЅG.![XZJ=%?i@׵~ uFxs5Pq1$XZt5r+-:z┄PL0o] Ķ`;B)v"9DNԋ;O]j m-U&ӳkz93M?N J?,ڡG_ӓs;Hˆn,8 ׳QnKۖzF>rt/J78zy}9ڹ<.h*498Q \7AOj*өE8%΄uB7Ҳ)Pw[?X'\cDn̳{sG ŵx cW#dl ba Ä_'-oPKХU jXsnmptt_split_044.htmlTn0 }W~,vzs\tð=fCQJLBeɕ\;Mv[sCŢT0Cу~ &:֔Nv/ vcWAATE|>y?=='HvE,hQ QdlS 5 o+t wGJzGp/6! 92貦Y^6R": O=%uj#f;n䦢ᗛ5.IRȊS>8Zm{zX 7ZV"/9L aҠ)0VR?E5- hN mzt9WnQWxb\S%T(9otWaĢshY׫9w$ߎDn8ߢ- >#tvgj6H1 p-w,@20;(tgqT1DcmY??8,ٚrM=#S$.5scPG1 +: Uy9V$LTPgɼmb^+ap:SvotYRu0MJxٲȌRfK>V Vn Sprcb\zբ1+Q:-E^E\<*tYS6 !ZLmr> Γ@H"93g.g8J.+ OhЪ}#@UPM7Go;̑*f3V-p FY:sΐ:WfSThӆAC]ѿBa ju-| NӰ}BW~)Qwf}G7pM^X 8?]Ζ-UgbpuI  Z-ǹxj:xq.EZmgmIM4UJnJ.ŃA[$0f ƻݨ~fo63؞$cȫZbbEDHFR QF:ZJa]郞Z ʣYpc R&`Mֱ 4Z+QsyY"y{?Y~p+#zjz }* ~eiBD( ƹXJ|&ft5{MQ7MIC!IIv=0ȵ$mKO!Ql g=xM (yj}Ɣm>ȆOe(K!Izݚ9V[\ndov_Ti4/S6O&m/ЖF: ͲC?X7!y9# 3owvs+nܳ/C-os7PKХU.h浕snmptt_split_034.htmlTmO0ί8YHM% b&A7A'&!Xsv_sCCh|Řp T+"[B u-TX낣8prp)qkS 6.kWR7 KfݠZ@@0 T)T͒* rE֐lڰ5R2;Š%UvYYywVM+G*\HlSm+S.%%M.0| ,"ܗܵOqSGqR'w,uu8䢨 +qV(B65bE]e?5+'AW~Di$)qŵʞ͒LarJ/Wr5\( A @)PK2矋 sJr S=sPs<+ C?`${PKХU Ysnmptt_split_024.htmlTQo0~߯8e dliH[F *7$;N9']WUc$}wK-jsN==vunxt'<ݘU7o뺨;-x0,H*"|g5Y-2ȍ}Ȯ b N/+}^2zxrpp[["Z_QT'<*0IF—c۳vW~\&S4j#&s` cXTGI_Dy I- hM^:,9:2܎B-퍰^t6KOebZ ]O̅=@~R6h4yS?Hh| Uy!UC® {ZR*\}t7$hA #\|8dڋ>jh,Υiz0hoC uuw ²U ^O)J"VI΀7ɂvVFg)KwPKХUdD snmptt_split_008.htmlVmo9ί{6JJmڃBd-b{{7/Tpjyf٤Xըnj*\Ufl82#SeGQ\,Qtڬ`xh ` >St KqQaNb*7>oo`.Vڀ _7B.5V<0?rF[&.,? )Yqq _P}⺰0Sk٘eut68}uIܰSVQ~OZa]XmG\8ܸ'5 3(P&JA9J Q56آW)I[47w%T 1bi0%y؎@E\\FuF#N'?f=_a0]K^Bfc/ c["/mh &U%ԮI(v2E8=2x`F"\ ΅P`{+a* w->ɭzMYnpF`ŕͳL{0TM;d%_2R$ #Ȉ<($/8اF]m;zn6^́npq(h8>*-=l(e\)P1XՍnb%*o8 }O wNSW)p)E+8] vj3=PKХUP7snmptt_split_073.html[{oFߟbsG>8nIqBȕ eKcT@8Idj9;3;wq$neC}!_a2?ͬ?NDvƤǃr/*F/^ٞ' >x"byڙDfQYG*121n޽7a3 )}z&K.D{qy)<wQi={ޞv.*]yg2-)oxg责*z!4쫾䬺OWzs9ew'SG[(f&2Z{80(~ى疥a}!ܓⓥq *1YYȬ,=dU \$hp@yLyt- LC:j 6KXsk%E$  z:62(Z@Kaљ q2r&V*kl/]G T[NhMW@(Gf@'q3`zPYt~ S+*2HUR*H# /i$=-t^;E!͔//oyd4˂i~2KHxi>cKmkW`m s (%̤/+Vii #bS'd >0`&=3,L P.yF@wvww$32 - 4Z*)Ux_@K/k.H!e,S#u p‰ՎJ Ue7q"rDֈ,5~# ` "O Ud+̧0+*0i0.<vJAU  z^_;/ ovQ*Bũ;:(QP=Z5X 5t%gLT*bn;W*ؕJU:&e6 DOЌDQ--Dq5'*3N"Vl;"]}nUgP0R'SDDB= nf#rkNj" ׉<1aDz$NUYa(O"ĦfȅL)|`>hA,9ch85ȗ}o*w8 -i!kGtx*̉Ɔ(ѭ,a ̭Ĥݼ*@*{iXSI !rOJz> !xN8 *uV>2ǐF,!\,A`oOȤ2U( J X& 6j\K$v )%Py,%c e1aqKň6{ȤWWvy0Hi;i(-E/곇yLoe@kH5[d$ c |m+^"7d6K6YC,"()m=B{+GOS^Aw @HAad'd$"PYmn VRňNȱ-L3oglpEz\a3pط\K|?'Xm967xklcQT1PUA>{I恛Q UAj2WV a  Hx_junk{C!V\R>DCkPZx=^C?0^fgߢR H!rjyW{o7xouuԁs,t#$BHY9zz 0V:pZDb8yvo! ȣ_,0sBi%9]IPJZ[jd_(!bnTC!n]i5ų7=uꊬ0AdXaJ` .͸_+h?6P\17*[!:)bz < ;%n. *]}{ jr`wn{ AwMijmݤoIޥҧF&ͮ9A>v{OAϕY/wYke(# fEo@g(srXA)sV+p_*?eXltz۾nTt~=eaս 7Oc J9j}qa2 h mFp%iQWp,cݼW;_oJAG`&pRaLSj΃ ZJQHt觳C"ٽ]GoL]S]}WoJE;>~Ȱ{4Cz+,q5szYHiF(URp!)|]f:Rzxe[tAL.H% D~tbt+Y=6'e;V2j \:۶ U|&@v/ծBy[/{0C4%AсGp쏏cw1<+^oD ~4B*i|^PE^@F$OG7<Ӎcؽk 4 Gلh"nvG'(Y&)} <ӱS; vxVyS%~1gL4f2|D0w*ϯ勁"8tg=ǩʌW^K#c-"\!WtϤSэt[eԤCd8B5{ $m>G1X <`mzl3D&+ڪv U"8%v=Ib(,J< 6sm/F \R%7PGtˀ]dSBjD. /(O-xyB~4LأO_(ѹzs,]Ug<]Pu z:OI"9B]I׶q~.: C]ov='XvۅxU7"/'&D׮~J(o6ri#D&A,.Y[F: 64_x+gm8pi"&"*]0(*@ ISg]*zMi%}ϦYf`[mG3mٮVg(uݭ+=z[ =cd1v1J*AEi_9T YKzr+w3@F8;~Vï)zǹ,`-,[- n#E3v&LxGCcNPNtS.-{L %a`)Xvs:# Cs wwҨT.\F3 jQ31L;h6TwHש͈#*,ˉavOC/_SE'[mZnz=pMB-DReɎ5Gsf\!j@qJfE)&M`s 'F\ 0ALk4˶GJ5unc!raD?CI*S{NܢtL.uHw̩Q'w8jp8˔t/гظu Q >;`GNr1.ML6U2"ՠR"U0iFwUuCDTCf@Z47R"C~$6 j}]K_X"l3yv|e#h{GZ˖ڄ8 y~^W26 \He<4?M3\)BULu38UecZfTs=VJ^sn fIMzvEX6˗حtTپdxaLE(vݩbV}yh7½@chv+*<%CcR)jSzqjS§r~ QH;4$`Js1g$FQw~xjSLޓ{Ŝ]{^o͠擲<_WNPKХUIӃ-Msnmptt_split_029.htmlTo6~_qbI7t MV-$"ɑTlQ]E⏻ qtz ] UǝN.dFʥ|r-u"^ApЧ},ʔ9DƵpAcڷ)/wkf;x[d\zR,,"1QvsOf,KZ ^NK?K,7,5tDna}k݁kt'ˏIlAC3rp|4N{X ߀o.w-ƅ/CX0x&";LA8 ڽ+V =>J3!+ +8e7JGT!#z6#઄6PjM7::dYsn窗/uZ%pc(z0BiV+Ark'uݏ<Z7_6#r(Em!~=(d agB9O [e` rOs?HLcoa|O¹nӓ5_9R?rT)j~pXtC7ӹycoG׵՝蝏>L⭹"b:MٖЅb[a#L,dk-3JVv/PKU7META-INF/calibre_bookmarks.txt]0w~P8 J!1bM(-PL@^nkd=P.ITqvGlG^GFb`+J΢!ցk4'aes.^+7זk6u܈}iMeWHa& لo C$9w [hf_4PwpQSQE$lPKХUoa,mimetypePKХU :META-INF/PKХUtcMETA-INF/container.xmlPKХUA![) 1content.opfPKХUCu(jtoc.ncxPKХUљF4F 1snmptt_split_056.htmlPKХUcFE2}snmptt_split_077.htmlPKХUSj_DQ (snmptt_split_069.htmlPKХUr] 0y.snmptt_split_060.htmlPKХU̪* (5snmptt_split_037.htmlPKХU.rr@snmptt_split_046.htmlPKХU~ "Esnmptt_split_006.htmlPKХU*;Psnmptt_split_002.htmlPKХU6pKa9Ysnmptt_split_036.htmlPKХUlsnmptt_split_017.htmlPKХU|EMosnmptt_split_011.htmlPKХUj _Uqvsnmptt_split_049.htmlPKХU& ހ"p|snmptt_split_075.htmlPKХU =~snmptt_split_066.htmlPKХUN4;snmptt_split_062.htmlPKХU -L \snmptt_split_039.htmlPKХU}m snmptt_split_023.htmlPKХU<{[p6snmptt_split_004.htmlPKХU6W(Ďtitlepage.xhtmlPKХU; snmptt_split_048.htmlPKХU@7 @snmptt_split_028.htmlPKХUfsnmptt_split_019.htmlPKХUTݹhsnmptt_split_021.htmlPKХUʈOsnmptt_split_038.htmlPKХUzka&Isnmptt_split_005.htmlPKХUޣthisnmptt_split_051.htmlPKХUr{p =snmptt_split_009.htmlPKХU#G:5Wsnmptt_split_020.htmlPKХUiݽ>Hsnmptt_split_003.htmlPKХU&Ssnmptt_split_001.htmlPKХUc>N"#snmptt_split_063.htmlPKХU%[snmptt_split_041.htmlPKХU`H snmptt_split_022.htmlPKХU3snmptt_split_013.htmlPKХU ^snmptt_split_050.htmlPKХU[*gsnmptt_split_014.htmlPKХU IvXsnmptt_split_031.htmlPKХU&F"c snmptt_split_065.htmlPKХUቬ"]snmptt_split_040.htmlPKХUp&snmptt_split_015.htmlPKХU^|hB 9snmptt_split_070.htmlPKХUu vsnmptt_split_068.htmlPKХUyTEsnmptt_split_053.htmlPKХUo'x snmptt_split_030.htmlPKХUY2Hsnmptt_split_016.htmlPKХU;c(^=cover_image.jpgPKХUKL>6+*snmptt_split_072.htmlPKХUlr2-snmptt_split_043.htmlPKХU^kf$0snmptt_split_018.htmlPKХU|f,&2snmptt_split_078.htmlPKХU@b5snmptt_split_052.htmlPKХUV4*u:snmptt_split_033.htmlPKХU1/=snmptt_split_010.htmlPKХUVu\\'Esnmptt_split_000.htmlPKХU5w6J%Lsnmptt_split_059.htmlPKХU/9cg Tsnmptt_split_042.htmlPKХUTc'pXsnmptt_split_026.htmlPKХU8n1X\snmptt_split_076.htmlPKХUs]ksnmptt_split_055.htmlPKХU (Ktsnmptt_split_074.htmlPKХUbsnmptt_split_071.htmlPKХU>ބsnmptt_split_032.htmlPKХUѩ_snmptt_split_027.htmlPKХU#Psnmptt_split_012.htmlPKХU Df Ƒsnmptt_split_061.htmlPKХU\snmptt_split_045.htmlPKХUM ŝsnmptt_split_054.htmlPKХU7 ''3Epage_styles.cssPKХUpqM snmptt_split_058.htmlPKХUOb =snmptt_split_035.htmlPKХUÉ7Y#snmptt_split_025.htmlPKХUh--snmptt_split_007.htmlPKХUH$4 stylesheet.cssPKХUX> ϳsnmptt_split_064.htmlPKХU jX@snmptt_split_044.htmlPKХUKsnmptt_split_057.htmlPKХU0̑B&snmptt_split_067.htmlPKХU.h浕snmptt_split_034.htmlPKХU Ycsnmptt_split_024.htmlPKХUdD snmptt_split_008.htmlPKХUP7snmptt_split_073.htmlPKХUE[Wsnmptt_split_047.htmlPKХUIӃ-Msnmptt_split_029.htmlPKU7/META-INF/calibre_bookmarks.txtPKYY snmptt_1.5/docs/snmptt.html0000664000000000000000000062363014277306025014651 0ustar rootroot

SNMP Trap Translator

SNMP Trap Translator v1.5

(SNMPTT)

This file was last updated on: August 17th, 2022

License

SNMPTT

License

Copyright 2002-2022 Alex Burger
alex_b@users.sourceforge.net
4/3/2002

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

What is it?

SNMPTT (SNMP Trap Translator) is an SNMP trap handler written in Perl for use with the Net-SNMP snmptrapd program (www.net-snmp.org). SNMPTT supports Linux, Unix and Windows.

Many network devices including but not limited to network switches, routers, remote access servers, UPSs, printers and operating systems such as Unix and Windows have the ability to send notifications to an SNMP manager running on a network management station. The notifications can be either SNMP Traps, or SNMP Inform messages.

The notifications can contain a wide array of information such as port failures, link failures, access violations, power outages, paper jams, hard drive failures etc. The MIB (Management Information Base) available from the vendor determines the notifications supported by each device.

The MIB file contains TRAP-TYPE (SMIv1) or NOTIFICATION-TYPE (SMIv2) definitions, which define the variables that are passed to the management station when a particular event occurs.

The Net-SNMP program snmptrapd is an application that receives and logs SNMP trap and inform messages via TCP/IP. Following is a sample syslog entry for a Compaq cpqDa3LogDrvStatusChange trap that notifies that the drive array is rebuilding using numeric OIDs:

Feb 12 13:37:10 server11 snmptrapd[25409]: 192.168.110.192: Enterprise Specific Trap (3008) Uptime: 306 days, 23:13:24.29, .1.3.6.1.2.1.1.5.0 = SERVER08, .1.3.6.1.4.1.232.11.2.11.1.0 = 0, .1.3.6.1.4.1.232.3.2.3.1.1.4.8.1 = rebuilding(7)

Here is the same trap using symbolic OIDs.

Feb 12 13:37:10 server11 snmptrapd[25409]: 192.168.110.192: Enterprise Specific Trap (3008) Uptime: 306 days, 23:13:24.29, sysName.0 = SERVER08, cpqHoTrapFlags.0 = 0, cpqDaLogDrvStatus.8.1 = rebuilding(7)

The output from snmptrapd can be changed via the -O option to display numeric or symbolic OIDs and other display options, but it generally follows the format of variable name = value, variable name = value etc.

A more descriptive / friendly trap message can be created using SNMPTT's variable substitution. Following is the same trap, logged with SNMPTT:

Feb 12 13:37:13 server11 TRAPD: .1.3.6.1.4.1.232.0.3008 Normal "LOGONLY" server08 - Logical Drive Status Change: Status is now rebuilding

The definition for the cpqDa3LogDrvStatusChange trap in the SNMPTT configuration file would be defined as follows:

FORMAT Logical Drive Status Change: Status is now $3.

The $3 represents the third variable as defined in the MIB file, which for this particular trap, is the cpqDaLogDrvStatus variable.

Another example of an SNMPTT configuration entry is:

FORMAT Compaq Drive Array Spare Drive on controller $4, bus $5, bay $6 status is $3.

Which could result in the following output:

"Compaq Drive Array Spare Drive on controller 3, bus 0, bay 3 status is Failed."

SNMPTT can log to any of the following destinations: text log, syslog, Windows Event log or a SQL database such as MySQL, PostreSQL or an ODBC accessible database such as Microsoft SQL. External programs can also be run to pass th e translated trap to an email client, paging software, Nagios, Icinga etc.

In addition to variable substitution, SNMPTT allows complex configurations allowing:

  • the ability to accept or reject a trap based on the host name, ip address, network range, or variable values inside of the trap enterprise variables
  • execute external programs to send pages, emails etc
  • perform regular expression search and replace on the translated message such as translating the variable value "Building alarm 4" to "Moisture detection alarm"

As of SNMPTT 1.5, both IPv4 and IPv6 are supported.

Downloading

SNMPTT can be downloaded from the Sourceforge files page. The primary git source code repository is avaialble at Sourceforge and a mirror is available at GitHub.

Requirements

  • Perl 5.6.1 or higher. SNMPTT began development using 5.6.1 and although it is now developed with 5.30, it should still be backwards compatible 5.6.1.
  • To use snmptthandler-embedded, Net-SNMP's snmptrapd must be compiled with embedded Perl enabled (--enable-embedded-perl configuration option)

Perl Modules

R/O Program / Module rpm deb
Required Net-SNMP (formerly known as UCD-SNMP). Specifically snmptrapd. net-snmp, net-snmp-utils snmp, snmptrapd
Optional Net-SNMP Perl module. Only required for features that perform conversions between symbolic and numeric OIDs. This is NOT the same as the Net::SNMP module availabe from CPAN. net-snmp-perl libsnmp-perl
Required Text::ParseWords module (included with most distributions) perl-Text-ParseWords
Required Getopt::Long module (included with most distributions)
Required Posix module (included with most if not all distributions)
Required Config::IniFiles module perl-Config-IniFiles (EPEL, PowerTools repos) libconfig-inifiles-perl
Required Time::HiRes module (only required when using SNMPTT in daemon mode - required by snmptthandler) perl-Time-HiRes libtime-hires-perl
Required Sys::Hostname module (included with most if not all distributions).
Required File::Basename module (included with most if not all distributions).
Required Text::Balanced module (included with most if not all distributions). perl-Text-Balanced
Optional Net::IP module. Required for IPv6 support. perl-Net-IP (EPEL repo) libnet-ip-perl
Optional IO::Socket::IP module (included with most if not all distributions). Required for DNS translations.
Optional Sys::Syslog module (included with most Unix distributions). Required for Syslog support using Unix sockets (local syslog). perl-Sys-Syslog
Optional Log::Syslog::Fast and Log::Syslog::Constants modules. Required for remote syslog and RFC5424 support.
Optional DBI module. Required for DBD::MySQL, DBD::PgPP and DBD::ODBC support. perl-DBI libclass-dbi-perl
Optional DBD::mysql module. Required for MySQL support. perl-DBD-MySQL libdbd-mysql-perl
Optional DBD::PgPP or DBD:Pg module. Required for PostgreSQL support. perl-DBD-Pg libdbd-pg-perl
Optional DBD::ODBC module. Required for ODBC (SQL etc) access on Linux / Windows (Win32::ODBC not required if using DBD::ODBC) perl-DBD-ODBC libdbd-odbc-perl
Optional Win32::ODBC module. Required for ODBC (SQL etc) access on Windows (DBD::ODBC not required if using Win32::ODBC)
Optional threads and Thread::Semaphore modules (included with most if not all distributions). Required when enabling threads for EXEC statements. perl-threads libthreads-perl
Optional Digest::MD5 module (included with most if not all distributions). Required when enabling duplicate trap detection. perl-Digest-MD5 libdigest-md5-perl


All development and testing was done with Linux, Windows 2000 or higher and various versions of Net-SNMP from UCD SNMP v4.2.1 to the current Net-SNMP 5.7.x release. The Windows version has been tested with both native mode and under Cygwin.

SNMP V1, V2 and V3 traps have been tested.

The latest version of Net-SNMP is recommended.

Note:

  • SNMPTT only requires the Net-SNMP Perl module if you want to have variable names translated into symbolic form, you want to be able to have snmptrapd pass traps using symbolic form, or you enable the options translate_integers, translate_trap_oid or translate_oids. Although not required, using the Perl module is recommended. It is also required if you want to use the snmptthandler-embedded trap handler with snmptrapd.

What's New

v1.5 - August 17th, 2022

  • Added PREEXEC support for unknown traps. Results are stored in the variable $pun. See the unknown_trap_preexec setting in snmptt.ini.
  • Added unknown_trap_nodes_match_mode setting to allow you to change how traps are handled when they do not match due to MATCH and NODES. If set to 1, traps are considered skipped instead of unknown. Statistics now include the number of skipped traps when enabled.
  • Added support for wildcards for the snmptt_conf_files setting in snmptt.ini. Example: /etc/snmptt/snmptt.*.conf
  • Added log_format snmptt.ini setting to allow you to define the STDOUT, text log and eventlog text format.
  • Added syslog_format snmptt.ini setting to allow you to define the syslog text format. This will allow you to add a structured data section for RFC5424 syslog.
  • Added variable substitution $j to pull out the enterprise number from the full enterprise OID. For example, for enterprise OID .1.3.6.1.4.1.232, $j would contain 232.
  • Added remote syslog support using the Perl module Log::Syslog::Fast which also allows you to specify the APP-NAME for RFC5424 syslog. Added the following snmptt.ini settings: syslog_module, syslog_remote_dest, syslog_remote_port, syslog_remote_proto, syslog_rfc_format, syslog_app and syslog_system_app.
  • Added reload support to the snmptt.service systemd file. This will allow you to use the 'systemctl reload snmptt' command to reload the configuration.
  • Added support for IPv6. To enable, set ipv6_enable = 1 in snmptt.ini.
  • Added support for sub-second sleep for spool folder processing.
  • snmptt.ini can now be located in /etc/snmptt and is searched for at this location first.
  • Updated documentation on securing SNMPTT to ensure the snmptt user has read access to the configuration files. This is required when issuing a reload.
  • Fixed a bug with daemon_uid that prevented SNMPTT from starting on FreeBSD (bug 47).
  • Fixed a bug where traps arriving with the hostname set to UNKNOWN were not being handled properly (bug 46).
  • Fixed a bug with MATCH which was preventing it from matching integers properly (bug 41).
  • Fixed a bug where the agent IP address was not handled correctly when it was received from Net-SNMP as IpAddress:x.x.x.x (bug 27).
  • Fixed a race condition bug with snmptthander and snmptthandler-embedded which could cause traps to be missed. Spool files are now immediately locked after creation using flock(). If flock() is not supported, the spool file will be created with a temporary filename and then renamed after closing.
  • Fixed a bug with wildcard_expansion_separator which caused an issue when using wildcard separators that were longer than one character (bug 38).
  • Fixed a bug where quotes were not properly removed from some incoming traps.
  • Fixed bug with debug mode that was causing some debug mode output even when debug mode was off.
  • Fixed a bug where DNS resolution was not working for enterprise variables when net_snmp_perl_enable was disabled.
  • Fixed a bug that prevented snmptt from starting when debug mode was disabled (bug 48).
  • Fixed debug output bug with snmptthandler-embedded (PR 1).
  • Fixed a bug with IPv6 address handling for NODES in snmptt.conf.
  • Fixed a bug that prevented the hostname from being extraced when IPv6 is disabled and the hostname is passed from Net-SNMP as UDP: [x.x.x.x]:xxxx->[x.x.x.x]:xxxx.
  • Changed net_snmp_perl_best_guess default from 0 to 2 as any modern system should support this. See FAQ and snmptt.ini for details on this variable.
  • Enabled Perl warnings to help ensure code is following best practices.
  • Ran code against Perl::Critic to find non-optimal code. Made various adjustments such as relacing bare words with variables and changing open() calls from two arguments to three.
  • Documentation was converted from html to markdown to make it easier to maintain and a full review was completed. Many improvments have been made including a new section on integrating with Icinga. The docs folder now contains .md, .html and .epub versions of the documentation.
  • snmptthandler-embedded:
    • Varbind types Gauge32 and Hex-STRING now have the Gauge32: and Hex-STRING: text removed for incoming traps. Unicode line endings are also removed (Perl 5.10 and higher).
  • snmpttconvertmib:
    • Added --exec_file option to allow you to provide an EXEC command inside of a file instead of specifying on the command line. Useful for commands that include quotes so that you don't have to worry about escaping on the command line. Also allows you to define multiple EXEC lines instead of just one.
    • Added --exec_mode option to allow you change how the EXEC line is built. Setting to 0 will append the format line to the end of the line (default). Setting to 1 does not append the format line to the end of the line. This is useful if you have added $Fz to the --exec line so that SNMPTT can replace it with the FORMAT line. Setting to 2 is similar to 1, but instead of SNMPTT having to replace $Fz with the FORMAT line, snmpttconvertmib will do the substitution.
    • Added --preexec and -preexec_file options.

v1.4.2 - July 23rd, 2020

  • Fixed a security issue with EXEC / PREXEC / unknown_trap_exec that could allow malicious shell code to be executed.
  • Fixed a bug with EXEC / PREXEC / unknown_trap_exec that caused commands to be run as root instead of the user defined in daemon_uid.

v1.4 - November 6th, 2013

  • Added snmptt.ini option net_snmp_perl_cache_enable to enable caching of Net-SNMP Perl module OID and ENUM translations. This may speed up translations and reduce CPU load when net_snmp_perl_enable and translate_* options are enabled.
  • Fixed bug with snmptthandler-embedded where IP addresses and OIDs were not being detected properly because they contained 'OID:', 'IpAddress:' etc.
  • Fixed bug with MATCH. The PREEXEC $p variable could not be used with MATCH. PREEXEC is now executed first if MATCH contains $p.
  • Fixed bug with syslog. Log entries were supposed to be logged with snmptt[pid] but instad of the pid it was actually the effective user ID (2980512).
  • Fixed bug where the hostname is not detected properly when snmptrapd is configured to not use DNS.
  • Fixed bug where if the spool directory is not defined, files may be deleted from the wrong folder (3020696).
  • Fixed bug with syslog logging. Function was not being called properly (3166749).
  • Fixed bug with MATCH where number ranges were not working (3397982).
  • Fixed bug with multi-line traps (2915658).
  • Fixed bug with LOGONLY severity. EXEC was being executed even if the trap had a severity of LOGONLY (3567744).
  • Fixed bug with snmptt hanging if the log message sent to syslog contained a % symbol. All %'s are now escaped before sending to syslog (3567748).
  • Fixed possible bug with MySQL. Put CONNECT string on one line.
  • Fixed bug with not being able to write to the debug log file when running snmptt as non-root if the debug file didn't already exist with the correct permissions at startup. The ownership of snmptt.debug is now set to daemon_uid before switching to the new uid. Patch 3423525.
  • Installation documentation updates (bug 3425999).

v1.3 - November 15th, 2009

  • Added snmptthandler-embedded - a Net-SNMP snmptrapd embedded Perl version of snmptthandler.
  • Added variable substitutions $Be, $Bu, $BE and $Bn for SNMPv3 securityEngineID, securityName, contextEngineID and contextName (requires snmptthandler-embedded handler).
  • Added snmptt.ini option duplicate_trap_window variable for duplicate trap detection.
  • Added LSB init keywords and actions to snmptt-init.d and changed the priority for start / stop so that it starts after snmptrapd and stops before snmptrapd.
  • Changed the default log path to /var/log/snmptt for Unix and c:\snmpt\log for Windows to make it easier to grant write permission to the snmptt process.
  • Changed umask for log files to 002 to ensure they are not created as world writable.
  • Fixed a bug where the the PID file was being created using the parent (root) PID instead of the child (daemon_uid) when daemon_uid is used.
  • The DEBUG log file will now be re-opened when a HUP signal is sent.
  • When debugging is enabled, flush buffers every sleep cycle so we can tail the debug log file.
  • Don't print messages to the console when starting in daemon mode unless debugging is enabled or an error occurs.
  • 'Could not open debug output file!' is no longer reported when debugging is disabled.
  • Added snmptt.logrotate file from Ville Skytta.
  • Fixed a bug (1748512) with handling escaped quotes in a trap message.
  • Updated snmptt-net-snmp-test to test MIB descriptions.
  • SNMPTTConvertMIB:
    • Fixed a bug (1678270) where a TRAP-TYPE / NOTIFICATION-TYPE line would not translate if it was split across two lines.

v1.2 - June 16th, 2007

  • When daemon_uid is used, two processes will now be spawned. The first process will be run as the same user that started SNMPTT (which should be root). The second will run as the daemon_uid user. This was changed so that SNMPTT could properly clean up the pid file on exit.
  • Added snmptt.ini option pid_file to allow for custom pid file locations when running in daemon mode.
  • Fixed bug where pid file did not contain the current pid of snmptt.
  • Added snmptt.ini options date_format, time_format, date_time_format, date_time_format_sql and stat_time_format_sql to allow the output format for $x and $X substitution variables, and the format of the date/time for text logs and SQL to be changed using strftime() variables. This allows for proper date/time data types to be used in SQL databases.
  • Added logging of trap statistics to a SQL table. Added *table_statistics snmptt.ini variable to define the table to be used.
  • Added ability to add custom columns to *_table and *_table_unknown tables. Added sql_custom_columns and sql_custom_columns_unknown snmptt.ini options.
  • Added snmptt.ini option unknown_trap_exec_format to allow custom output with substitutions.
  • Added the ability to log system messages to a text file in addtion to the existing syslog and Event Log. Added snmptt.ini options log_system and log_system_file.
  • Added a work-around to the Net-SNMP v5.4 traphandle bug (1638225) where the host name was set to . When detected, SNMPTT will use the host IP address instead.
  • Added a $H variable substitution to give the host name of the computer that is running SNMPTT, or a user defined value specified in the new snmptt_system_name snmptt.ini option.
  • Added MATCH support for bitwise AND
  • Added snmptt.ini option exec_escape to escape wildards (* and ?) in EXEC, PREEXEC and the unknown_trap_exec commands. This is enabled by default for Linux and Unix (or anything non-Windows) to prevent the wildcards from being expanded by the shell.
  • Moved unknown_trap_exec to Exec section in snmptt.ini.
  • Added 'use strict' pragma in source code.
  • Experimental: Added threads (Perl ithreads) support for EXEC. When enabled, EXEC commands will launch in a thread to allow SNMPTT to continue processing other traps. Added snmptt.ini options threads_enable and threads_max.
  • Fixed bug where snmptt tried to log to syslog when changing UIDs even if syslog_system_enable was set to 0.
  • Fixed a bug in REGEX with handling of captures. Text::Balanced module is now required.
  • Fixed a bug under Windows where SNMPTT was trying to log to syslog instead of the event log.
  • Fixed a bug where SNMPTT was attempting to log to syslog / eventlog when using the --time option.
  • Fixed a bug in MATCH where the i modifier was not handled correctly.
  • Added information to Nagios section of documentation for using SNMP traps as heartbeats by using freshness checks.
  • Added information to Nagios section of documentation for using freshness checks to automatically clear trap alerts.
  • SNMPTTConvertMIB:
    • Fixed a bug (1438394) where ARGUMENTS lines that have $1, $2 etc instead of %0, %1 would not translate.
    • Fixed a bug where a --#SEVERITYMAP line would be used instead of --#SEVERITY.

v1.1 - January 17th, 2006

  • Added PREEXEC snmptt.conf file option to allow an external program to be run before processing the FORMAT and EXEC lines. The output of the external program is stored in the $p_n_ variable where n is a number starting from 1. Multiple PREEXEC lines are permitted. The first PREEXEC stores the result of the command in $p1, the second in $p2 etc. Any ending newlines are removed. The snmptt.ini parameter pre_exec_enable can be used to enable / disable it.
  • MATCH statement now accepts any variable name instead of only enterprise variables. Example: MATCH $s:(Normal)
  • Added NODES MODE= snmptt.conf file option to allow you to select either POS (positive - the default) or NEG (negative) for NODES matches. If set to NEG, then NODES is a 'match' only if none of the NODES entries match.
  • Added unknown_trap_exec snmptt.ini option. If defined, the command will be executed for ALL unknown traps. Passed to the command will be all standard and enterprise variables, similar to unknown_trap_log_file but without the newlines.
  • snmptt --dump which dumps all the configured EVENTs, now displays duplicate EVENT entries to assist with troubleshooting duplicate entries trap logs.
  • If the debug log file can not be opened, a message is now logged to syslog if syslog_system_enable is enabled, and to the Event Log if eventlog_system_enable is enabled
  • Fixed bug with PostgreSQL where some trap data was interpreted as 'placeholders' in the INSERT statement which caused logging errors. PostgreSQL now uses PREPARE / EXECUTE statements instead.
  • MySQL now uses PREPARE / EXECUTE statements instead of a single INSERT statement.
  • Fixed bug in NODES where NODES entries from previous EVENTs were not being purged correctly.
  • Fixed bug where snmptt --dump would attempt to log to syslog or the Event Log.
  • Fixed bug that prevented the wildcard .* from being accepted on the EVENT line.
  • Added Windows Event Log forwarding documentation to integration section.
  • SNMPTTConvertMIB:
    • Fixed a bug when --format_desc=n was used that caused extra trailing whitespaces to be added for every non existent line in the description.
    • Fixed bug that prevented some MIBs from being accepted due to spacing in the DEFINITIONS::= line.
    • Fixed bug in that prevented --ARGUMENTS {} from being parsed due to spacing.

1.0 - August 30, 2004

  • SQL database connections are now opened after forking to the background when running in daemon mode, and after changing users when running SNMPTT as a non-root user. This should prevent 'gone away' and other connection problems with SQL databases due to lost handles.
  • Added mysql_ping_on_insert, postgresql_ping_on_insert and dbd_odbc_ping_on_insert options to 'ping' the database before doing an INSERT. Also added the options mysql_ping_interval, postgresql_ping_interval and dbd_odbc_ping_interval to periodically ping the database. These options will help ensure the connection to the database remains available. If an error is returned, it will attempt to reconnect to the database. This should prevent SNMPTT from having to be restarted if the SQL server is not available due to an outage or a connection timeout due to no activity.
  • Added variable substitution $Fz which when used on an EXEC line will dump the translated FORMAT line. This will allow for simplified EXEC lines when they are the same as the FORMAT line (minus the command to execute of course).
  • Added variable substitutions $Fa, $Ff, $Fn, $Fr, $Ft, for alarm (bell), form feed (FF), newline (LF, NL), return (CR) and tab (HT, TAB)
  • Added variable substitution $D to dump the description text for FORMAT and EXEC lines. The descriptions can be pulled from either the SNMPTT.CONF or MIB files. This is controlled by the description_mode and description_clean snmptt.ini options.
  • Added support for logging unknown traps to a SQL table
  • Added logging of statistical information for total traps received, total traps translated, total traps ignored and total unknown traps. Statistics are logged at shut down, and optionally at a defined interval defined by the new snmptt.ini variable statistics_interval. Logging can also be forced by sending the SIGUSR1 signal, or by creating a file called !statistics in the spool folder.
  • Added the error number reported by MySQL to MySQL errors (system syslog, eventlog etc)
  • Added /usr/local/etc/snmp and /usr/local/etc paths to the list of default directories searched for snmptt.ini.
  • Added some friendly error messages when required Perl modules are not available
  • Fixed bug with with handling traps in symbolic format (snmptrapd without -On)
  • Fixed bug with with using printing $ symbols in FORMAT and EXEC lines
  • Added Simple Event Correlator (SEC) integration documentation
  • SNMPTTConvertMIB:
    • Fixed bug that prevented the variable list (OBJECTS) of V2 MIB files from being converted
    • Fixed bug that caused an infinite loop processing the VARIABLES/OBJECTS section if the line in the MIB file contained spaces after the closing bracket

0.9 - November 3rd, 2003

  • Syslog messages are now logged with snmptt[pid] instead of TRAPD for traps, and snmptt-sys[pid] instead of SNMPTT for system messages
  • Added the option daemon_uid which causes snmptt to change to a different user (uid) after launching on Unix systems running in daemon mode
  • Fixed bug that prevented ip addresses from being detected correctly with translate_value_oids
  • Fixed bug with MATCH that caused integer ranges from not being handled correctly
  • Separated SNMPTT, SNMPTTCONVERT, SNMPTTCONVERTMIB and FAQ / Troubleshooting documentation into separate documents

0.8 - September 16th, 2003

  • Added MATCH keyword support to allow trap matching based on values contained inside the trap enterprise variables
  • REGEX now supports substitution with captures and the modifiers i, g and e. The e modifier allows for complex REGEX expressions.
  • Added support for remote MySQL and PostgreSQL databases
  • Added PostgreSQL support for DBD:Pg
  • An EVENT can now contain mulitple EXEC lines
  • An EVENT can now contain mulitple NODES lines
  • Added the option dynamic_nodes to allow NODES files to be either loaded at startup or loaded each time an EVENT is processed
  • Added trapoid column for database logging to contain the actual trap received. The eventid column will contain the actual matched entry from the .conf file (which could be a wildcard OID)
  • Fixed bug that prevented some variables from displaying the correct values because the received trap OID was replaced with the actual EVENT entry
  • Fixed bug that caused OIDs not to be translated correctly with translate_value_oids enabled
  • Agent IP address is now used instead of 'host' IP address for NODES matches, the 'hostname' column in database logs and the $A variable
  • Variable $A now prints the agent host name. $aA prints the agent IP address.
  • Variable $E now prints the enterprise in symbolic form. $e prints the numeric OID
  • Variable $O now prints the trap in symbolic form. $o prints the numeric OID
  • Added new variable substitution $i to print the actual matched entry from the .conf file (which could be a wildcard OID)
  • Added the configuration option dns_enable to enable DNS lookups on host and agent IP addresses
  • If DNS is enabled, NODES entries are resolved to IP addresses and the IP address is used to perform the match. This will allow for aliases to be used.
  • Added the option resolve_value_ip_addresses to resolve ip addresses contained inside enterprise variable values
  • Changed snmptt.ini setting translate_trap_oid to translate_log_trap_oid
  • Changed snmptt.ini setting translate_oids to translate_value_oids
  • Added configuraiton settings: translate_enterprise_oid_format, translate_trap_oid_format, translate_varname_oid_format and db_translate_enterprise
  • $O follows translate_trap_oid_format, and $o is always the numerical trap OID
  • $E follows translate_enterprise_oid_format, and $e is always the numerical enterprise OID
  • The enterprise column when logging to a database now follows the db_translate_enterprise setting
  • Fixed bug with $# to report the correct number of enterprise variables (was 1 lower than it should have been)
  • Fixed bug with handling traps that contain quoted values that span multiple lines
  • PID file now created (/var/run/snmptt.pid or ./snmptt.pid) when running in daemon mode on Linux / Unix. snmptt-init.d script updated to remove the pid file when shutting down snmptt.
  • Added Nagios / Netsaint integration documentation
  • Added contrib folder
  • SNMPTTConvertMIB
    • Now prints the variables contained in each trap definition unless --no_variables is set. When using --net_snmp_perl it will also resolve the Syntax (INTEGER, OCTETSTR etc) and Description. If it's an INTEGER, will also print out the enumeration tags if any exist.
    • Improved compatability with MIB files

0.7 - April 17th, 2003

  • Fixes a vulnerability that prevents the possibility of injected commands contained in traps from being executed when using the EXEC feature
  • Added the ability for traps passed from snmptrapd or loaded from the snmptt.conf files to contain symbolic OIDs such as linkDown and IF-MIB::linkUp. This feature requires the UCD-SNMP / Net-SNMP Perl module
  • Added the configuration options translate_trap_oid and translate_oids to have the trap OID and OID values contained in the trap variables converted from numerical OID to symbolic form before logging. This feature requires the UCD-SNMP / Net-SNMP Perl module
  • Added support for logging of traps using PostgreSQL via DBI / DBD::PgPP
  • Added REGEX keyword support to allow user definable search and replace on FORMAT / EXEC lines
  • NODES entry can now contain a CIDR address (eg: 192.168.10.0/23), or a network range (192.168.10.0-192.168.11.255)
  • NODES entry can now contain a mix of host names, IP addresses, CIDR addresses, network ranges and filenames
  • Added the ability to force a reload of the configuration files while running in daemon mode by placing a file called !reload in the spool directory
  • Added snmptt-net-snmp-test program to perform various translations of numeric and symbolic OIDS to assist with determining if the installed Perl module
    will function as expected with SNMPTT
  • Fixed bug that prevented quoted text from being logged correctly to SQL databases
  • Fixed bug that would prevent the translation of integer values to enumeration tags and variable name substitutions when using Net-SNMP 5.0.x
  • SNMPTTConvertMIB
    • FORMAT / EXEC line can now contain any of the following:
      • --#SUMMARY or DESCRIPTION (use DESCRIPTION only if --#SUMMARY does not exist)
      • description or --#SUMMARY (use --#SUMMARY only if DESCRIPTION does not exist)
      • --#SUMMARY and DESCRIPTION
      • DESCRIPTION and --#SUMMARY
    • When using the DESCRIPTION to build the FORMAT / EXEC line, can now choose between using the first line of the DESCRIPTION field, or the first x number of sentences
    • The use of the --#SUMMARY and DESCRIPTION line for the FORMAT / EXEC line can be disabled
    • Support for multiple --#SUMMARY lines
    • Support for --#SEVERITY lines
    • The default of using the $* wildcard can be disabled
    • Conversion of the DESCRIPTION section to SDESC / EDESC can be disabled
    • EXEC line can be specified on the command line
    • NODES line can be specified on the command line

0.6 - March 25th, 2003

  • Logging:
    • Added support for logging of traps using DBD::ODBC
    • Fixed bug with Win32::ODBC connection not being closed on exit of SNMPTT
    • MySQL code cleanup
    • Added support for logging traps to the NT Event Log including the ability to select the Event Log level based on the severity level defined in the snmptt.conf file
    • Improved syslog support by adding the ability to select the syslog level based on the severity level defined in the snmptt.conf file
    • Added syslog and NT Event Log support for SNMPTT 'system' events such as startup, shutdown, errors handling spool directory / files, database connectivity errors etc
    • Added the option keep_unlogged_traps to have SNMPTT erase the spooled trap file only after it successfully logs to at least one or all log systems. This will help prevent traps from being lost due to logging problems.
  • Added ability to translate integer values to enumeration tags defined in MIB files. This feature requires the UCD-SNMP / Net-SNMP Perl module
  • Added new variable substitutions: $v_n_ (variable name), $+n(variable name:value), $-n (variable name (type):value), $+* (same as $+n but wildcard), and $-* (same as $-n but wildcard). Translation of the variable name using the MIB requires the UCD-SNMP / Net-SNMP Perl module.
  • If a variable is passed from snmptrapd that is blank, snmptt will replace it with (null)
  • Fixed bug that would prevent variables numbered 10 or higher from being translated correctly
  • Fixed bug with handling trap data that contains spaces but is not inside of quotes
  • Code cleanup to remove Perl warnings (-w)
  • Added separate debug file for snmptthandler
  • Cleaned up defaults code for snmptthandler
  • Added examples folder containg a sample snmptt.conf file and sample trap file
  • Added FAQ section to this document
  • Snmpttconvertmib
    • Code cleanup
    • Now uses new command line arguments (snmpttconvertmib -h for help).
    • Can now use either snmptranslate or the SNMP Perl module (Net-SNMP) to process MIB files
    • Can now add a NODES line when converting MIB files
    • Now checks the version of snmptranslate before converting the mib to ensure snmptranslate is called correctly
    • Fixed bug which would cause the last notification of a v2 MIB file not to be converted correctly

0.5 - February 12th, 2003

  • Spool file list sorted before processing to ensure traps are processed in the order they are received when in daemon mode
  • Added use_trap_time variable to config file for daemon mode to have SNMPTT use either the time from the spool file, or the current time. Changed SNMPTTHANDLER to output the current date and time on the first line of the spool file
  • Fixed bug with default variable settings being ignored. Defaults were not being set correctly if variable was not defined in the .ini file.
  • Fixed bug with SNMPTT ignoring the daemon mode parameter in the .ini file
  • Fixed bug with Nodes list not being flushed out for each processed trap when running in daemon mode
  • Added strip_domain and strip_domain_list configuration options to enable / disable the removal of the domain name from the host name passed to SNMPTT. Defaults to disabled (do not strip domain name)
  • SNMPTTCONVERTMIB now prepends the contents of the --#TYPE line (if present) to the --#SUMMARY line to provide a more descriptive FORMAT / EXEC line

0.4 - November 18th, 2002

  • Variable substitution changes:
    • $X = Date, $x = Time instead of $x being both date and time
    • $N = Event name instead of $S
    • Added $c, $@, $O, $o, $ar, $R, $aR, $G, $S
  • Configuration moved to a .ini file
  • MySQL support via DBI under Linux and Windows
  • ODBC support via Win32::ODBC under Windows

0.3 - September 11th, 2002

  • Daemon mode support for SNMPTT. When running as a daemon, SNMPTTHANDLER script is used to spool traps from SNMPTRAPD.
  • SNMPTTCONVERTMIB utility to convert trap / notify definitions from MIB files
  • Sample trap file (sample-trap) for testing
  • Command line options to SNMPTT
  • SNMPTT now strips UDP: and :port from IP addresses when using Net-SNMP 5.0+
  • NODES files can now contain comments
  • NODES files can now contain either host names or IP addresses
  • You can now have multiple definitions of the same trap so that different hosts / nodes can have different actions or one host have multiple actions
  • Configuration file can now contain a list of other configuration files to load

0.2 - July 10th, 2002

  • Improved debugging output
  • Severity substitution bug fix
  • SNMP V2 trap bug fix
  • UCD-SNMP v4.2.3 problem workaround

0.1 - April 18th, 2002

  • Initial release

Upgrading

v1.4.2 to v1.5beta1

To upgrade from v1.4.2 to v1.5 you should:

  1. Replace snmptt with the new version. Make sure the file is executable (chmod +x filename).
  2. Replace snmptthandler-embedded with the new version. Make sure the file is executable (chmod +x filename).
  3. Replace snmpttconvertmib with the new version. Make sure the file is executable (chmod +x filename).
  4. For systemd systems, replace the snmptt.service service file with the new version.
  5. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it.
  6. Secure your /etc/snmp or /etc/snmptt folder as described in the Securing SNMPTT section of the documentation.
  7. To enable IPv6 support, set ipv6_enable = 1 in snmptt.ini.

Notes:

  1. Starting with v1.5, you can use /etc/snmptt/ instead of /etc/snmp/ for your snmptt.ini file.
  2. DNS now requires the Perl module IO::Socket::IP.
  3. IPv6 requires the Perl module Net::IP.

v1.4 to v1.4.2

To upgrade from v1.4 to v1.4,1, you should:

  1. Replace snmptt with the new version. Make sure the file is executable (chmod +x filename).
  2. Confirm that the snmptt user account is a member of the appropriate groups such as nagios for Nagios and icingacmd for Icinga2.
  3. Backup your snmptt.ini file and then add the new option daemon_gid = snmptt below daemon_uid.
  4. To increase security:

    • If you are not currently using daemon mode and are running as root, please switch to daemon mode or run as different user such as snmptt.
    • Secure the spool folder with:

    • chown -R snmptt.snmptt /var/spool/snmptt

    • chmod -R 750 /var/spool/snmptt

    • Secure the /etc/snmp folder with

    • chown -R root.root /etc/snmp

    • chmod 755 /etc/snmp
    • chown snmptt.snmptt /etc/snmp/snmptt*
    • chmod 660 /etc/snmp/snmptt*

v1.3 to v1.4

To upgrade from v1.3 to v1.4, you should:

  1. Replace snmptt and snmptthandler-embedded with the new versions. Make sure the files are executable (chmod +x filename).
  2. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it. The only change is the addition of the net_snmp_perl_cache_enable option.
  3. Check your snmptt.conf files for any traps defined with LOGONLY. These entries will no longer have EXEC lines executed. In previous versions EXEC was exectued when it should not have been.
  4. The new snmptt.ini option net_snmp_perl_cache_enable defaults to on, so disable if required.

v1.2 to v1.3

To upgrade from v1.2 to v1.3, you should:

  1. Replace snmptt and snmpttconvertmib with the new versions. Make sure the files are executable (chmod +x filename).
  2. Copy snmptt-init.d to /etc/init.d/snmptt. Make sure the file is executable (chmod +x filename).
  3. Optional: Install and configure the snmptthandler-embedded trap handler. See Embedded handler for details.
  4. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it. The default log paths have changed so modify as needed.
  5. Setup log rotation by copying snmptt.logrotate to /etc/logrotate.d/snmptt and modifying as needed for the correct paths, rotate frequency etc.
  6. Enable any new features in snmptt.ini as required.

v1.1 to v1.2

To upgrade from v1.1 to v1.2, you should:

  1. Replace snmptt and snmpttconvertmib with the new versions contained in the v1.2 package. Make sure the files are executable (chmod +x filename).
  2. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it.
  3. Enable any new features in snmptt.ini as required.
  4. For Linux and Unix (or anything non-Windows), if you are using the daemon_uid option in snmptt.ini, and are monitoring the availability of snmptt by checking for the snmptt process, be aware that there will now be two snmptt processes running instead of one.
  5. For Linux and Unix (or anything non-Windows), the snmptt.ini exec_escape option is enabled by default which will escape wildcard characters (* and ?) for EXEC, PREEXEC and the unknown_trap_exec commands. Disable if required.

v1.0 to v1.1

To upgrade from v1.0 to v1.1, you should:

  1. Replace snmptt and snmpttconvertmib with the new versions contained in the v1.1 package. Make sure the files are executable (chmod +x filename).
  2. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it.
  3. Enable any new features in snmptt.ini as required.

v0.9 to v1.0

To upgrade from v0.9 to v1.0, you should:

  1. Replace snmptt, snmpttconvert, snmpttconvertmib, and snmptthandler with the new versions contained in the v1.0 package. Make sure the files are executable (chmod +x filename).
  2. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it
  3. If you are using a MySQL, PostgreSQL or ODBC (via DBD::ODBC) and do not want the database to be pinged before each INSERT, set mysql_ping_on_insert, postgresql_ping_on_insert or dbd_odbc_ping_on_insert to 0 in snmptt.ini. If you do not want the database to be pinged periodically, set mysql_ping_interval, postgresql_ping_interval or dbd_odbc_ping_interval to 0 in snmptt.ini.
  4. Enable any new features in snmptt.ini as required
  5. Test and report any issues to alex_b@users.sourceforge.net, or open a bug report at Sourceforge.

v0.8 to v0.9

To upgrade from v0.8 to v0.9, you should:

  1. Replace snmptt with the new version contained in the v0.9 package. Make sure the file is executable (chmod +x filename)
  2. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it
  3. If you have any external applications that monitor the syslog for SNMPTT or TRAPD messages, modify them to look for snmptt[pid] and snmptt-sys[pid] instead
  4. Enable any new features in snmptt.ini as required
  5. Test and report any issues to alex_b@users.sourceforge.net, or open a bug report at Sourceforge.

v0.7 to v0.8

To upgrade from v0.7 to v0.8, you should:

  1. Replace snmptt and snmpttconvertmib with the new versions contained in the v0.8 package. Make sure the files are executable (chmod +x filename)
  2. Replace your /etc/rc.d/init.d/snmptt file (cp snmptt-init.d /etc/rc.d/init.d/snmptt). Make sure the file is executable (chmod +x filename)
  3. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it
  4. In your snmptt.ini file, configure translate_log_trap_oid with translate_trap_oid value from old snmptt.ini
  5. In your snmptt.ini file, configure translate_value_oids with translate_oids value from old snmptt.ini
  6. In your snmptt.ini file, set dynamic_nodes to 1 if you want the NODES files to be loaded each time an event is processed which is how previous versions of snmptt worked
  7. In your snmptt.conf files, replace any $A with $aA unless you want agent host names to be used instead of IP addresses
  8. In your snmptt.conf files, replace any $E with $e unless you want Enterprise trap OID in symbolic format
  9. In your snmptt.conf files, replace any $O with $o unless you want Trap OID in symbolic format
  10. In your snmptt.conf files, append a g to the end of all REGEX lines to enable global search and replace
  11. Review other translate settings in snmptt.ini
  12. Enable any new features in snmptt.ini as required
  13. If you are using database logging, add a new column called trapoid
  14. If you are using database logging and you enable conversions of OIDs to long names, make sure the table columns are wide enough to hold them
  15. Test and report any issues to alex_b@users.sourceforge.net, or open a bug report at Sourceforge.

v0.6 to v0.7

To upgrade from v0.6 to v0.7, you should:

  1. Replace SNMPTT and SNMPTTCONVERTMIB with the new versions contained in the v0.7 package
  2. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it
  3. Enable any new features in snmptt.ini as required
  4. Test and report any issues to alex_b@users.sourceforge.net, or open a bug report at Sourceforge.

v0.5 to v0.6

To upgrade from v0.5 to v0.6, you should:

  1. Replace SNMPTTHANDLER, SNMPTT and SNMPTTCONVERTMIB with the new versions contained in the v0.6 package
  2. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it
  3. Enable any new features in snmptt.ini as required
  4. Test and report any issues to alex_b@users.sourceforge.net, or open a bug report at Sourceforge.

v0.4 to v0.5

To upgrade from v0.1, v0.2 to v0.3 to v0.4, you should:

  1. Set use_trap_time to 0 to have SNMPTT operate the same as v0.4, or leave as 1 (recommended default) and test
  2. Replace both SNMPTTHANDLER and SNMPTT with the new versions contained in the v0.5 package
  3. Backup your snmptt.ini file, replace it with the new version, and make any necessary configuration changes to it

v0.1, v0.2 or v0.3 to v0.4

To upgrade from v0.1, v0.2 to v0.3 to v0.4, you should:

  1. In your snmptt.conf file, replace all $x with $x $X (see What's New section)
  2. In your snmptt.conf file, replace all $S with $N (see What's New section)
  3. Configure the snmptt.ini as described in this file - configuration options are no longer stored in the snmptt and snmptthandler scripts
  4. If your snmptt.conf file contained a list of other snmptt.conf files instead of trap definitions, move that list to the snmptt.ini file

Installation

Overview

SNMPTT can be run in either daemon mode or standalone mode. Daemon mode is recommended. See Modes of Operation for more details.

The following outlines the general steps required to install and configure SNMPTT:

  1. Install Net-SNMP and SNMPTT as described below.
  2. Copy the sample snmptt.conf to your configuration folder.
  3. Modify snmptt.ini to include the snmptt.conf file and set any desired options.
  4. Start SNMPTT.
  5. Test using the sample trap file.
  6. Create an snmptt.conf file by hand, or using snmpttconvertmib.
  7. Configure your network devices to send traps to the Net-SNMP / SNMPTT machine.
  8. Initiate a trap on your network device and check the SNMPTT log files for the result.
  9. Secure the SNMPTT installation.

Installation - Unix

Package Manager

Packages are available for most Linux distributions and FreeBSD. Check your package manager to see if they have the latest version. If not, you can manually install using the steps below.

Manual installation

Steps:

  1. Install Net-SNMP and the Perl modules as listed int the Requirements section.

  2. Copy snmptt to /usr/sbin/ and ensure it is executable (chmod +x snmptt):

    cp snmptt /usr/sbin/
    chmod +x /usr/sbin/snmptt
    
  3. Create snmptt user:

    1. RedHat based systems:

      adduser -r snmptt
      
    2. Debian based systems:

      adduser --system --group snmptt
      
  4. Create /etc/snmptt and set permissions. Note: Starting with v1.5, you can use /etc/snmptt/ instead of /etc/snmp/ for your snmptt.ini file.m

    mkdir /etc/snmptt
    chown -R snmptt.snmptt /etc/snmptt
    chmod 750 /etc/snmptt
    
  5. Copy snmptt.ini to /etc/snmptt and edit the options inside the file:

    cp snmptt.ini /etc/snmptt/
    

    and

    vi /etc/snmptt/snmptt.ini
    
  6. Either copy examples/snmptt.conf.generic to /etc/snmptt/snmptt.conf (renaming the file during the copy) or use the touch command to create the file (touch /etc/snmptt/snmptt.conf).

    cp examples/snmptt.conf.generic /etc/snmptt/snmptt.conf
    

    or

    touch /etc/snmptt/snmptt.conf
    
  7. Create the log folder /var/log/snmptt/:

    mkdir /var/log/snmptt
    chown -R snmptt.snmptt /var/log/snmptt
    chmod -R 750 /var/log/snmptt
    
  8. Create the spool folder /var/spool/snmptt/:

    mkdir /var/spool/snmptt/
    chown -R snmptt.snmptt /var/spool/snmptt
    chmod -R 750 /var/spool/snmptt
    
  9. Startup scripts are included for SystemD (uses systemctl to control services) and SysVinit systems. Select one depending on your distribution.

    1. Systemd:

      1. Copy the script and remove executable flag if set.

        cp snmptt.service /usr/lib/systemd/system/snmptt.service
        chmod -x /usr/lib/systemd/system/snmptt.service
        
      2. Enable the service:

        systemctl enable snmptt.service
        
      3. Start the service:

        systemctl start snmptt.service
        
    2. SysVinit:

      1. Copy the script:

        cp snmptt-init.d /etc/rc.d/init.d/snmptt
        
      2. Add the service using chkconfig:

        chkconfig --add snmptt
        
      3. Configure the service to start at runlevel 2345:

        chkconfig --level 2345 snmptt on
        
      4. Start the service:

        service snmptt start
        
    3. Manually starting without SystemD or SysVinit:

      snmptt --daemon
      
  10. Check syslog to ensure SNMPTT started properly:

    grep snmptt /var/log/messages
    grep snmptt /var/log/syslog
    

    Example log messages:

    snmptt-sys[31442]: SNMPTT 1.5.2 started
    snmptt-sys[31442]: Loading /etc/snmptt/snmptt.conf
    snmptt-sys[31442]: Finished loading 64 lines from /etc/snmptt/snmptt.conf
    snmptt: PID file: /var/run/snmptt.pid
    snmptt-sys[31446]: Configured daemon_uid: snmptt
    snmptt-sys[31446]: Changing to UID: snmptt (1002), GID: snmptt (1002)
    

    Note: If SNMPTT doesn't start, try running it manually from the shell prompt to see if there are any missing Perl modules.

  11. A log rotation script is included which can be used to rotate the log files on Red Hat and other systems. Copy the file to the logrotate.d directory (renaming the file during the copy):

    cp snmptt.logrotate /etc/logrotate.d/snmptt
    

    Edit the /etc/logrotate.d/snmptt and update the paths and rotate frequency as needed.

    vi /etc/logrotate.d/snmptt
    

Net-SNMP handlers

Next we will install the Net-SNMP handler. There are two options: The standard handler and the embedded handler. The embedded handler is recommended.

Net-SNMP Standard handler

The standard handler is a small Perl program that is called by snmptrapd each time a trap is received when using daemon mode. The limitations of this handler are:

  • Each time a trap is received, a process must be created to run the snmptthandler program and snmptt.ini is read each time
  • SNMPv3 EngineID and names are not passed by snmptrapd to snmptthandler

The benefits of using this handler are:

  • Does not require Net-SNMP embedded Perl for snmptrapd
  • Has been around since v0.1 of SNMPTT
  • Sufficient for most installations

Steps:

  1. Copy snmptthandler to /usr/sbin/ and ensure it is executable (chmod +x snmptthandler).

    cp snmptthandler /usr/sbin/
    chmod +x /usr/sbin/snmptthandler
    
  2. Manually start snmptthandler to make sure there are no missing Perl modules:

    /usr/sbin/snmptthandler
    

    Missing Perl module example:

    Can't locate Time/HiRes.pm in @INC (you may need to install the Time::HiRes module) (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at /usr/sbin/snmptthandler-embedded line 42.
    BEGIN failed--compilation aborted at /usr/sbin/snmptthandler-embedded line 42.
    

    No missing Perl modules example. Errors below can be ignored:

    SNMPTTHANDLER started: Sun Feb 27 10:39:39 2022
    
    s = 1645976379, usec = 360145
    s_pad = 1645976379, usec_pad = 360145
    
    Data received:
    

    Press control-c to exit the handler.

  3. For SNMPTT daemon mode:

    1. Modify (or create) the Net-SNMP snmptrapd.conf file by adding the following lines:

      disableAuthorization yes
      traphandle default /usr/sbin/snmptthandler
      

      Note: You can locate the snmptrapd.conf file by running:

      snmpconf -i
      
  4. For SNMPTT standlone mode:

    1. Modify (or create) the Net-SNMP snmptrapd.conf file by adding the following lines:

      disableAuthorization yes
      traphandle default /usr/sbin/snmptt
      

      Note: You can locate the snmptrapd.conf file by running:

      snmpconf -i
      

      Note: It is possible to configure snmptrapd to execute snmptt based on the specific trap received, but using the default option is preferred

  5. Permanently change snmptrapd to use the -On option by modifying the startup script:

    1. Systemd:

      Edit the unit file and add the -On option:

      systemctl edit --full snmptrapd.service
      

      Change:

      Environment=OPTIONS="-Lsd"
      

      to:

      Environment="OPTIONS=-Lsd -On"
      

      Note: Move the first quote to before OPTIONS.

    2. SysVinit:

      Edit the /etc/rc.d/init.d/snmptrapd file and add "-On" to OPTIONS:

      vi /etc/rc.d/init.d/snmptrapd
      

      Change:

      OPTIONS="-Lsd"
      

      to:

      OPTIONS="-Lsd -On"
      

    Note: The -On option is recommended. This will make snmptrapd pass OIDs in numeric form and prevent SNMPTT from having to translate the symbolic name to numerical form. If the Net-SNMP Perl module is not installed, then you MUST use the -On switch. Depending on the version of Net-SNMP, some symbolic names may not translate correctly. See the FAQ for more info.

    As an alternative, you can edit the Net-SNMP configuration file /etc/snmp/snmp.conf to include the line: printNumericOids 1. This setting will take effect no matter what is used on the command line.

  6. Start / restart snmptrapd using systemctl or service:

    systemctl restart snmptrapd
    service snmptrapd restart
    
  7. Check syslog to ensure SNMPTT started properly:

    grep snmptrapd /var/log/messages
    grep snmptrapd /var/log/syslog
    
  8. Follow the steps in the section Securing SNMPTT to ensure SNMPTT has been configured securely.

Net-SNMP Embedded handler

The embedded handler is a small Perl program that is loaded directly into snmptrapd when snmptrapd is started. The limitations of this handler are:

  • Requires embedded Perl for snmptrapd
  • Only works with daemon mode

The benefits of using this handler are:

  • The handler is loaded and initialized when snmptrapd is started, so there is less overhead as a new process does not need to be created and initialization is done only once (loading of snmptt.ini).
  • SNMPv3 EngineID and names variables are available in SNMPTT (B* variables)

Steps:

  1. Make sure snmptrapd has embedded Perl support enabled. To see if it's enabled in your installation, type:

    snmptrapd -H 2>&1 | grep perl
    

    If it returns perl PERLCODE then embedded Perl is enabled. If it's not enabled, you will need to find another Net-SNMP package with it enabled, or compile Net-SNMP using the --enable-embedded-perl configure option.

  2. Copy snmptthandler-embedded to /usr/sbin/. It does not need to be executable as it is called directly by snmptrapd.

    cp snmptthandler-embedded /usr/sbin/
    chmod +x /usr/sbin/snmptthandler-embedded
    
  3. Manually start snmptthandler-embedded to make sure there are no missing Perl modules:

    /usr/sbin/snmptthandler-embedded
    

    Missing Perl module example:

    Can't locate Time/HiRes.pm in @INC (you may need to install the Time::HiRes module) (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at /usr/sbin/snmptthandler-embedded line 42.
    BEGIN failed--compilation aborted at /usr/sbin/snmptthandler-embedded line 42.
    

    No missing Perl modules example. Errors below can be ignored:

    Bareword "NETSNMPTRAPD_HANDLER_OK" not allowed while "strict subs" in use at /usr/sbin/snmptthandler-embedded line 264.
    Execution of /usr/sbin/snmptthandler-embedded aborted due to compilation errors.
    
  4. Configure snmptrapd and install the service:

    Modify (or create) the Net-SNMP snmptrapd.conf file by adding the lines below. Note: You can locate the snmptrapd.conf file by running snmpconf -i.

    disableAuthorization yes
    perl do "/usr/sbin/snmptthandler-embedded"
    
  5. Permanently change snmptrapd to use the -On option by modifying the startup script:

    1. Systemd:

      Edit the unit file and add the -On option:

      systemctl edit --full snmptrapd.service
      

      Change:

      Environment=OPTIONS="-Lsd"
      

      to:

      Environment="OPTIONS=-Lsd -On"
      

      Note: Move the first quote to before OPTIONS.

    2. SysVinit:

      Edit the /etc/rc.d/init.d/snmptrapd file and add "-On" to OPTIONS:

      vi /etc/rc.d/init.d/snmptrapd
      

      Change:

      OPTIONS="-Lsd"
      

      to:

      OPTIONS="-LsdOn"
      

    Note: The -On option is recommended. This will make snmptrapd pass OIDs in numeric form and prevent SNMPTT from having to translate the symbolic name to numerical form. If the Net-SNMP Perl module is not installed, then you MUST use the -On switch. Depending on the version of Net-SNMP, some symbolic names may not translate correctly. See the FAQ for more info.

    As an alternative, you can edit the Net-SNMP configuration file /etc/snmp/snmp.conf to include the line: printNumericOids 1. This setting will take effect no matter what is used on the command line.

  6. Start / restart snmptrapd using systemctl or service:

    systemctl restart snmptrapd
    service snmptrapd restart
    
  7. Check syslog to ensure SNMPTT started properly:

    grep snmptrapd /var/log/messages
    grep snmptrapd /var/log/syslog
    
  8. Follow the steps in the section Securing SNMPTT to ensure SNMPTT has been configured securely.

Note: The default snmptt.ini enables logging to snmptt.log and also syslog for both trap messages and snmptt system messages. Change the following settings if required: log_enable, syslog_enable and syslog_system_enable.

Testing

  1. Copy a sample trap file to the spool folder:

    cp examples/'#sample-trap.generic.daemon' /var/spool/snmptt/
    
  2. Check the snmptt.log file for the trap. Note: The date is from the sample trap file.

    tail /var/log/snmptt/snmptt.log
    
    Mon Aug 16 10:06:35 2004 .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" router01 - Link down on interface 3.  Admin state: 2.  Operational state: 3
    
  3. Check syslog for the trap from snmptt. Note: The date is from the sample trap file.

    grep 'snmptt\[' /var/log/messages
    grep 'snmptt\[' /var/log/syslog
    
    Feb 27 10:29:52 server1 snmptt[83096]: .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" router01 - Link down on interface 3.  Admin state: 2.  Operational state: 3
    
  4. Generate a linkDown trap using snmptrap:

    snmptrap -v 2c -c public 127.0.0.1 .1.3.6.1.6.3.1.1.5.3 .1.3.6.1.6.3.1.1.5.3 ifIndex i 2 ifAdminStatus i 1 ifOperStatus i 2
    
  5. Check the syslog file for the trap from snmptrapd:

    grep snmptrapd /var/log/messages
    grep snmptrapd /var/log/syslog
    
    Feb 27 11:04:03 bink snmptrapd[84697]: 2022-02-27 11:04:03 localhost [UDP: [127.0.0.1]:40290->[127.0.0.1]:162]:#012.1.3.6.1.6.3.1.1.4.1.0 = OID: .1.3.6.1.6.3.1.1.5.3#011.1.3.6.1.2.1.2.2.1.1 = INTEGER: 2#011.1.3.6.1.2.1.2.2.1.7 = INTEGER: up(1)#011.1.3.6.1.2.1.2.2.1.8 = INTEGER: down(2)
    

    Note: If you see the error 'SELinux is preventing /usr/sbin/snmptrapd from write access on the directory snmptt', then SELinux needs to be configured to allow snmptrapd to write to the spool folder.

  6. Check the snmptt.log file for the trap:

    tail /var/log/snmptt/snmptt.log
    
    Sun Feb 27 11:04:03 2022 .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" 127.0.0.1 - Link down on interface 2.  Admin state: 1.  Operational state: 2
    
  7. Check syslog for the trap from snmptt.

    grep 'snmptt\[' /var/log/messages
    grep 'snmptt\[' /var/log/syslog
    
    Feb 27 11:04:07 server1 snmptt[83096]: .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" 127.0.0.1 - Link down on interface 2.  Admin state: 1.  Operational state: 2
    

Troubleshooting:

  1. Enable debug mode by defining both DEBUGGING = 0 and DEBUGGING_FILE = /var/log/snmptt/snmptt.debug in /etc/snmptt/snmptt.ini and restart snmptt. If the file is not created, check syslog for errors. Either the DEBUGGING_FILE path is incorrect or there is a permissions error creating the debug log file.
  2. Make sure permissions have been set for the various folders and files.
  3. SELinux may interfere with snmptrapd and snmptt. Disable or reconfigure at your own discretion.
  4. Test running snmptt and the handlers as explained above to make sure there are no missing Perl modules.

Installation - Windows

The Net-SNMP trap receiver does not currently support embedded Perl, so only the standard trap handler can be used with Windows.

  1. Create the directory c:\snmp and copy snmptt and snmptthandler to it.

    md c:\snmp
    copy snmptt c:\snmpt\
    copy snmptthandler c:\snmp\
    
  2. Copy snmptt.ini-nt to %SystemRoot%\snmptt.ini (c:\windows\snmptt.ini) and edit the options inside the file.

    cp snmptt.ini-nt %SystemRoot%\snmptt.ini
    
  3. Either copy examples\snmptt.conf.generic to c:\snmp\snmptt.conf (renaming the file during the copy) or create the file using notepad.

    copy examples\snmptt.conf.generic c:\snmp\snmptt.conf  
    notepad c:\snmp\snmptt.conf
    
  4. Create the log folder c:\snmp\log\.

    md c:\snmp\log
    
  5. For SNMPTT standlone mode:

    1. Modify (or create) the Net-SNMP snmptrapd.conf file by adding the following line:

      traphandle default perl c:\snmp\snmptt
      

      Note: It is possible to configure snmptrapd to execute snmptt based on the specific trap received, but using the default option is preferred

  6. For SNMPTT daemon mode:

    1. Modify (or create) the Net-SNMP snmptrapd.conf file by adding the following line:
      traphandle default perl c:\snmp\snmptthandler
      
  7. Create the spool folder c:\snmptt\spool\.

    md c:\snmptt\spool
    
  8. Launch snmptt using:

    snmptt --daemon
    
  9. Start SNMPTRAPD using the command line: SNMPTRAPD -On:

    snmptrapd -On
    

    Note: The -On option is recommended. This will make snmptrapd pass OIDs in numeric form and prevent SNMPTT from having to translate the symbolic name to numerical form. If the Net-SNMP Perl module is not installed, then you MUST use the -On switch. Depending on the version of Net-SNMP, some symbolic names may not translate correctly. See the FAQ for more info.

    As an alternative, you can edit the Net-SNMP configuration file /etc/snmp/snmp.conf to include the line: printNumericOids 1. This setting will take effect no matter what is used on the command line.

  10. Follow the steps in the section Securing SNMPTT to ensure SNMPTT has been configured securely.

Windows EventLog:

If you have enabled Windows Event Log support, then you must install an Event Message File to prevent "Event Message Not Found" messages from appearing in the Event Log. Microsoft Knowledge Base article KB166902 contains information on this error.

The Event Message File is aincluded with SNMPTT is a pre-compiled binary DLL. To compile the DLL yourself, see 'Compiling' below.

To install the DLL:

  1. Backup your system

  2. Make sure Event Viewer is not open

  3. Copy bin\snmptt-eventlog.dll to %windir%\system32

    copy bin\snmptt-eventlog.dll %windir%\system32\
    
  4. Launch the Registry Editor

  5. Go to 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog\Application'

  6. Create a new subkey (under Application) called SNMPTT

  7. Inside of the SNMPTT key, create a new String Value called EventMessageFile. Give it a value of %windir%\system32\snmptt-eventlog.dll.

  8. Inside of the SNMPTT key, create a new DWORD Value called TypesSupported. Give it a value of 7.

To un-install the DLL:

  1. Backup your system

  2. Make sure Event Viewer is not open

  3. Launch the Registry Editor

  4. Go to 'HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Eventlog\Application'

  5. Delete the key SNMPTT

  6. Delete the file %windir%\system32\snmptt-eventlog.dll

    del %windir%\system32\snmptt-eventlog.dll
    

Compiling snmptt-eventlog.dll (MS Visual C++ required)

  1. If your environment is not already set up for command line compilation, locate vcvars32.bat, start a command prompt, and execute it (vcvars32.bat).

  2. cd into the directory where snmptt-eventlog.mc is located (included with SNMPTT) and execute the following commands:

    mc snmptt-eventlog.mc
    
    rc /r snmptt-eventlog.rc
    
    link /nodefaultlib /INCREMENTAL:NO /release /nologo -base:0x60000000 -machine:i386 -dll -noentry -out:snmptt-eventlog.dll snmptt-eventlog.res
    
  3. Install the DLL as described above

Windows Service:

To configure SNMPTT as a service under Windows, follow these steps. More information can be obtained from the Windows Resource Kit.

  1. Install the Windows resource kit

  2. Copy the srvany.exe program to c:\windows\system32 from c:\Program Files\Resource Kit *

    copy srvany.exe c:\%windir%\system32\
    
  3. Install the SNMPTT service using:

    instsrv SNMPTT c:\windows\\system32\srvany.exe
    
  4. Configure the service:

    1. Launch REGEDIT

    2. Go to HKLM\SYSTEM\CurrentControlSet\SNMPTT

    3. Create a key called: Parameters

    4. Inside of Parameters, create a Sting Value (REG_SZ) called Application with the value of: c:\perl\bin\perl.exe

    5. Inside of Parameters, create a Sting Value (REG_SZ) called AppParameters with the value of: c:\snmp\snmptt --daemon

  5. Start the service from the control panel, or from a command prompt, type:

    net start snmptt
    
  6. Note: To remove the service, type:

    instsrv SNMPTT remove
    

Securing SNMPTT

As with most software, SNMPTT should be run without root or administrator privileges. Running with a non privileged account can help restrict what actions can occur when using features such as EXEC and REGEX.

For Linux and Unix, if you start SNMPTT as root, a user called 'snmptt' should be created and the snmptt.ini option daemon_uid should be set to the numerical user id (eg: 500) or textual user id (snmptt). Only define daemon_uid if starting snmptt using root.

If you start SNMPTT as a non-root user, then daemon_uid is not required (and will probably not work).

When using daemon_uid in daemon mode, there will be two SNMPTT processes. The first will run as root and will be responsible for creating the .pid file, and for cleaning up the .pid file on exit. The second process will run as the user defined by daemon_uid. If the system syslog (syslog_system_enable) is enabled, a message will be logged stating the user id has been changed. All processing from that point on will be as the new user id. This can be verified by looking the snmptt processes using ps.

For Windows, a local or domain user account called 'snmptt' should be created. If running as a Windows service, the service should be configured to use the snmptt user account. Otherwise the system should be logged in locally with the snmptt account before launching SNMPTT in daemon mode.

The script snmptthandler which is called from Net-SNMP's snmptrapd will be executed in the same security context as snmptrapd.

The SNMPTT user should be configured with the following permissions:

  • read / delete access to spool directory to be able to read new traps, and delete processed traps
  • read access to configuration files (snmptt.ini and all snmptt.conf files)
  • write access to log folder /var/log/snmptt/ or c:\snmp\log\.
  • any other permissions required for EXEC statements to execute

If snmptrapd is run as a non root / administrator, it should be configured with the following permissions below. Note: SELinux may prevent writing to the folder.

  • write access to spool directory

Grant access and secure the spool folder with:

    chown -R snmptt.snmptt /var/spool/snmptt
    chmod -R 750 /var/spool/snmptt

Grant access and secure the log folder with:

    chown -R snmptt.snmptt /var/log/snmptt
    chmod -R 750 /var/log/snmptt

If you are using /etc/snmp to store the SNMPTT configuration files, secure the folder with:

    chown -R root.root /etc/snmp
    chmod 755 /etc/snmp
    chown snmptt.snmptt /etc/snmp/snmptt*
    chmod 660 /etc/snmp/snmptt*

If you are using /etc/snmptt to store the SNMPTT configuration files, secure folder with:

    chown -R snmptt.snmptt /etc/snmptt
    chmod 750 /etc/snmptt

Note: Starting with v1.5, you can use /etc/snmptt/ instead of /etc/snmp/ for your snmptt.ini file:

Note: It is recommended that only the user running snmptrapd and the snmptt user be given permission to the spool folder. This will prevent other users from placing files into the spool folder such as non-trap related files, or the !reload file which causes SNMPTT to reload.

Configuration Options - snmptt.ini

As mentioned throughout this document, configuration options are set by editing the snmptt.ini file.

For Linux / Unix, the following directories are searched to locate snmptt.ini:

/etc/snmptt/
/etc/snmp/
/etc/
/usr/local/etc/snmp/
/usr/local/etc/

Note: /etc/snmptt/ is new for v1.5.

For Windows, the file should be in %SystemRoot%\. For example, c:\windows.

If an alternative location is desired, the snmptt.ini file path can be set on the command line using the -ini= parameter. See Command Line Arguments.

A sample snmptt.ini is provided in this package. See the Installation section.

This file does not document all configuration options available in snmptt.ini. Please view the snmptt.ini for a complete description of all options.

Note: The default snmptt.ini enables logging to snmptt.log and also syslog for both trap messages and SNMPTT system messages. Change the following settings if required: log_enable, syslog_enable and syslog_system_enable.

Modes of Operation

SNMPTT can be run in two modes: daemon mode and standalone mode. Daemon mode is recommended.

Daemon mode

When SNMPTT is run in daemon mode, the snmptrapd.conf file would contain a traphandle statement such as:

traphandle default /usr/sbin/snmptthandler

or when using the embedded handler:

perl do "/usr/sbin/snmptthandler-embedded"

When a trap is received by SNMPTRAPD, the trap is passed to the SNMPTT handler script which performs the following tasks:

  • reads trap passed from snmptrapd
  • writes the trap in a new unique file to a spool directory such as /var/spool/snmptt
  • quits

SNMPTT running in daemon mode performs the following tasks:

  • loads configuration file(s) containing trap definitions at startup
  • reads traps passed from spool directory
  • searches traps for a match
  • logs, executes EXEC statement etc
  • sleeps for 5 seconds (configurable)
  • loops back up to 'reads traps passed from spool directory'

Using SNMPTTHANDLER and SNMPTT in daemon mode, a large number of traps per minute would be handled easily.

Running SNMPTT with the --daemon command line option or setting the mode variable in the snmptt.ini file to daemon will cause SNMPTT to run in daemon mode.

By setting the snmptt.ini variable use_trap_time to 1 (default), the date and time used for logging will be the date and time passed inside the trap spool file. If use_trap_time is set to 0, the date and time that the trap was processed by SNMPTT is used. Setting use_trap_time to 0 can result in inaccurate time stamps in log files due to the length of time SNMPTT sleeps between spool directory polling.

Note: When running on a non Windows platform, SNMPTT will fork to the background and create a pid file in /var/run/snmptt.pid if daemon_fork is set to 1. If the user is not able to create the /var/run/snmptt.pid file, it will attempt to create one in the current working directory.

Sending the HUP signal to SNMPTT when running as a daemon will cause it to reload the configuration file including the .ini file, snmptt.conf files listed in the .ini file and any NODES files if dynamic_nodes is disabled. A reload can also be forced by adding a file to the spool directory called !reload. The filename is not case sensitive. If this file is detected, it will flag a reload to occur and will delete the file. This would be the only way to cause a reload when using Windows as Windows does not support signals.

Statistical logging of total traps received, total traps translated and total unknown traps can be enabled by setting the statistics_interval snmptt.ini variable to a value greater than 0. At each interval (defined in seconds), the statistics will be logged to syslog or the event log.

Sending the USR1 signal will also cause the statistical information for total traps received, total traps translated and total unknown traps to be logged. This could be used for example if you want to log statistics at a set time each day using a task scheduler instead of using the interval time defined in the snmptt.ini variable statistics_interval. A statistics dump can also be forced by adding a file to the spool directory called !statistics which is processed similar to the !reload file.

Standalone mode

To use SNMPTT in standalone mode, the snmptrapd.conf file would contain a traphandle statement such as:

    traphandle default /usr/sbin/snmptt

When a trap is received by snmptrapd, the trap is passed to the /usr/sbin/snmptt script. SNMPTT performs the following tasks:

  • reads trap passed from snmptrapd
  • loads configuration file(s) containing trap definitions
  • searches traps for a match
  • logs, executes EXEC statements etc
  • snmptt exits

With a 450 Mhz PIII (way back in 1998) and a 9000 line snmptt.conf containing 566 unique traps (EVENTs), it took under a second to process the trap including logging and executing the qpage program. The larger the snmptt.conf file is, the longer it will take to process. If there are a large number of traps being received, daemon mode should be used. If it takes 1 second to process one trap, then obviously you shouldn't try to process more than one trap per second. Daemon mode should be used instead.

Running SNMPTT without the --daemon command line option will result standalone mode unless the mode variable in the snmptt.ini file is set to daemon. For standalone mode, the mode variable in the snmptt.ini file should be set standalone.

Note: Enabling the Net-SNMP Perl module will greatly increase the startup time of SNMPTT. Daemon mode is recommended.

Command line arguments

The following command line arguments are supported:

Usage:  
    snmptt [<options>]  
Options:  
    --daemon                Start in daemon mode  
    --debug=n               Set debug level (1 or 2)  
    --debugfile=filename    Set debug output file  
    --dump                  Dump (display) defined traps  
    --help                  Display this message  
    --ini=filename          Specify path to snmptt.ini file  
    --version               Display author and version information  
    --time                  Use to see how long it takes to load and  
                            process trap file (eg: time snmptt --time)

Logging

Logging - Standard

Translated traps can be sent to standard output and to a log file. The output format is:

date trap-oid severity category hostname - translated-trap

To configure standard output or regular logging, edit the snmptt.ini file and modify the following variables:

stdout_enable
log_enable
log_file
log_format

The output format can be changed from the above default by modifying the log_format setting. See snmptt.ini for details.

Logging - Unknown traps

Logging of unrecognized traps is possible. This would be used mainly for troubleshooting purposes.

To configure unknown trap logging, edit the snmptt.ini file and modify the following variables:

enable_unknown_trap_log
unknown_trap_log_file

Unknown traps can be logged to a SQL table as described in the Database section.

Logging - Syslog

Translated traps can be sent to syslog. The format of the entries will be similar to above without the date (as syslog adds the date):

trap-oid severity category hostname - translated-trap

Syslog entries normally start with: date hostname snmptt[pid]:

To configure syslog, edit the snmptt ini file and modify the following variables:

syslog_enable
syslog_facility
syslog_level
syslog_module
syslog_remote_dest *
syslog_remote_port *
syslog_remote_proto *
syslog_rfc_format *
syslog_app *
syslog_format

(*) Only applicable when using syslog_module = 1

When using the default syslog_module setting of 0, syslog messages are logged to the local system using Unix sockets following the RFC3164 standard.

To enable RFC5424 or remote syslog server support, set syslog_module to 1 and define the (*) settings. Be sure to enable UDP or TCP syslog reception in your syslog server. See snmptt.ini for details on each setting.

SNMPTT system errors and messages such as startup, shutdown, trap statistics etc can be sent to syslog by editing the snmptt.ini file and modifying the following variables:

syslog_system_enable
syslog_system_facility
syslog_system_level

Syslog system entries normally start with: date hostname snmptt-sys[pid]:

The following errors are logged:

SNMPTT (version) started (*)
Unable to enter spool dir x (*)
Unable to open spool dir x (*)
Unable to read spool dir x (*)
Could not open trap file x (*)
Unable to delete trap file x from spool dir (*)
Unable to delete !reload file spool dir (*)
Unable to delete !statistics file spool dir (*)
Reloading configuration file(s) (*)
SNMPTT (version) shutdown (*)
Loading snmpttconfigfile (*)
Could not open configuration file: snmpttconfigfile(*)
Finished loading x lines from snmpttconfigfile (*)
MySQL error: Unable to connect to database
SQL error: Unable to connect to DSN
Can not open log file logfile
MySQL error: Unable to perform PREPARE
MySQL error: Unable to perform INSERT INTO (EXECUTE)
DBI DBD::ODBC error: Unable to perform INSERT INTO
Win32::ODBC error: Unable to perform INSERT INTO
PostgreSQL error: Unable to connect to database
PostgreSQL error: Unable to perform PREPARE
PostgreSQL error: Unable to perform INSERT INTO (EXECUTE)

* (daemon mode only)

Logging - Windows EventLog

When running under Windows, translated traps can be sent to the EventLog. All traps are logged under EventID 2 under the source SNMPTT. The format of the entries will be similar to above without the date (as the Event Log logs the date):

trap-oid severity category hostname translated-trap

To configure eventlog support, edit the snmptt ini file and modify the following variables:

eventlog_enable
eventlog_type

SNMPTT system errors can be sent to the Event Log by editing the snmptt.ini file and modifying the following variables:

eventlog_system_enable

The following errors are logged. Note that each error contains a unique EventID:

EventID 0: SNMPTT (version) started (*)
EventID 3: Unable to enter spool dir x (*)
EventID 4: Unable to open spool dir x (*)
EventID 5: Unable to read spool dir x (*)
EventID 6: Could not open trap file x (*)
EventID 7: Unable to delete trap file x from spool dir (*)
EventID 20: Unable to delete !reload file spool dir (*)
EventID 21: Unable to delete !statistics file spool dir (*)
EventID 8: Reloading configuration file(s) (*)
EventID 1: SNMPTT (version) shutdown (*)
EventID 9: Loading snmpttconfigfile (*)
EventID 10: Could not open configuration file: snmpttconfigfile(*)
EventID 11: Finished loading x lines from snmpttconfigfile(*) EventID 12: MySQL error: Unable to connect to database
EventID 13: SQL error: Unable to connect to DSN dsn
EventID 14: Can not open log file logfile
EventID 23: MySQL error: Unable to perform PREPARE
EventID 15: MySQL error: Unable to perform INSERT INTO (EXECUTE)
EventID 16: DBI DBD::ODBC error: Unable to perform INSERT INTO
EventID 17: Win32::ODBC error: Unable to perform INSERT INTO
EventID 18: PostgreSQL error: Unable to connect to database
EventID 22: PostgreSQL error: Unable to perform PREPARE
EventID 19: PostgreSQL error: Unable to perform INSERT INTO (EXECUTE)

* (daemon mode only)

Note:

To prevent "Event Message Not Found" messages in the Event Viewer, an Event Message File must be used. For information on installing the message file, see the Installation section for Windows.

Logging - Database

Translated and unrecognized traps can be sent to a database. MySQL (tested under Linux), PostgreSQL (tested under Linux) and ODBC (tested under Windows) can be used.

After configuring database logging below, you can also enable unknown trap logging by editing the snmptt.ini file and modifying the following variables:

enable_unknown_trap_log
db_unknown_trap_format

DBD::MySQL

To configure SNMPTT for MySQL, modify the following variables in the snmptt.ini file.

mysql_dbi_enable
mysql_dbi_host
mysql_dbi_port
mysql_dbi_database
mysql_dbi_table
mysql_dbi_table_unknown
mysql_dbi_username
mysql_dbi_password

Note: Sample values are defined in the default ini file. Defining mysql_dbi_table_unknown is optional.

The following MySQL script will create the database and table. Permissions etc should also be defined. Run 'mysql' as root and enter:

CREATE DATABASE snmptt;   
USE snmptt;

DROP TABLE snmptt;   
CREATE TABLE snmptt (   
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,   
eventname VARCHAR(50),   
eventid VARCHAR(50),   
trapoid VARCHAR(100),  
enterprise VARCHAR(100),   
community VARCHAR(20),   
hostname VARCHAR(100),   
agentip  VARCHAR(16),   
category VARCHAR(20),   
severity VARCHAR(20),   
uptime  VARCHAR(20),   
traptime VARCHAR(30),   
formatline VARCHAR(255));

Note: To store the traptime as a real date/time (DATETIME data type), change 'traptime VARCHAR(30),' to 'traptime DATETIME,' and set date_time_format_sql in snmptt.ini to %Y-%m-%d %H:%M:%S.

Note: If you do not want the auto-incrementing id column, remove the 'id INT...' line.

If logging of unknown traps to a SQL table is required, create the snmptt_unknown table using:

USE snmptt;

DROP TABLE snmptt_unknown;   
CREATE TABLE snmptt_unknown (   
trapoid VARCHAR(100),  
enterprise VARCHAR(100),   
community VARCHAR(20),   
hostname VARCHAR(100),   
agentip  VARCHAR(16),   
uptime  VARCHAR(20),   
traptime VARCHAR(30),   
formatline VARCHAR(255));

Note: To store the traptime as a real date/time (DATETIME data type), change 'traptime VARCHAR(30),' to 'traptime DATETIME,' and set date_time_format_sql in snmptt.ini to %Y-%m-%d %H:%M:%S.

If logging of statistics to a SQL table is required, create the snmptt_statistics table using:

USE snmptt;

DROP TABLE snmptt_statistics;  
CREATE TABLE snmptt_statistics (  
stat_time VARCHAR(30),  
total_received BIGINT,  
total_translated BIGINT,  
total_ignored BIGINT,  
total_skipped BIGINT,  
total_unknown BIGINT);

Note: Only include total_skipped if unknown_trap_nodes_match_mode is set to 1.

Note: To store the stat_time as a real date/time (DATETIME data type), change 'stat_time VARCHAR(30),' to 'stat_time DATETIME,' and set stat_time_format_sql in snmptt.ini to %Y-%m-%d %H:%M:%S.

Note: The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment.

To add a user account called 'snmptt' with a password of 'mytrap' for use by SNMPTT, use the following SQL statement:

GRANT ALL PRIVILEGES ON \*.\* TO 'snmptt'@'localhost' IDENTIFIED BY 'mytrap';

DBD::PgPP (PostgreSQL)

To configure SNMPTT for PostgreSQL, modify the following variables in the snmptt.ini file.

postgresql_dbi_enable
postgresql_dbi_module
postgresql_dbi_hostport_enable
postgresql_dbi_host
postgresql_dbi_port
postgresql_dbi_database
postgresql_dbi_table
postgresql_dbi_table_unknown
postgresql_dbi_username
postgresql_dbi_password

Note: Sample values are defined in the default ini file. Defining postgresql_dbi_table_unknown is optional.

The following shell / PostgreSQL commands will drop the existing database if it exists and then delete the existing snmptt user. It will then create a new snmptt database, a new snmptt user (prompting for a password) and then create the table. Run these commands as root.

su - postgres
dropdb snmptt  
dropuser snmptt

createuser -P snmptt  
createdb -O snmptt snmptt  
psql snmptt

DROP TABLE snmptt;  
CREATE TABLE snmptt (  
eventname VARCHAR(50),  
eventid VARCHAR(50),  
trapoid VARCHAR(100),  
enterprise VARCHAR(100),  
community VARCHAR(20),  
hostname VARCHAR(100),  
agentip  VARCHAR(16),  
category VARCHAR(20),  
severity VARCHAR(20),  
uptime  VARCHAR(20),  
traptime VARCHAR(30),  
formatline VARCHAR(255));

GRANT ALL ON snmptt TO snmptt;  
\q

Note: To store the traptime as a real date/time (timestamp data type), change 'traptime VARCHAR(30),' to 'traptime timestamp,' and set date_time_format_sql in snmptt.ini to %Y-%m-%d %H:%M:%S.

If logging of unknown traps to a SQL table is required, create the snmptt_unknown table using:

su - postgres  
psql snmptt

DROP TABLE snmptt_unknown;
CREATE TABLE snmptt_unknown (
trapoid VARCHAR(100),  
enterprise VARCHAR(100),  
community VARCHAR(20),  
hostname VARCHAR(100),  
agentip  VARCHAR(16),  
uptime  VARCHAR(20),  
traptime VARCHAR(30),  
formatline VARCHAR(255));

GRANT ALL ON snmptt_unknown TO snmptt;  
\q

Note: To store the traptime as a real date/time (timestamp data type), change 'traptime VARCHAR(30),' to 'traptime timestamp,' and set date_time_format_sql in snmptt.ini to %Y-%m-%d %H:%M:%S.

If logging of statistics to a SQL table is required, create the snmptt_statistics table using:

su - postgres  
psql snmptt


DROP TABLE snmptt_statistics;  
CREATE TABLE snmptt_statistics (  
stat_time VARCHAR(30),  
total_received BIGINT,  
total_translated BIGINT,  
total_ignored BIGINT,  
total_skipped BIGINT,  
total_unknown BIGINT);

GRANT ALL ON snmptt_statistics TO snmptt;  
\q

Note: Only include total_skipped if unknown_trap_nodes_match_mode is set to 1.

Note: To store the stat_time as a real date/time (timestamp data type), change 'stat_time VARCHAR(30),' to 'stat_time timestamp,' and set stat_time_format_sql in snmptt.ini to %Y-%m-%d %H:%M:%S.

The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment.

DBD::ODBC

SNMPTT can access ODBC data sources using either the DBD::ODBC module on Linux and Windows, or the WIN32::ODBC module on Windows.

To configure SNMPTT for ODBC access using the module DBD::ODBC, modify the following variables in the snmptt script.

dbd_odbc_enable = 1;
dbd_odbc_dsn = 'snmptt';
dbd_odbc_table = 'snmptt';
dbd_odbc_table_unknown = 'snmptt';
dbd_odbc_username = 'snmptt';
dbd_odbc_password = 'password';

Note:

SNMPTT does not create the DSN connection. You must define the DSN outside of SNMPTT. See 'Data Sources (ODBC)' in Windows help for information on creating a DSN connection.

Defining dbd_odbc_table_unknown is optional.

Sample values are defined in the default ini file.

The following MS SQL Server / Access script can create the table inside an existing database. Permissions etc should also be defined.

CREATE TABLE snmptt (
eventname character(50) NULL,
eventid  character(50) NULL,
trapoid character(100) NULL,
enterprise character(100) NULL,
community character(20) NULL,
hostname character(100) NULL,
agentip  character(16) NULL,
category character(20) NULL,
severity character(20) NULL,
uptime  character(20) NULL,
traptime character(30) NULL,
formatline character(255) NULL);

Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set date_time_format_sql in snmptt.ini to a compatible format. For example: %Y-%m-%d %H:%M:%S.

If logging of unknown traps to a SQL table is required, create the snmptt_unknown table using:

CREATE TABLE snmptt_unknown (
trapoid character(100) NULL,
enterprise character(100) NULL,
community character(20) NULL,
hostname character(100) NULL,
agentip  character(16) NULL,
uptime  character(20) NULL,
traptime character(30) NULL,
formatline character(255) NULL);

Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set date_time_format_sql in snmptt.ini to a compatible format. For example: %Y-%m-%d %H:%M:%S.

If logging of statistics to a SQL table is required, create the snmptt_statistics table using:

CREATE TABLE snmptt_statistics (  
stat_time character(30) NULL,  
total_received BIGINT NULL,  
total_translated BIGINT NULL,  
total_ignored BIGINT NULL,  
total_skipped BIGINT NULL,  
total_unknown BIGINT NULL);

Note: Only include total_skipped if unknown_trap_nodes_match_mode is set to 1.

Note: To store the stat_time as a real date/time, change 'stat_time character(30),' to the date/time data type supported by the database and and set stat_time_format_sql in snmptt.ini to a compatible format. For example: %Y-%m-%d %H:%M:%S.

All variables are inserted into the database using 'INSERT INTO' as text including the date and time. The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment.

Win32::ODBC

SNMPTT can access ODBC data sources using either the DBD::ODBC module on Linux and Windows, or the WIN32::ODBC module on Windows.

To configure SNMPTT for MS SQL via ODBC on Windows, modify the following variables in the snmptt script.

sql_win32_odbc_enable = 1;  
sql_win32_odbc_dsn = 'snmptt';  
sql_win32_odbc_table = 'snmptt';  
sql_win32_odbc_username = 'snmptt';  
sql_win32_odbc_password = 'password';

Note:

SNMPTT does not create the DSN connection. You must define the DSN outside of SNMPTT. See 'Data Sources (ODBC)' in Windows help for information on creating a DSN connection.

Defining sql_win32_odbc_table_unknown is optional.

Sample values are defined in the default ini file.

The following MS SQL Server script can create the table inside an existing database. Permissions etc should also be defined.

CREATE TABLE snmptt (
eventname character(50) NULL,
eventid  character(50) NULL,
trapoid character(50) NULL,
enterprise character(50) NULL,
community character(20) NULL,
hostname character(100) NULL,
agentip  character(16) NULL,
category character(20) NULL,
severity character(20) NULL,
uptime  character(20) NULL,
traptime character(30) NULL,
formatline character(255) NULL);

Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set date_time_format_sql in snmptt.ini to a compatible format. For example: %Y-%m-%d %H:%M:%S.

If logging of unknown traps to a SQL table is required, create the snmptt_unknown table using:

CREATE TABLE snmptt_unknown (
trapoid character(50) NULL,
enterprise character(50) NULL,
community character(20) NULL,
hostname character(100) NULL,
agentip  character(16) NULL,
uptime  character(20) NULL,
traptime character(30) NULL,
formatline character(255) NULL);

Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set date_time_format_sql in snmptt.ini to a compatible format. For example: %Y-%m-%d %H:%M:%S.

If logging of statistics to a SQL table is required, create the snmptt_statistics table using:

CREATE TABLE snmptt_statistics (  
stat_time character(30) NULL,  
total_received BIGINT NULL,  
total_translated BIGINT NULL,  
total_ignored BIGINT NULL,  
total_skipped BIGINT NULL,  
total_unknown BIGINT NULL);

Note: Only include total_skipped if unknown_trap_nodes_match_mode is set to 1.

Note: To store the stat_time as a real date/time, change 'stat_time character(30),' to the date/time data type supported by the database and and set stat_time_format_sql in snmptt.ini to a compatible format. For example: %Y-%m-%d %H:%M:%S.

All variables are inserted into the database using 'INSERT INTO' as text including the date and time. The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment.

Executing an external program

An external program can be launched when a trap is received. The command line is defined in the snmptt.conf configuration file. For example, to send a page using QPAGE (http://www.qpage.org), the following command line could be used:

qpage -f TRAP notifygroup1 "$r $x $X Compaq Drive Array Spare Drive on controller $4, bus $5, bay $6 status is $3."

$r is translated to the hostname, $x is the current date, and $X is the current time (described in detail below).

To enable or disable the execution of EXEC definitions, edit the snmptt.ini file and modify the following variable:

exec_enable

It is also possible to launch an external program when an unknown trap is received. This can be enabled by defining unknown_trap_exec in snmptt.ini. Passed to the command will be all standard and enterprise variables, similar to unknown_trap_log_file but without the newlines.

SNMPTT.CONF Configuration file format

The configuration file (usually /etc/snmp/snmptt.conf or c:\snmp\snmptt.conf) contains a list of all the defined traps.

If your snmptt.conf file is getting rather large and you would like to divide it up into many smaller files, then do the following:

  • create additional snmptt.conf files
  • add the file names to the snmptt_conf_files section in the snmptt.ini file.

For example:

snmptt_conf_files = <<END  
/etc/snmp/snmptt.conf.generic  
/etc/snmp/snmptt.conf.compaq  
/etc/snmp/snmptt.conf.cisco  
/etc/snmp/snmptt.conf.hp  
/etc/snmp/snmptt.conf.3com  
END

The syntax of the snmptt.conf file is:

EVENT event_name event_OID "category" severity

FORMAT format_string

[EXEC command_string]

[PREEXEC command_string]

[NODES sources_list]

[MATCH [MODE=[or | and]] | [$n:[!][( ) | n | n-n | > n | < n | x.x.x.x | x.x.x.x-x.x.x.x | x.x.x.x/x]]

[REGEX ( )( )[i][g][e]]

[SDESC]
[EDESC]

Note: Lines starting with a # are treated as comments and will be ignored.

Note: The EVENT and FORMAT line are REQUIRED. Commands in [] are optional. Do NOT include the []s in the configuration file!

EVENT

EVENT event_name event_OID "category" severity

event_name:

Unique text label (alias) containing no spaces. This would match the name on the TRAP-TYPE or NOTIFICATION-TYPE line in the MIB file when converted using snmpttconvertmib.

event_OID:

Object identifier string in dotted format or symbolic notation containing no spaces.

For example, a Compaq (enterprise .1.3.6.1.4.1.232) cpqHoGenericTrap trap (trap 11001) would be written as:

.1.3.6.1.4.1.232.0.11001

Symbolic names can also be used if the Net-SNMP Perl module is installed and enabled by setting net_snmp_perl_enable in the snmptt.ini file. For example:

linkDown

IF-MIB::linkDown

Notes:

  • Net-SNMP 5.0.9 and earlier does not support including the module name (eg: IF-MIB::) when translating an OID. A patch is available for 5.0.8+ that is included in Net-SNMP 5.1.1 and higher. The patch is available from the Net-SNMP patch page. If the version of Net-SNMP you are using does not support this feature and the event OID is specified with the module name, the event definition will be ignored. Also note that UCD-SNMP may not properly convert symbolic names to numeric OIDs which could result in traps not being matched.

  • SNMP V1 traps are in the format of enterprise ID (.1.3.6.1.4.1.232) followed by a 0, and then followed by the trap number (11001).

  • There can be multiple entries for the same trap OID in the configuration file. If multiple_event is enabled in the snmptt.ini, then it will process all matching traps. If multiple_event is disabled, only the first matching entry will be used.

Wildcards in dotted format notation can also be used. For example:

.1.3.6.1.4.1.232.1.2.*

Notes:

  • Specific trap matches are performed before wildcards so if you have an entry for .1.3.6.1.4.1.232.1.2.5 AND .1.3.6.1.4.1.232.1.2.*, it will process the .5 trap when received even if the wildcard is defined first.

  • If you want to capture all undefined traps, you can create a wildcard event at the end of your last snmptt.conf file using .*. For example:

 EVENT unknown .* "Status Events" Normal
  • Wildcard matches are only matched if there are NO exact matches. This takes into consideration the NODES list. Therefore, if there is a matching trap, but the NODES list prevents it from being considered a match, the wildcard entry will only be used if there are no other exact matches.

category:

  • Character string enclosed in double quotes ("). Used when logging output (see above).

  • If the category is "IGNORE", no action will take place even if the snmptt.conf contains FORMAT and / or EXEC statements.

  • If the category is "LOGONLY", the trap will be logged as usual, but the EXEC statements will be ignored.

Note: If you plan on using an external program such as Nagios, you probably do not want any traps defined with LOGONLY as the EXEC line would never be used to submit the passive service check.

severity:

  • Character string of the severity of the event. Used in the output when logging. Example: Minor, Major, Normal, Critical, Warning. The snmptt.ini contains options to match the syslog level or NT Event Log type to the severity level.

FORMAT

FORMAT format_string

There can be only one FORMAT line per EVENT.

The format string is used to generate the text that will be logged to any of the supported logging methods.

Variable substitution is performed on this string using the following variables:

$A - Trap agent host name (see Note 1)
$aA - Trap agent IP address
$Be - securityEngineID (snmpEngineID) (see Note 7)
$Bu - securityName (snmpCommunitySecurityName) (see Note 7)
$BE - contextEngineID (snmpCommunityContextEngineID) (see Note 7)
$Bn - contextName (snmpCommunityContextName) (see Note 7)
$c - Category
$C - Trap community string
$D - Description text from SNMPTT.CONF or MIB file (see Note 6)
$E - Enterprise trap OID in symbolic format
$e - Enterprise trap OID in number format (.1.3.6.1.4.1.n)
$j - Enterprise number (n)
$Fa - alarm (bell) (BEL)
$Ff - form feed (FF)
$Fn - newline (LF, NL)
$Fr - return (CR)
$Ft - tab (HT, TAB)
$Fz - Translated FORMAT line (EXEC, log_format and syslog_format only)
$G - Generic trap number (0 if enterprise trap)
$S - Specific trap number (0 if generic trap)
$H - Host name of the system running SNMPTT
$N - Event name defined in .conf file of matched entry
$i - Event OID defined in .conf file of matched entry (could be a wildcard OID)
$O - Trap OID in symbolic format (see Note 4)
$o - Trap OID in numerical format (see Note 4)
$pn - PREEXEC result n (1-n)
$pun - Unknown trap PREEXEC result n (1-n). See unknown_trap_preexec setting in snmptt.ini.
$R, $r - Trap hostname (see Note 1)
$aR, $ar - IP address
$s - Severity
$T - Uptime: Time since network entity was initialized
$X - Time trap was spooled (daemon mode) or current time (standalone mode)
$x - Date trap was spooled (daemon mode) or current date (standalone mode)
$# - Number of (how many) variable-bindings in the trap
$$ - Print a $
$@ - Number of seconds since the epoch of when the trap was spooled (daemon mode) or the current time (standalone mode)
$n - Expand variable-binding n (1-n) (see Note 2,5)
$+n - Expand variable-binding n (1-n) in the format of variable name:value (see Note 2,3,5)
$-n - Expand variable-binding n (1-n) in the format of variable name (variable type):value (see Note 2,3,5)
$vn - Expand variable name of the variable-binding n (1-n)(see Note 3)
$* - Expand all variable-bindings (see Note 5)
$+* - Expand all variable-bindings in the format of variable name:value (see Note 2,3,5)
$-* - Expand all variable-bindings in the format of variable name (variable type):value (see Note 2,3,5)

Example:

FORMAT NIC switchover to slot $3, port $4 from slot $5, port $6

Notes:

For the text log file, the output will be formatted as:

date time trap-OID severity category hostname - format

For all other log files except MySQL, DBD::ODBC and Win32::ODBC, the output will be formatted as:

trap-OID severity category hostname - format

For MySQL, DBD::ODBC and Win32::ODBC, the formatline column will contain only the format text.

Note (1):

See the section 'Name Resolution / DNS' for important DNS information.

Note (2):

If translate_integers is enabled in the snmptt.ini file, SNMPTT will attempt to convert integer values received in traps into text by performing a lookup in the MIB file.
You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling net_snmp_perl_enable in the snmptt.ini file.

For this feature to work, you must ensure Net-SNMP is configured correctly with all the required MIBS. If the option is enabled, but the value can not be found, the integer value will be used. If the MIB files are present, but translations do not occur, ensure Net-SNMP is correctly configured to process all the required mibs. This is configured in the Net-SNMP /etc/snmp/snmp.conf file. Alternatively, you can try setting the mibs_enviroment variable in snmptt.ini to ALL (no quotes) to force all MIBS to be initialized at SNMPTT startup.

If translate_integers is enabled while using stand-alone mode, it may take longer to process each trap due to the initialization of the MIB files.

Note (3):

$vn, $+n and $-n variable names and variable type are translated into the text name by performing a lookup in the MIB file. You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling net_snmp_perl_enable in the snmptt.ini file. If net_snmp_perl_enable is not enabled, the $vn variable will be replaced with the text 'variablen' where n is the variable number (1+).

For the name translation to work, you must ensure Net-SNMP is configured correctly with all the required MIBS. If the option is enabled and the correct name is not returned, ensure Net-SNMP is correctly configured to process all the required mibs. This is configured in the Net-SNMP snmp.conf file. Alternatively, you can try setting the mibs_enviroment variable in snmptt.ini to ALL (no quotes) to force all MIBS to be initialized at SNMPTT startup.

Note (4):

If translate_trap_oid is enabled in the snmptt.ini file, SNMPTT will attempt to convert the numeric OID of the received trap into symbolic form such as IF-MIB::linkDown. You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling net_snmp_perl_enable in the snmptt.ini file. If net_snmp_perl_enable is not enabled, it will default to using the numeric OID.

Net-SNMP 5.0.9 and earlier does not support including the module name (eg: IF-MIB::) when translating an OID and most of the 5.0.x versions do not properly tranlsate numeric OIDs to long symbolic names. A patch is available for 5.0.8+ that is included in Net-SNMP 5.1.1 and higher. The patch is available from the Net-SNMP patch page.

Note (5):

If translate_oids is enabled in the snmptt.ini file, SNMPTT will attempt to convert any numeric OIDs found inside the variables passed inside the trap to symbolic form. You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling net_snmp_perl_enable in the snmptt.ini file. If net_snmp_perl_enable is not enabled, it will default to using the numeric OID.

Net-SNMP 5.0.9 and earlier does not support including the module name (eg: IF-MIB::) when translating an OID and most of the 5.0.x versions do not properly tranlsate numeric OIDs to long symbolic names. A patch is available for 5.0.8+ that is inlcuded in Net-SNMP 5.1.1 and higher. The patch is available from the Net-SNMP patch page.

Note (6):

The snmptt.ini description_mode option must be set to either 1 or 2. If set to 1, the description is pulled from the SNMPTT.CONF files. If set to 2, the description is pulled from the MIB file. If using the MIB file, you must have the Net-SNMP Perl module installed and enabled.

Note (7):

These variables are only available when using the embedded trap handler for snmptrapd (snmptthandler-embedded).

EXEC

[EXEC command_string]

There can be multiple EXEC lines per EVENT.

Optional string containing a command to execute when a trap is received. The EXEC lines are executed in the order that they appear.

EXEC uses the same variable substitution as the FORMAT line. You can use $Fz on the EXEC line to add the translated FORMAT line instead of repeating what you already defined on FORMAT.

Examples:

EXEC /usr/bin/qpage -f TRAP alex "$r: $x $X - NIC switchover to slot $3, port $4 from slot $5, port $6"

FORMAT NIC switchover to slot $3, port $4 from slot $5, port $6
EXEC /usr/bin/qpage -f TRAP alex "$r: $x $X - $Fz"

EXEC c:\\snmp\\pager netops "$r: $x $X - NIC switchover to slot $3, port $4 from slot $5, port $6"

Note: Unlike the FORMAT line, nothing is prepended to the message. If you would like to include the hostname and date in the page above, you must use the variables such as $r, $x and $X.

Note: If the trap severity is set to LOGONLY in the snmptt.conf file, EXEC will not be executed.

PREEXEC

[PREEXEC command_string]

There can be multiple PREEXEC lines per EVENT.

Optional string containing a command to execute after a trap is received but before the FORMAT and EXEC statements are processed. The output of the external program is stored in the $pn variable where n is a number starting from 1. Multiple PREEXEC lines are permitted. The first PREEXEC stores the result of the command in $p1, the second in $p2 etc. Any ending newlines are removed. The snmptt.ini parameter pre_exec_enable can be used to enable / disable PREEXEC statements.

PREEXEC uses the same variable substitution as the FORMAT line.

Example:

EVENT linkDown .1.3.6.1.6.3.1.1.5.3 "Status Events" Normal  
FORMAT Link down on interface $1($p1). Admin state: $2. Operational state: $3  
PREEXEC /usr/local/bin/snmpget -v 1 -Ovq -c public $aA ifDescr.$1

Sample output:

Link down on interface 69("100BaseTX Port 1/6 Name SERVER1"). Admin state up. Operational state: down

In the above example the result is in quotes because that is what comes back from snmpget (it is not added by SNMPTT).

Note: PREEXEC will execute even if the trap severity is set to LOGONLY in the snmptt.conf file.

NODES

[NODES sources_list]

Used to limit which devices can be mapped to this EVENT definition.

There can be multiple NODES lines per EVENT.

Optional string containing any combination of host names, IP addresses, CIDR network address, network IP address ranges, or a filename. If this keyword is omitted then ALL sources will be accepted. Each entry is checked for a match. As soon as one match occurs, searching stops.

For example, if you only wanted devices on the subnet 192.168.1.0/24 to trigger this EVENT, you could use a NODES entry of:

NODES 192.168.1.0/24

Or, if you wanted devices on IPv4 subnet 192.168.1.0/24 and IPv6 subnet 2001:db8:a::/64:

 NODES 192.168.1.0/24 2001:db8:a::/64

If a filename is specified, it must be specified with a full path.

There are two modes of operation: POS (positive - the default) and NEG (negative). If set to POS, then NODES is a 'match' if any of the NODES entries match. If set to NEG, then NODES is a 'match' only if none of the NODES entries match. To change the mode of operation, use one of the following statements:

NODES MODE=POS
NODES MODE=NEG

A common use for this feature is when you have devices that implement a trap in a non-standard way (added additional variables for example) such as the linkDown and linkUp traps. By defining two EVENT statements and using NODES statements with NODES MODE, you can have one EVENT statement handle the standard devices, and the other handle the other devices with the extended linkDown / linkUp traps.

Example 1:

This example will match any hosts called fred, barney, betty or wilma:

NODES fred barney betty wilma

Example 2:

This example will match any hosts not called fred, barney, betty or wilma:

NODES fred barney betty wilma
NODES MODE=NEG

Example 3:

This example will load the file /etc/snmptt-nodes (see below), and match any hosts called fred, barney, betty, wilma, network ip addresses 192.168.1.1, 192.168.1.2, 192.168.1.3, 192.168.2.1, network range 192.168.50.0/22 or network range 192.168.60.0-192.168.61.255:

NODES /etc/snmptt-nodes

Example 4:

This example will load both files /etc/snmptt-nodes and /etc/snmptt-nodes2 (see above example):

NODES /etc/snmptt-nodes /etc/snmptt-nodes2

Example 5:

NODES 192.168.4.0/22 192.168.60.0-192.168.61.255 /etc/snmptt-nodes2

Example 6:

NODES fred /etc/snmptt-nodes pebbles /etc/snmptt-nodes2 barney

where snmptt-nodes contains:

fred  
barney betty  
# comment lines  
192.168.1.1 192.168.1.2 192.168.1.3  
192.168.2.1  
192.168.50.0/22  
192.168.60.0-192.168.61.255  
wilma

Notes:

  • The names are NOT case sensitive and comment lines are permitted by starting the line with a #.

  • CIDR network addresses must be specified using 4 octets followed by a / followed by the number of bits. For example: 172.16.0.0/24. Using 172.16/24 will NOT work.

  • Do not use any spaces between network ranges as they will be interpreted as two different values. For example, 192.168.1.1 - 192.168.1.20 will not work. Use 192.168.1.1-192.168.1.20 instead.

  • By default, NODES files are loaded when the snmptt.conf files are loaded (during startup of SNMPTT). The snmptt.ini option dynamic_nodes can be set to 1 to have the nodes files loaded each time an EVENT is processed.

  • See the section 'Name Resolution / DNS' for important DNS information.

  • See the section IPv6 for important IPv6 information.

MATCH

[MATCH [MODE=[or | and]] | [$n:[!][( )[i] | n | n-n | > n | < n | x.x.x.x | x.x.x.x-x.x.x.x | x.x.x.x/x]]

Optional match expression that must be evaluated to true for the trap to be considered a match to this EVENT definition.

If a MATCH statement exists, and no matches evaluate to true, then the default will be to NOT match this EVENT definition.

The following Perl regular expression modifiers are supported:

i - ignore case when trying to match

The following command formats are available:

MATCH MODE=[or | and]
MATCH $x: [!] (reg) [i]
MATCH $x: [!] n
MATCH $x: [!] n-n
MATCH $x: [!] < n
MATCH $x: [!] > n
MATCH $x: [!] & n
MATCH $x: [!] x.x.x.x
MATCH $x: [!] x.x.x.x-x.x.x.x
MATCH $x: [!] x.x.x.x/x
MATCH $x: [!] x:x:x
MATCH $x: [!] x:x:x/x

where:

or or and set the default evaluation mode for ALL matches
$x is any variable (example: $3, $A, $* etc)
reg is a regular expression
! is used to negate the result (not)
& is used to perform a bitwise AND
n is a number
x.x.x.x is an IP address
x.x.x.x-x.x.x.x is an IPv4 network address range
x.x.x.x/x is an IPv4 CIDR network addresss
x:x:x is an IPv6 addresss
x:x:x/x is an IPv6 CIDR network addresss

Notes:

  • To limit which devices can be mapped to this EVENT definition based on the IP address / hostname of the device / agent that sent the trap, the NODES keyword is recommended.

  • If the match mode is 'or', once a match occurs no other matches are performed and the end result is true.

  • If the match mode is 'and', once a match fails, no other matches are performed and the end result is false.

  • To use parentheses ( or ) in the search expression, they must be backslashed (\).

  • If no MATCH MODE= line exists, it defaults to 'or'.

  • There can be only one match mode per EVENT. If multiple MATCH MODE= lines exists, the last one in the list is used.

  • See the section IPv6 for important IPv6 information.

Examples:

$2 must be between 1000 and 2000:

MATCH $2: 1000-2000

Any one of the following must match (or): $3 must be 52, or $4 must be an IP address between 192.168.1.10 and 192.168.1.20, or the severity must be 'Major':

MATCH $3: 52
MATCH $4: 192.168.1.10-192.168.1.20
MATCH $s: (Major)

$6 must be IPv6 address 2002:0000:0000:1234:abcd:ffff:c0a8:0101 or in subnet 2002:0000:0000:1234:0000:0000:0000:0000/64:

MATCH $6: 2002::1234:abcd:ffff:c0a8:0101
MATCH $6: 2002:0000:0000:1234:0000:0000:0000:0000/64

All must match (and): $3 must be greater than 20, and $5 must not contain the words alarm or critical, $6 must contain the string '(1) remaining' and $7 must contain the string 'power' which is not case sensitive:

MATCH $3: >20  
MATCH $5: !(alarm|critical)  
MATCH $6: (\(1\) remaining)  
MATCH $7: (power)i  
MATCH MODE=and

The integer $1 must have bit 4 set:

MATCH $1: &8

REGEX

[REGEX( )( )[i][g][e]]

Optional regular expression to perform a search and replace on the translated FORMAT / EXEC line. Multiple REGEX ( )( ) lines are permitted.

First ( ) contains the search expression.
Second ( ) contains the replacement text

The following Perl regular expression modifiers are supported:

i - ignore case when trying to match left side
g - replace all occurances instead of only the first
e - execute the right side (eval) as code

To use substitution with captures (memory parenthesis) or the e modifier, you must first enable support in the snmptt.ini file by setting allow_unsafe_regex to 1. Note: This is considered unsafe because the contents of the right expression is executed (eval) by Perl which could contain unsafe code. If this option is enabled, BE SURE THAT THE SNMPTT CONFIGURATION FILES ARE SECURE!

Each REGEX line is processed in order from top to bottom and are accumulative. The second REGEX operates on the results of the first REGEX etc.

Example:

FORMAT line before: UPS has detected a building alarm. Cause: UPS1 Alarm #14: Building alarm 3.

REGEX (Building alarm 3)(Computer room high temperature)  
REGEX (Building alarm 4)(Moisture detection alarm)  
REGEX (roOm)(ROOM)ig  
REGEX (UPS)(The big UPS)  
REGEX (\s+)( )g

FORMAT line after: The big UPS has detected a building alarm. Cause: UPS1 Alarm #14: Computer ROOM high temperature

To use parentheses ( or ) in the search expression, they must be backslashed (\) otherwise it is interpreted as a capture (see below). The replacement text does not need to be backslashed.

Example:

FORMAT line before: Alarm (1) and (2) has been triggered

REGEX (\(1\))(One)  
REGEX (\(2\))((Two))

FORMAT line after: Alarm One and (Two) has been triggered

If allow_unsafe_regex is enabled, then captures can be used in the replacement text.

Example:

FORMAT line before: The system has logged exception error 55 for the service testservice

REGEX (The system has logged exception error (\d+) for the service (\w+))(Service $2 generated error $1)

FORMAT line after: Service testservice generated error 55

If allow_unsafe_regex is enabled and an e modifier is specified, then the right side is executed (evaluated). This allows you to use Perl functions to perform various tasks such as convert from hex to decimal, format text using sprintf etc. All text must be inside of quotes, and statements can be concatenated together using the dot (.).

Example 1:

FORMAT line before: Authentication Failure Trap from IP address: C0 A8 1 FE

REGEX (Address: (\w+)\s+(\w+)\s+(\w+)\s+(\w+))("address: ".hex($1).".".hex($2).".".hex($3).".".hex($4))ei

FORMAT line after: Authentication Failure Trap from IP address: 192.168.1.254

Example 2:

FORMAT line before: Authentication Failure Trap from IP address: C0 A8 1 FE

REGEX (Address: (\w+)\s+(\w+)\s+(\w+)\s+(\w+))("address:".sprintf("%03d.%03d.%03d.%03d",hex($1),hex($2),hex($3),hex($4)))ie

FORMAT line after: Authentication Failure Trap from IP address: 192.168.001.254

Example 3

This example is for a BGP bgpBackwardTranstion trap. The OID for the bgpBackwardTranstion trap has the IP address of the device that transitioned appended to the end of the OID. To create a meaningful trap message, the IP address needs to be separated from the variable OID. Because the IP address is part of the OID variable name instead of the OID value, a REGEX expression is needed. The following uses the $+1 variable on the FORMAT line so REGEX can parse out the IP address.

FORMAT line before: Peer:$+2

FORMAT line after substitution, but before REGEX: Peer:bgpPeerState.192.168.1.1:idle

REGEX (Peer:.\*\.(\d+\.\d+\.\d+\.\d+):(.\*))("Peer: $1 has transitioned to $2")e

FORMAT line after: Peer: 192.168.1.1 has transitioned to idle

Example 4

This example is a sample of using Perl subroutines inside of a REGEX statement.

FORMAT line before: Extremely severe error has occured

REGEX (Extremely severe error has occured)(("Better get a lotto ticket!!  Here is a lotto number to try:".sprintf ("%s", lottonumber());sub lottonumber { for(my $i=0;$i<6;$i++) { $temp = $temp . " " . (int(rand 49) +1); } return $temp; } )ie

FORMAT line after: Better get a lotto ticket!! Here is a lotto number to try: 36 27 38 32 29 6

Note: The REGEX expression is executed on the final translated FORMAT / EXEC line, after all variable substitutions have been completed.

SDESC

[SDESC]

Optional start of a description. All text between this line and the line EDESC will be ignored by SNMPTT. This section can be used to enter comments about the trap for your own use. If you use a SDESC, you MUST follow with a EDESC.

EDESC

[EDESC]

Used to end the description section.

Example:

SDESC  
Trap used when power supply fails in  
a server.  
EDESC

SNMPTT.CONF Configuration file Notes

When there are multiple definitions of the same trap in the configuration file, the following rules apply:

A match occurs when:

  • The received trap OID matches a defined OID in the configuration file
  • AND ( the hostname matches a defined hostname in the NODES entry OR there is no NODES entry )
  • AND ( the MATCH statement evaluates to TRUE OR the there is no MATCH entry )

If multiple_event is set to 1 in snmptt.ini:

  • A trap is handled as many times as it matches in the configuration file
  • If any number of exact matches exist, the wildcard match is NOT performed
  • If an exact match does NOT exist, the wildcard match IS performed if ( the hostname matches a defined hostname in the NODES entry OR there is no NODES entry ) AND ( the MATCH statement evaluates to TRUE OR the there is no MATCH entry )

If multiple_event is set to 0 in snmptt.ini:

  • A trap is handled once using the first match in the configuration file
  • If an exact match exists, the wildcard match is NOT performed
  • If an exact match does NOT exist, the wildcard match IS performed if ( the hostname matches a defined hostname in the NODES entry OR there is no NODES entry ) AND ( the MATCH statement evaluates to TRUE OR the there is no MATCH entry )

Name resolution / DNS

Snmptrapd passes the IP address of the device sending the trap (host name), the host name of the device sending the trap (host name, if configured to resolve host names) and the IP address of the actual SNMP agent (agent).

If the configuration setting dns_enable is set to 0 (dns disabled), then the host name of the AGENT will not be available for the $A variable, NODES matches, and the hostname column in SQL databases. The only exception to this is if the (host) IP address matches the (agent) IP address and snmptrapd is configured to resolve host names. In that case, the host name of the (host) will be used for the (agent) host name as they are obviously the same host.

If the configuration setting dns_enable is set to 1 (dns enabled), then the host name of both the host and the AGENT will be resolved via DNS. NODES entries will also be resolved to IP addresses before performing matches.

The host name may resolve to the Fully Qualified Domain Name (FQDN). For example: barney.bedrock.com. Adding an entry for the host in your /etc/hosts file or %systemroot%\system32\drivers\etc\hosts may result in the short name being used instead (barney). You can also enable the strip_domain / strip_domain_list options to have SNMPTT strip the domain of any FQDN host. See the snmptt.ini file for details.

To allow IP addresses to be resolved to host names, PTR records must exist in DNS or the local hosts file must contain all hosts.

It is recommended that either DNS be installed on the machine running SNMPTT / snmptrapd or a local hosts file be configured will all devices. DNS should be configured as a secondary (authoritive) for the domains that it will receive traps from. This will reduce network resolution traffic, speed up resolution, and remove the dependency of the network for DNS. If a local DNS or hosts file is not used, then the entire network management station could become useless during a DNS / remote network outage and could cause false alarms for network management software.

Sample SNMPTT.CONF files

Sample1

Note: The examples folder also contains a sample snmptt.conf file.

Following is a sample of two defined traps in snmptt.conf:

#  
EVENT COMPAQ_11003 .1.3.6.1.4.1.232.0.11003 "LOGONLY" Normal  
FORMAT Compaq Generic Trap: $*  
EXEC qpage -f TRAP notifygroup1 "Compaq Generic Trap: $*"  
NODES /etc/snmp/cpqnodes  
SDESC  
Generic test trap  
EDESC  
#  
#  
EVENT cpqDa3AccelBatteryFailed .1.3.6.1.4.1.232.0.3014 "Error Events" Critical  
FORMAT Battery status is $3.  
EXEC qpage -f TRAP notifygroup1 "$s $r $x $X: Battery status is $3"  
NODES ntserver1 ntserver2 ntserver3  
#  
#

Sample2

Following is a sample of a list of files to load in snmptt.ini:

snmptt_conf_files = <<END  
/etc/snmp/snmp-compaq.conf  
/etc/snmp/snmp-compaq-hsv.conf  
END

Following is a sample of one defined trap in /etc/snmp/snmptt-compaq.conf:

#  
EVENT COMPAQ_11003 .1.3.6.1.4.1.232.0.11003 "LOGONLY" Normal  
FORMAT Compaq Generic Trap: $*  
EXEC qpage -f TRAP notifygroup1 "Compaq Generic Trap: $*"  
NODES /etc/snmp/cpqnodes  
SDESC  
Generic test trap  
EDESC  
#

Following is a sample of one defined trap in /etc/snmp/snmptt-compaq-hsv.conf:

#  
EVENT mngmtAgentTrap-16025 .1.3.6.1.4.1.232.0.136016025 "Status Events" Normal  
FORMAT Host $1 : SCellName-TimeDate $2 : EventCode $3 : Description $4  
EXEC qpage -f TRAP notifygroup1 "Host $1 : SCellName-TimeDate $2 : EventCode $3 : Description $4"  
SDESC  
"Ema EMU Internal State Machine Error [status:10]"  
EDESC  
#

Notes

trapd.conf & MIB files

An existing HP Openview trapd.conf can be used in most cases but the file must be a VERSION 3 file. SNMPTT does not support all the variables implemented in HPOV, but most are available. The following variables may or may not match exactly to HPOV: $O, $o, $r, $ar, $R, $aR.

Some vendors (such as Compaq and Cisco ) provide a file that can be imported in to HP Openview using an HP Openview utility. snmpttconvert can be used to convert the file to snmptt.conf format.

Some vendors provide a MIB file that contains TRAP or NOTIFICATION definitions. snmpttconvertmib can be used to convert the file to snmptt.conf format.

IPv6

  • For incoming traps, a simple Perl regular expression is used to detect an IPv6 address for the trap host and Agent IPs as it is assumed that Net-SNMP is passing a valid IPv6 address. If the address has a zone index such as %ens160 or %1 appended to it, the zone index is removed. Zone indexes are not removed from enterprise variable values.
  • When DNS is enabled to resolve enterprise variables, the trap host and Agent IP (dns_enabled = 1), a complex regular expression is used to confirm that the IP address is valid before passing on to the IO::Socket::IP module for DNS resolution. The Dartware regular expression is used. Also see here for a test suite.
  • For NODES and MATCH, the Net::IP module is used as it allows you to test to see if two IP addresses are the same or if one IP address is within the range of another. IPv6 addresses can be represented in multiple ways so this module is used to ensure that an IP address such as 2002:0000:0000:1234:abcd:ffff:c0a8:0101 is matched to its shorthand version of 2002::1234:abcd:ffff:c0a8:0101. When running a modified version of the test suite above against Net::IP, it was found that it only passed 316 out of 490 tests compared to Dartmouth which passed 489 out of 490. There may be issues with matching some IP addresses passed in enterprise variables for MATCH but NODES should not be affected as Net-SNMP should be passing a supported format for both the host and agent IP addresses.

Limitations

Standalone mode only:

With a 450 Mhz PIII (way back in 1998) and a 9000 line snmptt.conf containing 566 unique traps (EVENTs), it took under a second to process the trap including logging and executing the qpage program. The larger the snmptt.conf file is, the longer it will take to process. If there are a large number of traps being received, daemon mode should be used. If it takes 1 second to process one trap, then obviously you shouldn't try to process more than one trap per second. Daemon mode should be used instead.

Note: Enabling the Net-SNMP Perl module will greatly increase the startup time of SNMPTT. Daemon mode is recommended.

Standalone or daemon mode:

The SNMPTRAPD program blocks when executing traphandle commands. This means that if the program called never quits, SNMPTRAPD will wait forever. If a trap is received while the traphandler is running, it is buffered and will be processed when the traphandler finishes. I do not know how large this buffer is.

The program called by SNMPTT (EXEC) blocks SNMPTT. If you call a program that does not return, SNMPTT will be left waiting. In standalone mode, this would cause snmptrapd to wait forever also.

Feedback & Bugs

Please send me any comments - good or bad - to alex_b@users.sourceforge.net. If you have any problems including converting trap files, please send me an email and include the file you are trying to convert and I will try to take a look at it.

Please also send any bug reports, patches or improvements so I can fix / add them and add it to the next release. You can also use Sourceforge for bugs and feature requests.

Integration with other software

Nagios

Overview

This section will outline the basic steps to integrate SNMPTT with Nagios Core. If you are using Nagios XI, see Handling SNMP Traps With Nagios.

Before attempting to integrate SNMPTT with Nagios, please ensure that you have a fully functioning SNMPTT system that can at least log translated traps to a log file.

Nagios Passive Service Checks

Passive service checks allow Nagios to process service check results that are submitted by external applications. Using SNMPTT's EXEC statement, the received trap can be passed to Nagios using the submit_check_result script included with Nagios. Once received by Nagios, Nagios will handle alerting for the trap.

One service is defined for each Nagios host that is to receive traps from SNMPTT. The benefits of using only one service entry is that it makes it easier to set up Nagios. Trying to define every possible trap for every host you have is not recommended. For example, after converting the MIBS from Compaq, there are over 340 traps defined. Trying to define this for every Compaq server would not be a good idea as 40 servers * 340 traps = 13,600 service definitions.

The downside of using only one service entry is that you will only see the last trap that was received on the Nagios console. Alerting will be handled by Nagios for each trap received but the console will only show the last one as being in the warning or critical state. The service will remain in this state until you manually force a service check unless you have freshness checking enabled (Nagios 2.0 and higher). See Clearing received traps in Nagios below.

Nagios Volatile Services

When defining the service for receiving the SNMPTT translated trap, the service must be defined as volatile. When a service is changed from an OK state to a non-OK state, contacts are notified. Normally, a service is Nagios in not defined volatile which means if another service check is performed and the state is still non-OK then NO contacts are notified. Because there is only one service entry for SNMP traps, we need to make sure we are contacted every time a trap comes in.

Creating the Nagios service entry

Following is a sample service entry for Nagios.

define service{
    host_name               server01            # Name of host
    service_description     TRAP                # Name of service.  What you use here must match the same value for the submit_check_result script
    is_volatile             1                   # Enables volatile services
    check_command           check-host-alive    # Used to reset the status to OK when 'Schedule an immediate check of this service' is selected.
    max_check_attempts      1                   # Leave as 1.
    normal_check_interval   1                   # Leave as 1.
    retry_check_interval    1                   # Leave as 1.
    active_checks_enabled   0                   # Prevent active checks from occuring as we are only using passive checks.
    passive_checks_enabled  1                   # Enables passive checks
    check_period            24x7                # Required for freshness checking.
    notification_interval   31536000            # Notification interval.  Set to a very high number to prevent you from
                                                #  getting pages of previously received traps (1 year - restart Nagios 
                                                #  at least once a year! - do not set to 0!).
    notification_period     24x7                # When you can be notified.  Can be changed
    notification_options    w,u,c               # Notify on warning, unknown and critical.  Recovery is not enabled so we do not 
                                                #  get notified when a trap is cleared.
    notifications_enabled   1                   # Enable notifications
    contact_groups          cg_core             # Name of contact group to notify
}

Note: To simplify the configuration, you can create a service template.

Note: Previous versions of this documentation defined a check_period of none, and did not set active_checks_enabled to 0. As of SNMPTT 1.2, setting active_checks_enabled to 0 instead of setting check_period to none is recommened as freshness checks require it. The recovery notification option has also been removed so we do not get notified when a trap is cleared.

Creating the SNMPTT EXEC statement

The Nagios distribution should contain the script submit_check_result in the contrib/eventhandlers directory. Create a directory called eventhandlers under libexec (/usr/local/nagios/libexec) and copy the submit_check_result script to that directory. Make sure the script is executable (chmod +x submit_check_result).

The submit_check_result script expects the following arguments:

host_name svc_description return_code plugin_output

The possible return codes are: 0=OK, 1=WARNING, 2=CRITICAL, -1=UNKNOWN. See the top of the submit_check_result script for a detailed description of each argument.

Create an EXEC statement such as the following for each EVENT entry in your snmptt.conf file:

EXEC /usr/local/nagios/libexec/eventhandlers/submit_check_result $r TRAP 1 "xxxxxx"

where "xxxxxx" is the text for the trap using the same format as the FORMAT statement. For example:

EXEC /usr/local/nagios/libexec/eventhandlers/submit_check_result $r TRAP 1 "Drive $1 in bay $2 has failed"

The variable substitution $r is used to pass the host name, TRAP matches the service definition defined above, 1 represents a WARNING, and "xxxxxx" is the same text used for your FORMAT line.

Instead of repeating the same text as the FORMAT line, you can instead use the $Fz variable in the EXEC statement. For example, to generate the EXEC command when using snmpttconvertmib:

Create a file called exec-commands.txt with (all on one line):

/usr/local/nagios/libexec/eventhandlers/submit_check_result $r TRAP 1 "$Fz"

Run snmpttconvertmib using:

snmpttconvertmib --in=/usr/share/snmp/mibs/mibname.mib --out=/etc/snmp/snmptt.conf --exec_mode=1 --exec_file=exec-commands.txt

Note: Run snmpttconvertmib -h for information on the command line options.

You must make sure that the host definition in Nagios matches the hostname that will be passed from SNMPTT using the $r variable. See the section 'Name Resolution / DNS' for important DNS information.

Clearing received traps in Nagios

Using the above configuration, once a trap is received for a host, it will remain in the WARNING state. To clear the trap from the Nagios console, open the TRAP service and click 'Schedule an immediate check of this service'. This will cause the defined service check to be run (check-host-alive in the example above) which will then change the status code to OK and clear the warning after a minute or so, assuming of course the system responds OK to the check-host-alive check. An alternative to using check-host-alive is to create a new command called reset-trap with:

#!/bin/sh  
/bin/echo "OK: No recent traps received"  
exit 0

Be sure to create a command definition in your commands.cfg file. See the 'Object configuration file options' section of the Nagios documentation.

Nagios 2.0 introduced service and host result freshness checks. Service freshness checks can be used to automatically reset the trap notification to an OK state by defining check_freshness and freshness_threshold in the service definition. Using freshness checks is recommended over normal active checks (defined by normal_check_interval) because the next check time of a normal active check does not change when a service changes state. Because of this, if you wanted to clear the trap after 24 hours, the last trap would be cleared some time between when it happened at 24 hours, depending on when the last active check was done. With freshness checking, the check command will be run freshness_threshold seconds after the last passive result was received.

For freshness checking to work, normal_check_interval must be set to 1, valid check_period should be set to 24x7 and the following service definitions should be added.

check_freshness         1           # Enable freshness checking
freshness_threshold     86400       # Reset trap alert every 24 hours.

SNMP heartbeat monitoring

If you have an application that sends periodic SNMP heartbeats, it is possible to use freshness checking to alert if a heartbeat has not been received.

To configure a heartbeat trap, start by creating a new service definition by following 'Creating the Nagios service entry' above, but use a new service_description such as MyApp_heartbeat. Next, add / change the following service definitions.

check_freshness         1                           # Enable freshness checking
freshness_threshold     1200                        # Check freshness every 20 minutes.
check_command           myapp_heartbeat_alarm_set   # Command to execute when a heartbeat is not received within freshness_threshold seconds.
notification_options    w,u,c,r                     # Notify on warning, unknown critical and recovery.

Note: For freshness checking to work, normal_check_interval must be set to 1, and valid check_period should be set to 24x7.

In this example, it is assumed that the heartbeat trap is received every 15 minutes, so a freshness_threshold of 20 minutes was selected in case the heartbeat was delayed.

Create the new myapp_heartbeat_alarm_set command for Nagios:

#!/bin/sh  
/bin/echo "CRITICAL: Heartbeat signal from MyApp was not received!"  
exit 2

Be sure to create a command definition in your commands.cfg file. See the 'Object configuration file options' section of the Nagios documentation.

Next, add an EXEC statement to the snmptt.conf file for the trap definition:

EXEC /usr/local/nagios/libexec/eventhandlers/submit_check_result $r MyApp_heartbeat 1 "Heartbeat signal from MyApp received."

As long as the traps are received, the MyApp_heartbeat service will have an OK status. If the heartbeat is not received, the freshness command will be executed which will set the status to CRITICAL.

Icinga

Overview

This section will outline the basic steps to integrate SNMPTT with Icinga. Some of the configuration and scripts were copied from Icinga's SNMPTT documentation.

Before attempting to integrate SNMPTT with Icinga, please ensure that you have a fully functioning SNMPTT system that can at least log translated traps to a log file.

Icinga Passive Service Checks

Passive service checks allow Icinga to process service check results that are submitted by external applications. Using SNMPTT's EXEC statement, the received trap can be passed to Icinga via the curl program. Once received by Icinga, Icinga will handle alerting for the trap.

In this guide, we setup one service definition for each Icinga host that is to receive traps from SNMPTT. The benefits of using only one service entry is that it makes it easier to set up Icinga. Trying to define every possible trap for every host you have is not recommended. For example, after converting the MIBS from Compaq, there are over 340 traps defined. Trying to define this for every Compaq server would not be a good idea as 40 servers * 340 traps = 13,600 service definitions.

The downside of using only one service entry is that you will only see the last trap that was received on the Icinga console. Alerting will be handled by Icinga for each trap received but the console will only show the last one as being in the warning or critical state. The service will remain in this state until you manually force a service check unless you have freshness checking enabled. See Clearing received traps in Icinga below.

Icinga Volatile Services

When defining the service for receiving the SNMPTT translated trap, the service must be defined as volatile. When a service is changed from an OK state to a non-OK state, contacts are notified. Normally, a service in Icinga is not defined volatile which means if another service check is performed and the state is still non-OK then NO contacts are notified. Because there is only one service entry for SNMP traps, we need to make sure we are contacted every time a trap comes in.

Creating the Icinga service entry

Following is a sample service entry for Icinga.

object Service "TRAP" {  
  host_name = "server1.domain"  
  import "generic-service"

  check_command         = "dummy"  
  event_command         = "trap-reset-event"

  enable_notifications  = 1  
  enable_active_checks  = 0  
  enable_passive_checks = 1  
  enable_flapping       = 0  
  volatile              = 1  
  enable_perfdata       = 0

  vars.dummy_state      = 0  
  vars.dummy_text       = "Manual reset."

  vars.sla              = "24x7"  
}

Note: To simplify the configuration, you can instead apply the service to hosts using an 'apply Service'. See the Icinga SNMPTT documentation for an example.

Creating the SNMPTT EXEC statement

Create an EXEC statement such as the following for each EVENT entry in your snmptt.conf file:

EXEC /usr/bin/curl -k -s -S -i -u apiuser:password -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/process-check-result' -d '{ "type": "Service", "filter": "host.name==\"$A\" && service.name==\"TRAP\"", "exit_status": 2, "plugin_output": "xxxxxx", "check_source": "$A", "pretty": true }'

where apiuser:password is the API username and password, xxxxxx is the text for the trap using the same format as the FORMAT statement.

The variable substitution $A is used to pass the host name for host.name and check_source, TRAP for service.name matches the service definition defined above, exit_status of 1 represents a WARNING, and xxxxxx is the same text used for your FORMAT line.

Instead of repeating the same text as the FORMAT line, you can instead use the $Fz variable in the EXEC statement. For example, to generate the EXEC command when using snmpttconvertmib:

Create a file called exec-commands.txt with (all on one line):

/usr/bin/curl -k -s -S -i -u apiuser:password -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/process-check-result' -d '{ "type": "Service", "filter": "host.name==\"$A\" && service.name==\"TRAP\"", "exit_status": 2, "plugin_output": "$Fz", "check_source": "$A", "pretty": true }'

Run snmpttconvertmib using:

snmpttconvertmib --in=/usr/share/snmp/mibs/mibname.mib --out=/etc/snmp/snmptt.conf --exec_mode=1 --exec_file=exec-commands.txt

Note: Run snmpttconvertmib -h for information on the command line options.

An API user must be defined in api-users.conf with the permission actions/process-check-result. Example:

object ApiUser "snmptt" {  
  password = "xxxxxxxxxxxxxxx"  
  permissions = [ "actions/process-check-result" ]  
}

You must make sure that the host definition in Icinga matches the hostname that will be passed from SNMPTT using the $A variable. See the section 'Name Resolution / DNS' for important DNS information.

Clearing received traps in Icinga

Using the above configuration, once a trap is received for a host, it will remain in the WARNING state. To clear the trap from the Icinga console, open the TRAP service and click 'Check Now'. This will cause the defined event check to be run (trap-reset-event in the example above) which will then change the status code to OK and clear the warning. For this to work, you must define an Icinga command:

object EventCommand "trap-reset-event" {  
  command = [ ConfigDir + "/scripts/trap_reset_event.sh" ]

  arguments = {  
    "-i" = "$service.state_id$"  
    "-n" = "$host.name$"  
    "-s" = "$service.name$"  
  }  
}

Create the trap_reset_event.sh script in ConfDir /scripts (/etc/icinga2/scripts) and make sure it's executable (chmod +x).

#!/bin/bash

SERVICE_STATE_ID=""  
HOST_NAME=""  
SERVICE_NAME=""

show_help()  
{  
cat <<-EOF  
    Usage: ${0##*/} [-h] -n HOST_NAME -s SERVICE_NAME  
    Writes a coldstart reset event to the Icinga command pipe.

      -h                  Display this help and exit.  
      -i SERVICE_STATE_ID The associated service state id.  
      -n HOST_NAME        The associated host name.  
      -s SERVICE_NAME     The associated service name.  
EOF  
}

while getopts "hi:n:s:" opt; do  
    case "$opt" in  
      h)  
          show_help  
          exit 0  
          ;;  
      i)  
          SERVICE_STATE_ID=$OPTARG  
          ;;  
      n)  
          HOST_NAME=$OPTARG  
          ;;  
      s)  
          SERVICE_NAME=$OPTARG  
          ;;  
      '?')  
          show_help  
          exit 0  
          ;;  
      esac  
done

if [ -z "$SERVICE_STATE_ID" ]; then  
    show_help  
    printf "\n  Error: -i required.\n"  
    exit 1  
fi

if [ -z "$HOST_NAME" ]; then  
    show_help  
    printf "\n  Error: -n required.\n"  
    exit 1  
fi

if [ -z "$SERVICE_NAME" ]; then  
    show_help  
    printf "\n  Error: -s required.\n"  
    exit 1  
fi

if [ "$SERVICE_STATE_ID" -gt 0 ]; then  
    echo "[`date +%s`] PROCESS_SERVICE_CHECK_RESULT;$HOST_NAME;$SERVICE_NAME;0;Auto-reset (`date +"%m-%d-%Y %T"`)." >> /var/run/icinga2/cmd/icinga2.cmd  
fi

To have the TRAP service automatically cleared 20 minutes after the last trap was received, modify the service entry to enable active checks and define a check_interval:

enable_passive_checks = 1  
check_interval        = 1200

Zabbix

Information on handling SNMP traps with Zabbix can be found in the Zabbix documentation.

SEC - Simple Event Correlator

Overview

Simple Event Correlator (SEC) is a free and platform independent event correlation tool.

This section will outline the basic steps to integrate SNMPTT with SEC. It will not attempt to explain how SEC works. There is very good documentation available on the SECs web page and a good introduction to SEC can be found here. You should be able to install and configuration SEC before attempting to integrate it with SNMPTT. You should also have a functioning SNMPTT system that can at least log translated traps to a log file.

This section outlines one method of integrating SEC with SNMPTT. Another method is documented in the March 2005 edition of Sys Admin Magazine in an article written by Francois Meehan. A copy of the article is available here.

Here are a couple of examples of why you would want to integrate SNMPTT with SEC:

  1. You have a 'noisy' device that constantly sends the same trap over and over again. It would be possible to simply disable the trap in SNMPTT, but you want the trap to be logged, just not excessively. The SEC 'SingleWithSupress' could be used to reduce the number of traps logged.
  2. Router interfaces often go up and down and you are receiving a trap for each event. You do not want to be alerted every time the interface 'bounces', but you do want to be alerted if it happens many times over a set period of time. You want to be alerted when the interface is down for more than 10 seconds, and then when the interface comes back up.

The following outlines how the flow of traps between SNMPTT and SEC could take place:

  1. SNMPTT receives a trap.
  2. SNMPTT logs the trap to a separate log file such as /var/log/snmptt/snmptt.sec.log using '/bin/echo ...' for the EXEC statement. No FORMAT line is defined so the trap is not logged to the regular snmptt.log log file (or SQL table if a SQL server is used).
  3. SEC monitors the log file for new entries.
  4. SEC correlates the messages from the log file.
  5. When a new alert needs to be generated by SEC based on its rules, SEC will call an external script which will feed the information back into SNMPTT as a trap using a user defined unique trap OID. The unique trap OID is defined in a custom snmptt.conf file (such as /etc/snmp/snmptt.conf.sec).
  6. SNMPTT will process the new trap as it would any other trap by logging to snmptt.log, a SQL table etc.

Configuration Overview

The following outlines how example 2 from above could be handled using SEC. This is a slightly modified version of the example from the SEC Examples page.

The example provides the following:

  • Prevents interface flapping from flooding the log files
  • Provides an 'unstable' and 'stable' alert based on how often the interface bounces.

The following steps need to be completed:

  1. Modify the Cisco snmptt.conf file to output linkDown and linkUp messages to a separate log file.
  2. Create a new snmptt.conf file to handle incoming alerts from SEC
  3. Create a SEC configuration file to correlate the linkDown / linkUp messages and pass new alerts to a script
  4. Create a script that will feed the messages from SEC back in to SNMPTT
  5. Test

1. Modify the Cisco SNMPTT.CONF file

The existing SNMPTT.CONF file needs to be modified to output the linkDown and linkUp messages to a separate log file for processing by SEC.

Following is an example snmptt.conf.cisco file modified to log a linkdown or linkup message to /var/log/snmptt/snmptt.sec.log. As you can see there are no FORMAT lines so the trap will not be logged to the regular SNMPTT log system.

EVENT Cisco_Link_Down .1.3.6.1.6.3.1.1.5.3.1.3.6.1.4.1.9 "Cisco Events" Minor  
EXEC /bin/echo "node=$A msg_text=cisco linkdown trap on interface $1" >> /var/log/snmptt/snmptt.sec.log  
SDESC  
This event occurs when the Cisco agent  
detects an interface has gone down.

A linkDown trap signifies that the sending  
protocol entity recognizes a failure in one of  
the communication links represented in the  
agent's configuration.  
EDESC  
#  
#  
#  
EVENT Cisco_Link_Up .1.3.6.1.6.3.1.1.5.4.1.3.6.1.4.1.9 "Cisco Events" Normal  
EXEC /bin/echo "node=$A msg_text=cisco linkup trap on interface $1" >> /var/log/snmptt/snmptt.sec.log  
SDESC  
This event occurs when the Cisco agent  
detects an interface has come back up.

A linkUp trap signifies that the sending  
protocol entity recognizes that one of the  
communication links represented in the agent's  
configuration has come up.  
EDESC  
#  
#  
#

2. Create a new SNMPTT.CONF file for incoming SEC alerts

A new SNMPTT.CONF file needs to be created which will handle the incoming traps from SEC.

Following is an example snmptt.conf.sec file to accept incoming traps from SEC. Use an enterprise OID that will not interferre with any other OIDs already configured on your system. For example, .1.3.6.1.4.1.9999.

EVENT Cisco_Link_DownUp .1.3.6.1.4.1.9999.1 "Cisco Events" Normal  
FORMAT $1  
#  
#  
#  
EVENT Cisco_Link_DownUp .1.3.6.1.4.1.9999.2 "Cisco Events" Major  
FORMAT $1  
#  
#  
#

3. Create a SEC configuration file

Following is a SEC configuration file that handles the even correlation for the Cisco traps. This file is the same as the file available on the SEC Examples page except comments and file paths have been modified.

########################################################  
          Sample SEC ruleset for SNMPTT  
########################################################

# process Cisco linkDown/linkUp trap events received from  
# SNMPTT via log file

type=PairWithWindow  
ptype=RegExp  
pattern=node=(\S+).*msg_text=cisco linkdown trap on interface (\S+)  
desc=CISCO $1 INTERFACE $2 DOWN  
action=event %s;  
continue2=TakeNext  
ptype2=RegExp  
pattern2=node=$1.*msg_text=cisco linkup trap on interface $2  
desc2=CISCO %1 INTERFACE %2 BOUNCE  
action2=event %s;  
window=20

type=SingleWithSuppress  
continue=TakeNext  
ptype=RegExp  
pattern=CISCO (\S+) INTERFACE (\S+) DOWN  
desc=cisco $1 interface $2 down  
action=reset +1 %s  
window=60

type=Pair  
ptype=RegExp  
pattern=CISCO (\S+) INTERFACE (\S+) DOWN  
desc=cisco $1 interface $2 down  
action=shellcmd /home/snmptt/cisco_msg $1 $2 major down  
ptype2=RegExp  
pattern2=node=$1.*msg_text=cisco linkup trap on interface $2  
desc2=cisco %1 interface %2 up  
action2=shellcmd /home/snmptt/cisco_msg %1 %2 normal up  
window=86400

type=SingleWith2Thresholds  
ptype=RegExp  
pattern=CISCO (\S+) INTERFACE (\S+) BOUNCE  
desc=cisco $1 interface $2 is unstable  
action=shellcmd /home/snmptt/cisco_msg $1 $2 major unstable  
window=3600  
thresh=10  
desc2=cisco $1 interface $2 is stable again  
action2=shellcmd /home/snmptt/cisco_msg $1 $2 normal stable  
window2=10800  
thresh2=0

Here is a quick breakdown of what each rule does:

First rule:

  • If a linkDown is received (node=x msg_text=cisco linkdown trap on interface x from SNMPTT), and then a linkUp is received within 20 seconds, it is considered a BOUNCE. A new 'event' is created with the internal SEC event 'CISCO %1 INTERFACE %2 BOUNCE' is created which is passed to the other rules.
  • If a linkDown is received and a linkUp is not received within 20 seconds, a new 'down' internal SEC event is created (CISCO $1 INTERFACE $2 DOWN) which is passed to the other rules.

Second rule:

  • Allows only one 'CISCO x INTERFACE x DOWN' message to be processed over 60 seconds.

Third rule:

  • When a SEC internally generated 'CISCO $1 INTERFACE $2 DOWN' message is found, it passes the host name, interface number and 'major down' to the cisco_msg script.
  • When a SEC internally generated 'CISCO $1 INTERFACE $2 UP' message is found, it passes the host name, interface number and 'normal up' to the cisco_msg script.

Fourth rule:

  • If ten 'CISCO %1 INTERFACE %2 BOUNCE' messages are detected over the span of 1 hour, it passes the host name, interface number and 'major unstable' to the cisco_msg script.
  • If after the last unstable alert there are no 'CISCO %1 INTERFACE %2 BOUNCE' messages for 3 hours, it passes the host name, interface number and 'normal stable' to the cisco_msg script.

4. Create a script to pass a trap back to SNMPTT

Following is a Perl script that passes the information passed from SEC back to SNMPTT by calling snmptthandler directly. This file is basically a modified Perl version of the shell script available on the SEC Examples page.

#!/usr/bin/perl  
#  
# the cisco_msg script:  
#  
use Socket;

node = shift(@ARGV);  
interface = shift(@ARGV);  
severity = shift(@ARGV);  
text  = shift(@ARGV);

temp_ipaddr = gethostbyname($node);  
if (defined($temp_ipaddr)) {  
  $ipaddr = Socket::inet_ntoa(scalar($temp_ipaddr));  
}  
else {  
  $ipaddr = "0.0.0.0";  
}

# use snmpget utility from Net-SNMP package  
ifname=`/usr/bin/snmpget -c public -OQv $NODE .1.3.6.1.2.1.2.2.1.2.$IF`  
description=`/usr/bin/snmpget -c public -OQv $NODE .1.3.6.1.4.1.9.2.2.1.1.28.$IF`

message="Interface $ifname ($description) $text";  
message=~s/\"/\'/g;

open (TRAP, "|/usr/sbin/snmptthandler");

select TRAP;

print "$node\n";  
print "$ipaddr\n";  
print ".1.3.6.1.2.1.1.3.0 00:00:00:00.00\n";  
if ($severity=~/normal/i) {  
  print ".1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.9999.1\n";  
}  
else {  
  print ".1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.9999.2\n";  
}  
print ".1.3.6.1.4.1.9999.1.1 $message\n";  
print ".1.3.6.1.6.3.18.1.3.0 $ipaddr\n";  
print ".1.3.6.1.6.3.18.1.4.0 public\n";  
print ".1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.9999\n";

close TRAP;

Windows Event Log forwarding

Overview

The Windows utility Event to Trap Translator (evntwin.exe and evntcmd.exe) can be used to configure Windows to forward user selectable Event Log entries to an SNMP manager when using the Microsoft SNMP service. SNMPTT can be configured to process these traps like any other trap. If the Event to Trap Translator is not already installed on your machine, it should be available from the Microsoft Resource Kit, SMS or after installation of the Microsoft SNMP service (Windows 2000 AS and Windows XP or higher).

This section will outline the basic steps to configure Windows to forward event log entries to Net-SNMP / SNMPTT when using the Microsoft SNMP server (not the Net-SNMP snmpd.exe agent). It will not attempt to explain how evntwin.exe and evntcmd.exe function. Documentation on using evntwin.exe and evntcmd.exe is available on the Microsoft web site and should be reviewed. You should have a functioning SNMPTT system that can at least log translated traps to a log file before attempting this.

SNMP Service

The Windows SNMP Service is the Microsoft SNMP agent which is responsible for handling SNMP requests from management stations such as queries for CPU utilization, disk space etc. The agent is also responsible for sending traps to management stations when an event occurs.

Note: The Microsoft SNMP Trap Service is used to RECEIVE SNMP traps which is similar to the Net-SNMP snmptrapd.exe daemon. The Microsoft SNMP Trap Service is NOT used to send traps and is not required.

Configuring the trap destination

The Windows SNMP agent needs to be configured to forward traps to your Net-SNMP / SNMPTT management station. This is done using the following steps:

  • Open Administrative Tools
  • Open Services
  • Open Local Policies
  • Open SNMP Service
  • Click the Traps tab
  • Enter a community name and click Add to List
  • Click Add and enter the IP address of the management station
  • Click Apply
  • Click OK
  • Right-click on SNMP Service and select Restart

After the service is restarted, a coldStart trap will be sent to the management station. If SNMPTT has been configured to translate coldStart messages, you should see a log entry similar to the following:

Thu Sep 9 21:33:06 2004 .1.3.6.1.6.3.1.1.5.1 Normal "Status Events" server1 - Device reinitialized (coldStart)

Note:If the SNMP Service is not listed in the Services Control Panel, then it needs to be installed using Add/Remove Programs. Under Add/Remove Windows Components, select Management and Monitoring Tools and then Simple Network Management Protocol.

Configuring the Event to Trap Translator

The following steps explain how to configure the Event to Trap Translator to forward system logon failures to SNMPTT:

  • Launch evntwin.exe
  • For Configuration Type select Custom
  • Click the Edit button
  • Inside Event Sources, expand Security and then click Security
  • Locate Event ID 529 (Logon Failure:%n%tReason:%t%tUnknown username or bad password%n.)
  • Click Add
  • Click OK
  • Click Apply

The SNMP agent should now forward all logon failures to the SNMP management station. A restart of the SNMP service should not be necessary.

Configuring SNMPTT to accept the Microsoft traps

An SNMPTT.CONF file needs to be created to handle the Microsoft traps. All Microsoft traps start with .1.3.6.1.4.1.311.1.13.1. For simplicity, a single SNMPTT.CONF EVENT entry will be used with a wildcard to accept all Microsoft traps. Following is an example snmptt.conf.microsoft file which needs to be included in the list of .conf files in the TrapFiles section in snmptt.ini:

EVENT EventLog .1.3.6.1.4.1.311.1.13.1.* "Regular" Normal
FORMAT EventLog entry: $1

The first enterprise variable ($1) contains the complete text that is displayed in the Event Log Description box. Variables are described in more detail in the Advanced Section.

After creating the snmptt.conf.microsoft file and adding it to the snmptt.ini, restart SNMPTT.

Testing

To test that the trap is received by SNMPTT, a logon failure in Windows should be created.

Your default installation of Windows may not create Event Log entries for unsuccessful logins. To configure Windows to log all failed logins, follow these steps:

  • Open Administrative Tools
  • Open Local Security Policy
  • Open Local Policies
  • Open Audit Policy
  • Enable auditing of failures for Audit account logon events
  • Enable auditing of failures for Audit logon events

The settings should take effect immediately, and a reboot should not be required.

To generate an event log entry, you can either log off and try to log on to the system with an invalid username and password, or use the runas.exe command from command prompt. For example:

runas /user:fakeuser cmd

When prompted for a password, press Enter.

SNMPTT should log something similar to the following:

Thu Sep 9 21:05:40 2004 .1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121.0.529 Normal "Regular" server1 - Event Log entry: Logon Failure:.....Reason:..Unknown user name or bad password.....User Name:.fakeuser.....Domain:.......Logon Type:.joint-iso-ccitt.....Logon Process:.seclogon.....Authentication Package:.Negotiate.....Workstation Name:.SERVER1.

The text in the log entry should match the text in the Description field of the Event Log entry but without the formatting.

Advanced Configuration

Specific EVENTs

Instead of using a wildcard EVENT entry to match all Microsoft traps, it is possible to create EVENT entries for each trap. As SNMPTT will only match using wildcard entries if there is no exact EVENT match, it may be desirable to create EVENT entries for a select number of important events, and keep the wildcard to catch any others.

To determine the trap OID that will be used for the EVENT, display the entry in evntwin.exe and combine the Enterprise OID, a 0 and the Trap Specific ID. For example, for the security event ID 529 used above:

Enterprise OID: 1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121

Trap Specific ID: 529

Based on the information above, the following EVENT line would be used::

EVENT EventLog 1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121.0.529 "Regular" Normal

Enterprise variables

Each trap sent from the Event to Trap Translator contains the text displayed in the Description, User and Computer fields for the Event Log. Also passed are the individual variables which are used by the Windows SNMP Service to create the Description field in the Event Log.

The following lists the enterprise variables that can be used in SNMPTT for each trap:

  • $1:Event Log Description
  • $2:Event Log User
  • $3:Event Log Computer
  • $4:?
  • $5:?
  • $6:Event to Trap Translator variable %1
  • $7:Event to Trap Translator variable %2
  • $8:Event to Trap Translator variable %3
  • $9:Event to Trap Translator variable %4
  • $n:Event to Trap Translator variable %n-5

As the individual variables are passed in the trap, it is possible to recreate the FORMAT line instead of using the passed Description ($1) field. For example, $1 in the previous example contains:

Logon Failure:.....Reason:..Unknown user name or bad password.....User Name:.fakeuser.....Domain:.......Logon Type:.joint-iso-ccitt.....Logon Process:.seclogon.....Authentication Package:.Negotiate.....Workstation Name:.SERVER1.

By reviewing the Description field as defined in the evntwin.exe utility, a new cleaned up FORMAT line can be used that does not contain all the dots.

Following is the text from the Description field in evntwin.exe which will be used as a reference. Notice the use of %n variables which are equivalent to the SNMPTT $n variables +5 (%1 = SNMPTT's $6). Note: In the example below, %n is a newline and %t is a tab while %n is a variable number.

Logon Failure:%n %tReason:%t%tUnknown user name or bad password%n %tUser Name:%t%1%n %tDomain:%t%t%2%n %tLogon Type:%t%3%n %tLogon Process:%t%4%n %tAuthentication Package:%t%5%n %tWorkstation Name:%t%6

The EVENT entry could be cleaned up using:

EVENT EventLog 1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121.0.529 "Regular" Normal  
FORMAT Logon Failure: Reason: Unknown user name or bad password. User Name: $6, Domain: $7, Logon Type: $8, Logon Process: $9, Auth package: $10, Workstation name: $11

Xymon / Hobbit

Information on handling SNMP traps with Xymon (formerly Hobbit) can be found at http://cerebro.victoriacollege.edu/hobbit-trap.html.

snmptt_1.5/docs/snmptt.md0000664000000000000000000054165614277306025014314 0ustar rootroot SNMP Trap Translator # SNMP Trap Translator v1.5 **([SNMPTT](http://www.snmptt.org))** This file was last updated on: August 17th, 2022 [License](#License) [SNMPTT](#What-is-it) * [What is it?](#What-is-it) * [Downloading](#Downloading) * [Requirements](#Requirements) * [What's New](#Whats-New) * [Upgrading](#Upgrading) * [Installation](#Installation) * [Overview](#Installation-Overview) * [Unix](#Installation-Unix) * [Package Manager](#Installation-Unix-Package) * [Manual installation](#Installation-Unix-Manual) * [Net-SNMP handlers](#Installation-Unix-Handlers) * [Net-SNMP Standard handler](#Installation-Unix-Standard) * [Net-SNMP Embedded handler](#Installation-Unix-Embedded) * [Testing](#Installation-Unix-Testing) * [Windows](#Installation-Windows) * [Securing SNMPTT](#SecuringSNMPTT) * [Configuration Options - snmptt.ini](#Configuration-Options) * [Modes of Operation](#Modes-of-Operation) * [Daemon Mode](#Modes-of-Operation-Daemon) * [Standalone Mode](#Modes-of-Operation-Standalone) * [Command line arguments](#Command-line-arguments) * [Logging](#Logging) * [Standard](#LoggingStandard) * [Unknown Traps](#LoggingUnknown) * [Syslog](#LoggingSyslog) * [Windows EventLog](#LoggingEventLog) * [Database](#LoggingDatabase) * [MySQL](#LoggingDatabase-MySQL) * [PostgreSQL](#LoggingDatabase-PostgreSQL) * [ODBC](#LoggingDatabase-ODBC) * [Windows ODBC](#LoggingDatabase-Windows_ODBC) * [Executing an external program](#Executing-an-external-program) * [SNMPTT.CONF Configuration file format](#SNMPTT.CONF-Configuration-file-format) * [EVENT](#SNMPTT.CONF-EVENT) * [FORMAT](#SNMPTT.CONF-FORMAT) * [Variable-substitutions](#Variable-substitutions) * [EXEC](#SNMPTT.CONF-EXEC) * [PREEXEC](#SNMPTT.CONF-PREEXEC) * [NODES](#SNMPTT.CONF-NODES) * [MATCH](#SNMPTT.CONF-MATCH) * [REGEX](#SNMPTT.CONF-REGEX) * [SDESC](#SNMPTT.CONF-SDESC) * [EDESC](#SNMPTT.CONF-EDESC) * [SNMPTT.CONF Configuration file Notes](#SNMPTT.CONF-Configuration-file-Notes) * [Name resolution / DNS](#DNS) * [Sample SNMPTT.CONF files](#Sample-SNMPTT.CONF-file) * [Sample1 SNMPTT.CONF file](#Sample1-SNMPTT.CONF-file) * [Sample2 SNMPTT.CONF file](#Sample2-SNMPTT.CONF-file) * [Notes](#Notes) * [trapd.conf & MIB files](#Notes-trapd.conf) * [IPv6](#Notes-ipv6) * [Limitations](#Limitations) * [Feedback & Bugs](#Feedback) * [Integration with other software](#Integration-with-other-software) * [Nagios](#Nagios-Netsaint) * [Icinga](#Icinga) * [Zabbix](#Zabbix) * [Simple Event Correlator (SEC)](#SEC) * [Windows Event Log forwarding](#EventWin) * [Xymon / Hobbit](#Hobbit) # License Copyright 2002-2022 Alex Burger alex\_b@users.sourceforge.net 4/3/2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # What is it? SNMPTT (SNMP Trap Translator) is an SNMP trap handler written in Perl for use with the Net-SNMP [snmptrapd](http://www.net-snmp.org/man/snmptrapd.html) program ([www.net-snmp.org](http://www.net-snmp.org)). SNMPTT supports Linux, Unix and Windows. Many network devices including but not limited to network switches, routers, remote access servers, UPSs, printers and operating systems such as Unix and Windows have the ability to send notifications to an SNMP manager running on a network management station. The notifications can be either SNMP Traps, or SNMP Inform messages. The notifications can contain a wide array of information such as port failures, link failures, access violations, power outages, paper jams, hard drive failures etc. The MIB (Management Information Base) available from the vendor determines the notifications supported by each device. The MIB file contains TRAP-TYPE (SMIv1) or NOTIFICATION-TYPE (SMIv2) definitions, which define the variables that are passed to the management station when a particular event occurs. The Net-SNMP program **snmptrapd** is an application that receives and logs SNMP trap and inform messages via TCP/IP. Following is a sample syslog entry for a Compaq cpqDa3LogDrvStatusChange trap that notifies that the drive array is rebuilding using numeric OIDs: Feb 12 13:37:10 server11 snmptrapd[25409]: 192.168.110.192: Enterprise Specific Trap (3008) Uptime: 306 days, 23:13:24.29, .1.3.6.1.2.1.1.5.0 = SERVER08, .1.3.6.1.4.1.232.11.2.11.1.0 = 0, .1.3.6.1.4.1.232.3.2.3.1.1.4.8.1 = rebuilding(7) Here is the same trap using symbolic OIDs. Feb 12 13:37:10 server11 snmptrapd[25409]: 192.168.110.192: Enterprise Specific Trap (3008) Uptime: 306 days, 23:13:24.29, sysName.0 = SERVER08, cpqHoTrapFlags.0 = 0, cpqDaLogDrvStatus.8.1 = rebuilding(7) The output from snmptrapd can be changed via the -O option to display numeric or symbolic OIDs and other display options, but it generally follows the format of variable name = value, variable name = value etc. A more descriptive / friendly trap message can be created using SNMPTT's variable substitution. Following is the same trap, logged with SNMPTT: ``` Feb 12 13:37:13 server11 TRAPD: .1.3.6.1.4.1.232.0.3008 Normal "LOGONLY" server08 - Logical Drive Status Change: Status is now rebuilding ``` The definition for the cpqDa3LogDrvStatusChange trap in the SNMPTT configuration file would be defined as follows: ``` FORMAT Logical Drive Status Change: Status is now $3. ``` The $3 represents the third variable as defined in the MIB file, which for this particular trap, is the cpqDaLogDrvStatus variable. Another example of an SNMPTT configuration entry is: ``` FORMAT Compaq Drive Array Spare Drive on controller $4, bus $5, bay $6 status is $3. ``` Which could result in the following output: ``` "Compaq Drive Array Spare Drive on controller 3, bus 0, bay 3 status is Failed." ``` SNMPTT can log to any of the following destinations: text log, syslog, Windows Event log or a SQL database such as MySQL, PostreSQL or an ODBC accessible database such as Microsoft SQL. External programs can also be run to pass th e translated trap to an email client, paging software, Nagios, Icinga etc. In addition to variable substitution, SNMPTT allows complex configurations allowing: * the ability to accept or reject a trap based on the host name, ip address, network range, or variable values inside of the trap enterprise variables * execute external programs to send pages, emails etc * perform regular expression search and replace on the translated message such as translating the variable value "Building alarm 4" to "Moisture detection alarm" As of SNMPTT 1.5, both IPv4 and IPv6 are supported. # Downloading SNMPTT can be downloaded from the [Sourceforge files page](https://sourceforge.net/projects/snmptt/files/snmptt/). The primary git source code repository is avaialble at [Sourceforge](https://sourceforge.net/p/snmptt/git/ci/master/tree/) and a mirror is available at [GitHub](https://github.com/AlexB7/snmptt). # Requirements * Perl 5.6.1 or higher. SNMPTT began development using 5.6.1 and although it is now developed with 5.30, it should still be backwards compatible 5.6.1. * To use snmptthandler-embedded, Net-SNMP's snmptrapd must be compiled with embedded Perl enabled (**--enable-embedded-perl** configuration option) ## Perl Modules | R/O | Program / Module | rpm | deb | :--- | :--- | :--- | :--- | | Required | [Net-SNMP](http://www.net-snmp.org) (formerly known as UCD-SNMP). Specifically **snmptrapd.** | net-snmp, net-snmp-utils | snmp, snmptrapd | | Optional | [Net-SNMP Perl module](http://www.net-snmp.org/FAQ.html#How_do_I_install_the_Perl_SNMP_modules_). Only required for features that perform conversions between symbolic and numeric OIDs. This is NOT the same as the Net::SNMP module availabe from CPAN. | net-snmp-perl | libsnmp-perl | | Required | [Text::ParseWords](https://metacpan.org/pod/Text::ParseWords) module (included with most distributions) | perl-Text-ParseWords | | | Required | [Getopt::Long](https://metacpan.org/pod/Getopt::Long) module (included with most distributions) | | | | Required | [Posix](http://search.cpan.org/search?module=POSIX) module (included with most if not all distributions) | | | | Required | [Config::IniFiles](https://metacpan.org/pod/Config::IniFiles) module | perl-Config-IniFiles (EPEL, PowerTools repos)| libconfig-inifiles-perl | | Required | [Time::HiRes](https://metacpan.org/pod/Time::HiRes) module (only required when using SNMPTT in daemon mode - required by **snmptthandler**) | perl-Time-HiRes | libtime-hires-perl | | Required | [Sys::Hostname](https://metacpan.org/pod/Sys::Hostname) module (included with most if not all distributions). | | | | Required | [File::Basename](https://metacpan.org/pod/File::Basename) module (included with most if not all distributions). | | | | Required | [Text::Balanced](https://metacpan.org/pod/Text::Balanced) module (included with most if not all distributions). |perl-Text-Balanced| | | Optional | [Net::IP](https://metacpan.org/pod/Net::IP) module. Required for IPv6 support. | perl-Net-IP (EPEL repo)| libnet-ip-perl | | Optional | [IO::Socket::IP](https://metacpan.org/pod/IO::Socket::IP) module (included with most if not all distributions). Required for DNS translations. | | | | Optional | [Sys::Syslog](https://metacpan.org/pod/Sys::Syslog) module (included with most Unix distributions). Required for Syslog support using Unix sockets (local syslog). | perl-Sys-Syslog | | | Optional | [Log::Syslog::Fast](https://metacpan.org/pod/Log::Syslog::Fast) and [Log::Syslog::Constants](https://metacpan.org/pod/Log::Syslog::Constants) modules. Required for remote syslog and RFC5424 support. | | | | Optional | [DBI](https://metacpan.org/pod/DBI) module. Required for DBD::MySQL, DBD::PgPP and DBD::ODBC support. | perl-DBI | libclass-dbi-perl | | Optional | [DBD::mysql](https://metacpan.org/pod/DBD::mysql) module. Required for MySQL support. | perl-DBD-MySQL | libdbd-mysql-perl | | Optional | [DBD::PgPP](https://metacpan.org/pod/DBD::PgPP) or [DBD:Pg](https://metacpan.org/pod/DBD::Pg) module. Required for PostgreSQL support. | perl-DBD-Pg | libdbd-pg-perl | | Optional | [DBD::ODBC](https://metacpan.org/pod/DBD::ODBC) module. Required for ODBC (SQL etc) access on Linux / Windows (Win32::ODBC not required if using DBD::ODBC) | perl-DBD-ODBC | libdbd-odbc-perl | | Optional | [Win32::ODBC](https://metacpan.org/pod/Win32::ODBC) module. Required for ODBC (SQL etc) access on Windows (DBD::ODBC not required if using Win32::ODBC) | | | | Optional | [threads](https://metacpan.org/pod/threads) and [Thread::Semaphore](https://metacpan.org/pod/Thread::Semaphore) modules (included with most if not all distributions). Required when enabling threads for EXEC statements. | perl-threads | libthreads-perl | | Optional | [Digest::MD5](https://metacpan.org/pod/Digest::MD5) module (included with most if not all distributions). Required when enabling duplicate trap detection. | perl-Digest-MD5 | libdigest-md5-perl |
All development and testing was done with Linux, Windows 2000 or higher and various versions of Net-SNMP from UCD SNMP v4.2.1 to the current Net-SNMP 5.7.x release. The Windows version has been tested with both native mode and under Cygwin. SNMP V1, V2 and V3 traps have been tested. The latest version of Net-SNMP is recommended. Note: * SNMPTT only requires the Net-SNMP Perl module if you want to have variable names translated into symbolic form, you want to be able to have **snmptrapd** pass traps using symbolic form, or you enable the options **translate\_integers**, **translate\_trap\_oid** or **translate\_oids**. Although not required, using the Perl module is recommended. It is also required if you want to use the **snmptthandler-embedded** trap handler with snmptrapd. # What's New ## **v1.5** **\- August 17th, 2022** * Added PREEXEC support for unknown traps. Results are stored in the variable **$pu*n***. See the **unknown_trap_preexec** setting in **snmptt.ini**. * Added **unknown_trap_nodes_match_mode** setting to allow you to change how traps are handled when they do not match due to **MATCH** and **NODES**. If set to 1, traps are considered skipped instead of unknown. Statistics now include the number of skipped traps when enabled. * Added support for wildcards for the **snmptt_conf_files** setting in **snmptt.ini**. Example: **/etc/snmptt/snmptt.*.conf** * Added **log_format** **snmptt.ini** setting to allow you to define the STDOUT, text log and eventlog text format. * Added **syslog_format** **snmptt.ini** setting to allow you to define the syslog text format. This will allow you to add a structured data section for RFC5424 syslog. * Added variable substitution **$j** to pull out the enterprise number from the full enterprise OID. For example, for enterprise OID .1.3.6.1.4.1.232, **$j** would contain 232. * Added remote syslog support using the Perl module Log::Syslog::Fast which also allows you to specify the APP-NAME for RFC5424 syslog. Added the following **snmptt.ini** settings: **syslog_module**, **syslog_remote_dest**, **syslog_remote_port**, **syslog_remote_proto**, **syslog_rfc_format**, **syslog_app** and **syslog_system_app**. * Added reload support to the **snmptt.service** systemd file. This will allow you to use the **'systemctl reload snmptt'** command to reload the configuration. * Added support for IPv6. To enable, set **ipv6_enable = 1** in **snmptt.ini**. * Added support for sub-second sleep for spool folder processing. * **snmptt.ini** can now be located in **/etc/snmptt** and is searched for at this location first. * Updated documentation on securing SNMPTT to ensure the snmptt user has read access to the configuration files. This is required when issuing a reload. * Fixed a bug with **daemon_uid** that prevented SNMPTT from starting on FreeBSD (bug 47). * Fixed a bug where traps arriving with the hostname set to UNKNOWN were not being handled properly (bug 46). * Fixed a bug with **MATCH** which was preventing it from matching integers properly (bug 41). * Fixed a bug where the agent IP address was not handled correctly when it was received from Net-SNMP as **IpAddress:x.x.x.x** (bug 27). * Fixed a race condition bug with **snmptthander** and **snmptthandler-embedded** which could cause traps to be missed. Spool files are now immediately locked after creation using flock(). If flock() is not supported, the spool file will be created with a temporary filename and then renamed after closing. * Fixed a bug with **wildcard_expansion_separator** which caused an issue when using wildcard separators that were longer than one character (bug 38). * Fixed a bug where quotes were not properly removed from some incoming traps. * Fixed bug with debug mode that was causing some debug mode output even when debug mode was off. * Fixed a bug where DNS resolution was not working for enterprise variables when **net_snmp_perl_enable** was disabled. * Fixed a bug that prevented snmptt from starting when debug mode was disabled (bug 48). * Fixed debug output bug with snmptthandler-embedded (PR 1). * Fixed a bug with IPv6 address handling for NODES in snmptt.conf. * Fixed a bug that prevented the hostname from being extraced when IPv6 is disabled and the hostname is passed from Net-SNMP as UDP: [x.x.x.x]:xxxx->[x.x.x.x]:xxxx. * Changed **net_snmp_perl_best_guess** default from 0 to 2 as any modern system should support this. See FAQ and **snmptt.ini** for details on this variable. * Enabled Perl warnings to help ensure code is following best practices. * Ran code against Perl::Critic to find non-optimal code. Made various adjustments such as relacing bare words with variables and changing open() calls from two arguments to three. * Documentation was converted from html to markdown to make it easier to maintain and a full review was completed. Many improvments have been made including a new section on integrating with Icinga. The docs folder now contains **.md**, **.html** and **.epub** versions of the documentation. * **snmptthandler-embedded**: * Varbind types **Gauge32** and **Hex-STRING** now have the Gauge32: and Hex-STRING: text removed for incoming traps. Unicode line endings are also removed (Perl 5.10 and higher). * **snmpttconvertmib**: * Added **--exec_file** option to allow you to provide an EXEC command inside of a file instead of specifying on the command line. Useful for commands that include quotes so that you don't have to worry about escaping on the command line. Also allows you to define multiple EXEC lines instead of just one. * Added **--exec_mode** option to allow you change how the EXEC line is built. Setting to **0** will append the format line to the end of the line (default). Setting to **1** does not append the format line to the end of the line. This is useful if you have added **$Fz** to the **--exec** line so that SNMPTT can replace it with the FORMAT line. Setting to **2** is similar to **1**, but instead of SNMPTT having to replace **$Fz** with the FORMAT line, **snmpttconvertmib** will do the substitution. * Added **--preexec** and **-preexec_file** options. ## **v1.4.2** **\- July 23rd, 2020** * Fixed a security issue with EXEC / PREXEC / unknown\_trap\_exec that could allow malicious shell code to be executed. * Fixed a bug with EXEC / PREXEC / unknown\_trap\_exec that caused commands to be run as root instead of the user defined in daemon\_uid. ## **v1.4** **\- November 6th, 2013** * Added **snmptt.ini** option net\_snmp\_perl\_cache\_enable to enable caching of Net-SNMP Perl module OID and ENUM translations. This may speed up translations and reduce CPU load when net\_snmp\_perl\_enable and translate\_\* options are enabled. * Fixed bug with snmptthandler-embedded where IP addresses and OIDs were not being detected properly because they contained 'OID:', 'IpAddress:' etc. * Fixed bug with MATCH. The PREEXEC $p variable could not be used with MATCH. PREEXEC is now executed first if MATCH contains $p. * Fixed bug with syslog. Log entries were supposed to be logged with snmptt\[pid\] but instad of the pid it was actually the effective user ID (2980512). * Fixed bug where the hostname is not detected properly when snmptrapd is configured to not use DNS. * Fixed bug where if the spool directory is not defined, files may be deleted from the wrong folder (3020696). * Fixed bug with syslog logging. Function was not being called properly (3166749). * Fixed bug with MATCH where number ranges were not working (3397982). * Fixed bug with multi-line traps (2915658). * Fixed bug with LOGONLY severity. EXEC was being executed even if the trap had a severity of LOGONLY (3567744). * Fixed bug with snmptt hanging if the log message sent to syslog contained a % symbol. All %'s are now escaped before sending to syslog (3567748). * Fixed possible bug with MySQL. Put CONNECT string on one line. * Fixed bug with not being able to write to the debug log file when running snmptt as non-root if the debug file didn't already exist with the correct permissions at startup. The ownership of snmptt.debug is now set to daemon\_uid before switching to the new uid. Patch 3423525. * Installation documentation updates (bug 3425999). ## **v1.3** **\- November 15th, 2009** * Added snmptthandler-embedded - a Net-SNMP snmptrapd embedded Perl version of snmptthandler. * Added variable substitutions $Be, $Bu, $BE and $Bn for SNMPv3 securityEngineID, securityName, contextEngineID and contextName (requires snmptthandler-embedded handler). * Added **snmptt.ini** option **duplicate\_trap\_window variable** for duplicate trap detection. * Added LSB init keywords and actions to snmptt-init.d and changed the priority for start / stop so that it starts after snmptrapd and stops before snmptrapd. * Changed the default log path to /var/log/snmptt for Unix and c:\\snmpt\\log for Windows to make it easier to grant write permission to the snmptt process. * Changed umask for log files to 002 to ensure they are not created as world writable. * Fixed a bug where the the PID file was being created using the parent (root) PID instead of the child (daemon\_uid) when daemon\_uid is used. * The DEBUG log file will now be re-opened when a HUP signal is sent. * When debugging is enabled, flush buffers every sleep cycle so we can tail the debug log file. * Don't print messages to the console when starting in daemon mode unless debugging is enabled or an error occurs. * 'Could not open debug output file!' is no longer reported when debugging is disabled. * Added snmptt.logrotate file from Ville Skytta. * Fixed a bug (1748512) with handling escaped quotes in a trap message. * Updated snmptt-net-snmp-test to test MIB descriptions. * SNMPTTConvertMIB: * Fixed a bug (1678270) where a TRAP-TYPE / NOTIFICATION-TYPE line would not translate if it was split across two lines. ## **v1.2** **\- June 16th, 2007** * When **daemon\_uid** is used, two processes will now be spawned. The first process will be run as the same user that started SNMPTT (which should be root). The second will run as the **daemon\_uid** user. This was changed so that SNMPTT could properly clean up the pid file on exit. * Added snmptt.ini option **pid\_file** to allow for custom pid file locations when running in daemon mode. * Fixed bug where pid file did not contain the current pid of snmptt. * Added **snmptt.ini** options **date\_format**, **time\_format**, **date\_time\_format**, **date\_time\_format\_sql** and **stat\_time\_format\_sql** to allow the output format for **$x** and **$X** substitution variables, and the format of the date/time for text logs and SQL to be changed using **strftime()** variables. This allows for proper date/time data types to be used in SQL databases. * Added logging of trap statistics to a SQL table. Added **\*table\_statistics** **snmptt.ini** variable to define the table to be used. * Added ability to add custom columns to **\*\_table** and **\*\_table\_unknown** tables. Added **sql\_custom\_columns** and **sql\_custom\_columns\_unknown** **snmptt.ini** options. * Added **snmptt.ini** option **unknown\_trap\_exec\_format** to allow custom output with substitutions. * Added the ability to log system messages to a text file in addtion to the existing syslog and Event Log. Added **snmptt.ini** options **log\_system** and log\_system\_file. * Added a work-around to the [Net-SNMP v5.4 traphandle bug (1638225)](http://sourceforge.net/tracker/index.php?func=detail&aid=1638225&group_id=12694&atid=112694) where the host name was set to . When detected, SNMPTT will use the host IP address instead. * Added a **$H** variable substitution to give the host name of the computer that is running SNMPTT, or a user defined value specified in the new **snmptt\_system\_name** **snmptt.ini** option. * Added MATCH support for bitwise AND * Added **snmptt.ini** option **exec\_escape** to escape wildards (\* and ?) in EXEC, PREEXEC and the unknown\_trap\_exec commands. This is enabled by default for Linux and Unix (or anything non-Windows) to prevent the wildcards from being expanded by the shell. * Moved **unknown\_trap\_exec** to Exec section in **snmptt.ini**. * Added 'use strict' pragma in source code. * Experimental: Added threads (Perl ithreads) support for EXEC. When enabled, EXEC commands will launch in a thread to allow SNMPTT to continue processing other traps. Added **snmptt.ini** options **threads\_enable** and **threads\_max**. * Fixed bug where snmptt tried to log to syslog when changing UIDs even if syslog\_system\_enable was set to 0. * Fixed a bug in REGEX with handling of captures. Text::Balanced module is now required. * Fixed a bug under Windows where SNMPTT was trying to log to syslog instead of the event log. * Fixed a bug where SNMPTT was attempting to log to syslog / eventlog when using the --time option. * Fixed a bug in MATCH where the i modifier was not handled correctly. * Added information to Nagios section of documentation for using SNMP traps as heartbeats by using freshness checks. * Added information to Nagios section of documentation for using freshness checks to automatically clear trap alerts. * SNMPTTConvertMIB: * Fixed a bug (1438394) where ARGUMENTS lines that have $1, $2 etc instead of %0, %1 would not translate. * Fixed a bug where a --#SEVERITYMAP line would be used instead of --#SEVERITY. ## **v1.1** **\- January 17th, 2006** * Added **PREEXEC** **snmptt.conf** file option to allow an external program to be run before processing the FORMAT and EXEC lines. The output of the external program is stored in the **$p_n_** variable where **_n_** is a number starting from 1. Multiple **PREEXEC** lines are permitted. The first **PREEXEC** stores the result of the command in **$p1**, the second in **$p2** etc. Any ending newlines are removed. The **snmptt.ini** parameter **pre\_exec\_enable** can be used to enable / disable it. * **MATCH** statement now accepts any variable name instead of only enterprise variables. Example: MATCH $s:(Normal) * Added **NODES MODE=** snmptt.conf file option to allow you to select either **POS** (positive - the default) or **NEG** (negative) for **NODES** matches. If set to **NEG**, then **NODES** is a 'match' only if _none_ of the **NODES** entries match. * Added **unknown\_trap\_exec** **snmptt.ini** option. If defined, the command will be executed for ALL unknown traps. Passed to the command will be all standard and enterprise variables, similar to **unknown\_trap\_log\_file** but without the newlines. * **snmptt --dump** which dumps all the configured EVENTs, now displays duplicate EVENT entries to assist with troubleshooting duplicate entries trap logs. * If the debug log file can not be opened, a message is now logged to syslog if **syslog\_system\_enable** is enabled, and to the Event Log if **eventlog\_system\_enable** is enabled * Fixed bug with PostgreSQL where some trap data was interpreted as 'placeholders' in the INSERT statement which caused logging errors. PostgreSQL now uses PREPARE / EXECUTE statements instead. * MySQL now uses PREPARE / EXECUTE statements instead of a single INSERT statement. * Fixed bug in **NODES** where **NODES** entries from previous EVENTs were not being purged correctly. * Fixed bug where **snmptt --dump** would attempt to log to syslog or the Event Log. * Fixed bug that prevented the wildcard **.\*** from being accepted on the EVENT line. * Added Windows Event Log forwarding documentation to integration section. * SNMPTTConvertMIB: * Fixed a bug when **\--format\_desc=n** was used that caused extra trailing whitespaces to be added for every non existent line in the description. * Fixed bug that prevented some MIBs from being accepted due to spacing in the **DEFINITIONS::= line**. * Fixed bug in that prevented **\--ARGUMENTS {}** from being parsed due to spacing. ## **1.0** **\- August 30, 2004** * SQL database connections are now opened after forking to the background when running in daemon mode, and after changing users when running SNMPTT as a non-root user. This should prevent 'gone away' and other connection problems with SQL databases due to lost handles. * Added **mysql\_ping\_on\_insert**, **postgresql\_ping\_on\_insert** and **dbd\_odbc\_ping\_on\_insert** options to 'ping' the database before doing an INSERT. Also added the options **mysql\_ping\_interval**, **postgresql\_ping\_interval** and **dbd\_odbc\_ping\_interval** to periodically ping the database. These options will help ensure the connection to the database remains available. If an error is returned, it will attempt to reconnect to the database. This should prevent SNMPTT from having to be restarted if the SQL server is not available due to an outage or a connection timeout due to no activity. * Added variable substitution **$Fz** which when used on an EXEC line will dump the translated FORMAT line. This will allow for simplified EXEC lines when they are the same as the FORMAT line (minus the command to execute of course). * Added variable substitutions **$Fa**, **$Ff**, **$Fn**, **$Fr**, **$Ft**, for alarm (bell), form feed (FF), newline (LF, NL), return (CR) and tab (HT, TAB) * Added variable substitution **$D** to dump the description text for FORMAT and EXEC lines. The descriptions can be pulled from either the SNMPTT.CONF or MIB files. This is controlled by the **description\_mode** and **description\_clean** **snmptt.ini** options. * Added support for logging unknown traps to a SQL table * Added logging of statistical information for **total traps received**, **total traps translated**, **total traps ignored** and **total unknown traps**. Statistics are logged at shut down, and optionally at a defined interval defined by the new **snmptt.ini** variable **statistics\_interval**. Logging can also be forced by sending the SIGUSR1 signal, or by creating a file called !statistics in the spool folder. * Added the error number reported by MySQL to MySQL errors (system syslog, eventlog etc) * Added **/usr/local/etc/snmp** and **/usr/local/etc** paths to the list of default directories searched for **snmptt.ini**. * Added some friendly error messages when required Perl modules are not available * Fixed bug with with handling traps in symbolic format (snmptrapd without -On) * Fixed bug with with using printing $ symbols in FORMAT and EXEC lines * Added [Simple Event Correlator (SEC)](http://kodu.neti.ee/%7Eristo/sec/) integration documentation * SNMPTTConvertMIB: * Fixed bug that prevented the variable list (OBJECTS) of V2 MIB files from being converted * Fixed bug that caused an infinite loop processing the VARIABLES/OBJECTS section if the line in the MIB file contained spaces after the closing bracket ## **0.9** **\- November 3rd, 2003** * Syslog messages are now logged with snmptt\[pid\] instead of TRAPD for traps, and snmptt-sys\[pid\] instead of SNMPTT for system messages * Added the option daemon\_uid which causes snmptt to change to a different user (uid) after launching on Unix systems running in daemon mode * Fixed bug that prevented ip addresses from being detected correctly with translate\_value\_oids * Fixed bug with MATCH that caused integer ranges from not being handled correctly * Separated SNMPTT, SNMPTTCONVERT, SNMPTTCONVERTMIB and FAQ / Troubleshooting documentation into separate documents ## **0.8** **\-** **September 16th****, 2003** * Added MATCH keyword support to allow trap matching based on values contained inside the trap enterprise variables * REGEX now supports substitution with captures and the modifiers i, g and e. The e modifier allows for complex REGEX expressions. * Added support for remote MySQL and PostgreSQL databases * Added PostgreSQL support for [DBD:Pg](http://search.cpan.org/search?dist=DBD-Pg) * An EVENT can now contain mulitple EXEC lines * An EVENT can now contain mulitple NODES lines * Added the option dynamic\_nodes to allow NODES files to be either loaded at startup or loaded each time an EVENT is processed * Added trapoid column for database logging to contain the actual trap received. The eventid column will contain the actual matched entry from the .conf file (which could be a wildcard OID) * Fixed bug that prevented some variables from displaying the correct values because the received trap OID was replaced with the actual EVENT entry * Fixed bug that caused OIDs not to be translated correctly with translate\_value\_oids enabled * Agent IP address is now used instead of 'host' IP address for NODES matches, the 'hostname' column in database logs and the $A variable * Variable $A now prints the agent host name. $aA prints the agent IP address. * Variable $E now prints the enterprise in symbolic form. $e prints the numeric OID * Variable $O now prints the trap in symbolic form. $o prints the numeric OID * Added new variable substitution **$i** to print the actual matched entry from the .conf file (which could be a wildcard OID) * Added the configuration option dns\_enable to enable DNS lookups on host and agent IP addresses * If DNS is enabled, NODES entries are resolved to IP addresses and the IP address is used to perform the match. This will allow for aliases to be used. * Added the option resolve\_value\_ip\_addresses to resolve ip addresses contained inside enterprise variable values * Changed snmptt.ini setting translate\_trap\_oid to translate\_log\_trap\_oid * Changed snmptt.ini setting translate\_oids to translate\_value\_oids * Added configuraiton settings: translate\_enterprise\_oid\_format, translate\_trap\_oid\_format, translate\_varname\_oid\_format and db\_translate\_enterprise * $O follows translate\_trap\_oid\_format, and $o is always the numerical trap OID * $E follows translate\_enterprise\_oid\_format, and $e is always the numerical enterprise OID * The enterprise column when logging to a database now follows the db\_translate\_enterprise setting * Fixed bug with $# to report the correct number of enterprise variables (was 1 lower than it should have been) * Fixed bug with handling traps that contain quoted values that span multiple lines * PID file now created (/var/run/snmptt.pid or ./snmptt.pid) when running in daemon mode on Linux / Unix. snmptt-init.d script updated to remove the pid file when shutting down snmptt. * Added [Nagios](http://www.nagios.org) / Netsaint integration documentation * Added contrib folder * SNMPTTConvertMIB * Now prints the variables contained in each trap definition unless \--no\_variables is set. When using \--net\_snmp\_perl it will also resolve the Syntax (INTEGER, OCTETSTR etc) and Description. If it's an INTEGER, will also print out the enumeration tags if any exist. * Improved compatability with MIB files ## **0.7** **\- April 17th****, 2003** * Fixes a vulnerability that prevents the possibility of injected commands contained in traps from being executed when using the EXEC feature * Added the ability for traps passed from snmptrapd or loaded from the snmptt.conf files to contain symbolic OIDs such as linkDown and IF-MIB::linkUp. This feature requires the UCD-SNMP / Net-SNMP Perl module * Added the configuration options **translate\_trap\_oid** and translate\_oids to have the trap OID and OID values contained in the trap variables converted from numerical OID to symbolic form before logging. This feature requires the UCD-SNMP / Net-SNMP Perl module * Added support for logging of traps using PostgreSQL via DBI / DBD::PgPP * Added REGEX keyword support to allow user definable search and replace on FORMAT / EXEC lines * NODES entry can now contain a CIDR address (eg: 192.168.10.0/23), or a network range (192.168.10.0-192.168.11.255) * NODES entry can now contain a mix of host names, IP addresses, CIDR addresses, network ranges and filenames * Added the ability to force a reload of the configuration files while running in daemon mode by placing a file called !reload in the spool directory * Added snmptt-net-snmp-test program to perform various translations of numeric and symbolic OIDS to assist with determining if the installed Perl module will function as expected with SNMPTT * Fixed bug that prevented quoted text from being logged correctly to SQL databases * Fixed bug that would prevent the translation of integer values to enumeration tags and variable name substitutions when using Net-SNMP 5.0.x * SNMPTTConvertMIB * FORMAT / EXEC line can now contain any of the following: * \--#SUMMARY or DESCRIPTION (use DESCRIPTION only if --#SUMMARY does not exist) * description or --#SUMMARY (use --#SUMMARY only if DESCRIPTION does not exist) * \--#SUMMARY and DESCRIPTION * DESCRIPTION and --#SUMMARY * When using the DESCRIPTION to build the FORMAT / EXEC line, can now choose between using the first line of the DESCRIPTION field, or the first x number of sentences * The use of the --#SUMMARY and DESCRIPTION line for the FORMAT / EXEC line can be disabled * Support for multiple --#SUMMARY lines * Support for --#SEVERITY lines * The default of using the $\* wildcard can be disabled * Conversion of the DESCRIPTION section to SDESC / EDESC can be disabled * EXEC line can be specified on the command line * NODES line can be specified on the command line ## **0.6 - March** **25th, 2003** * Logging: * * Added support for logging of traps using DBD::ODBC * Fixed bug with Win32::ODBC connection not being closed on exit of SNMPTT * MySQL code cleanup * Added support for logging traps to the NT Event Log including the ability to select the Event Log level based on the severity level defined in the snmptt.conf file * Improved syslog support by adding the ability to select the syslog level based on the severity level defined in the snmptt.conf file * Added syslog and NT Event Log support for SNMPTT 'system' events such as startup, shutdown, errors handling spool directory / files, database connectivity errors etc * Added the option **keep\_unlogged\_traps** to have SNMPTT erase the spooled trap file only after it successfully logs to at least one or all log systems. This will help prevent traps from being lost due to logging problems. * Added ability to translate integer values to enumeration tags defined in MIB files. This feature requires the UCD-SNMP / Net-SNMP Perl module * Added new variable substitutions: **$v_n_** (variable name), **$+_n_**(variable name:value), **$-_n_** (variable name (type):value), **$+\*** (same as $+_n_ but wildcard), and **$-\*** (same as $-_n_ but wildcard). Translation of the variable name using the MIB requires the UCD-SNMP / Net-SNMP Perl module. * If a variable is passed from snmptrapd that is blank, snmptt will replace it with (null) * Fixed bug that would prevent variables numbered 10 or higher from being translated correctly * Fixed bug with handling trap data that contains spaces but is not inside of quotes * Code cleanup to remove Perl warnings (-w) * Added separate debug file for snmptthandler * Cleaned up defaults code for snmptthandler * Added examples folder containg a sample snmptt.conf file and sample trap file * Added FAQ section to this document * Snmpttconvertmib * Code cleanup * Now uses new command line arguments (snmpttconvertmib -h for help). * Can now use either snmptranslate or the SNMP Perl module (Net-SNMP) to process MIB files * Can now add a NODES line when converting MIB files * Now checks the version of snmptranslate before converting the mib to ensure snmptranslate is called correctly * Fixed bug which would cause the last notification of a v2 MIB file not to be converted correctly ## **0.5 - February 12th, 2003** * Spool file list sorted before processing to ensure traps are processed in the order they are received when in daemon mode * Added **use\_trap\_time** variable to config file for daemon mode to have SNMPTT use either the time from the spool file, or the current time. Changed SNMPTTHANDLER to output the current date and time on the first line of the spool file * Fixed bug with default variable settings being ignored. Defaults were not being set correctly if variable was not defined in the .ini file. * Fixed bug with SNMPTT ignoring the daemon mode parameter in the .ini file * Fixed bug with Nodes list not being flushed out for each processed trap when running in daemon mode * Added **strip\_domain** and **strip\_domain\_list** configuration options to enable / disable the removal of the domain name from the host name passed to SNMPTT. Defaults to disabled (do not strip domain name) * SNMPTTCONVERTMIB now prepends the contents of the --#TYPE line (if present) to the --#SUMMARY line to provide a more descriptive FORMAT / EXEC line ## **0.4 - November 18th, 2002** * Variable substitution changes: * * $X = Date, $x = Time instead of $x being both date and time * $N = Event name instead of $S * Added $c, $@, $O, $o, $ar, $R, $aR, $G, $S * Configuration moved to a .ini file * MySQL support via DBI under Linux and Windows * ODBC support via Win32::ODBC under Windows ## **0.3 - September 11th, 2002** * Daemon mode support for SNMPTT. When running as a daemon, SNMPTTHANDLER script is used to spool traps from SNMPTRAPD. * SNMPTTCONVERTMIB utility to convert trap / notify definitions from MIB files * Sample trap file (sample-trap) for testing * Command line options to SNMPTT * SNMPTT now strips UDP: and :_port_ from IP addresses when using Net-SNMP 5.0+ * NODES files can now contain comments * NODES files can now contain either host names or IP addresses * You can now have multiple definitions of the same trap so that different hosts / nodes can have different actions or one host have multiple actions * Configuration file can now contain a list of other configuration files to load ## **0.2 - July 10th, 2002** * Improved debugging output * Severity substitution bug fix * SNMP V2 trap bug fix * UCD-SNMP v4.2.3 problem workaround ## **0.1 - April 18th, 2002** * Initial release # Upgrading ## **v1.4.2 to v1.5beta1** To upgrade from v1.4.2 to v1.5 you should: 1. Replace **snmptt** with the new version. Make sure the file is executable (**chmod +x _filename_**). 1. Replace **snmptthandler-embedded** with the new version. Make sure the file is executable (**chmod +x _filename_**). 1. Replace **snmpttconvertmib** with the new version. Make sure the file is executable (**chmod +x _filename_**). 1. For systemd systems, replace the **snmptt.service** service file with the new version. 1. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it. 1. Secure your **/etc/snmp** or **/etc/snmptt** folder as described in the **Securing SNMPTT** section of the documentation. 1. To enable IPv6 support, set **ipv6_enable = 1** in **snmptt.ini**. Notes: 1. Starting with v1.5, you can use **/etc/snmptt/** instead of **/etc/snmp/** for your **snmptt.ini** file. 2. DNS now requires the Perl module **IO::Socket::IP**. 3. IPv6 requires the Perl module **Net::IP**. ## **v1.4 to v1.4.2** To upgrade from v1.4 to v1.4,1, you should: 1. Replace **snmptt** with the new version. Make sure the file is executable (**chmod +x _filename_**). 2. Confirm that the **snmptt** user account is a member of the appropriate groups such as **nagios** for Nagios and **icingacmd** for Icinga2. 3. Backup your **snmptt.ini** file and then add the new option **daemon\_gid = snmptt** below **daemon\_uid**. 4. To increase security: * If you are not currently using daemon mode and are running as root, please switch to daemon mode or run as different user such as **snmptt**. * Secure the spool folder with: * **chown -R snmptt.snmptt /var/spool/snmptt** * **chmod -R 750 /var/spool/snmptt** * Secure the /etc/snmp folder with * **chown -R root.root /etc/snmp** * **chmod 755 /etc/snmp** * **chown snmptt.snmptt /etc/snmp/snmptt\*** * **chmod 660 /etc/snmp/snmptt\*** ## **v1.3 to v1.4** To upgrade from v1.3 to v1.4, you should: 1. Replace **snmptt** and **snmptthandler-embedded** with the new versions. Make sure the files are executable (**chmod +x _filename_**). 2. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it. The only change is the addition of the net\_snmp\_perl\_cache\_enable option. 3. Check your snmptt.conf files for any traps defined with LOGONLY. These entries will no longer have EXEC lines executed. In previous versions EXEC was exectued when it should not have been. 4. The new **snmptt.ini** option net\_snmp\_perl\_cache\_enable defaults to on, so disable if required. ## **v1.2 to v1.3** To upgrade from v1.2 to v1.3, you should: 1. Replace **snmptt** and **snmpttconvertmib** with the new versions. Make sure the files are executable (**chmod +x _filename_**). 2. Copy snmptt-init.d to /etc/init.d/snmptt. Make sure the file is executable (**chmod +x _filename_**). 3. Optional: Install and configure the snmptthandler-embedded trap handler. See [Embedded handler](snmptt.html#Installation-Unix-Embedded) for details. 4. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it. The default log paths have changed so modify as needed. 5. Setup log rotation by copying snmptt.logrotate to /etc/logrotate.d/snmptt and modifying as needed for the correct paths, rotate frequency etc. 6. Enable any new features in **snmptt.ini** as required. ## **v1.1 to v1.2** To upgrade from v1.1 to v1.2, you should: 1. Replace **snmptt** and **snmpttconvertmib** with the new versions contained in the v1.2 package. Make sure the files are executable (**chmod +x _filename_**). 2. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it. 3. Enable any new features in **snmptt.ini** as required. 4. For Linux and Unix (or anything non-Windows), if you are using the **daemon\_uid** option in **snmptt.ini**, and are monitoring the availability of snmptt by checking for the snmptt process, be aware that there will now be two snmptt processes running instead of one. 5. For Linux and Unix (or anything non-Windows), the **snmptt.ini** **exec\_escape** option is enabled by default which will escape wildcard characters (\* and ?) for EXEC, PREEXEC and the unknown\_trap\_exec commands. Disable if required. ## **v1.0 to v1.1** To upgrade from v1.0 to v1.1, you should: 1. Replace **snmptt** and **snmpttconvertmib** with the new versions contained in the v1.1 package. Make sure the files are executable (**chmod +x _filename_**). 2. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it. 3. Enable any new features in **snmptt.ini** as required. ## **v0.9 to v1.0** To upgrade from v0.9 to v1.0, you should: 1. Replace **snmptt**, **snmpttconvert**, **snmpttconvertmib**, and **snmptthandler** with the new versions contained in the v1.0 package. Make sure the files are executable (**chmod +x _filename_**). 2. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it 3. If you are using a MySQL, PostgreSQL or ODBC (via DBD::ODBC) and do not want the database to be pinged before each INSERT, set **mysql\_ping\_on\_insert**, **postgresql\_ping\_on\_insert** or **dbd\_odbc\_ping\_on\_insert** to 0 in **snmptt.ini**. If you do not want the database to be pinged periodically, set **mysql\_ping\_interval**, **postgresql\_ping\_interval** or **dbd\_odbc\_ping\_interval** to 0 in **snmptt.ini**. 4. Enable any new features in snmptt.ini as required 5. Test and report any issues to alex\_b@users.sourceforge.net, or open a bug report at [Sourceforge](http://sourceforge.net/tracker/?group_id=51473&atid=463393). ## **v0.8 to v0.9** To upgrade from v0.8 to v0.9, you should: 1. Replace snmptt with the new version contained in the v0.9 package. Make sure the file is executable (chmod +x filename) 2. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it 3. If you have any external applications that monitor the syslog for SNMPTT or TRAPD messages, modify them to look for snmptt\[pid\] and snmptt-sys\[pid\] instead 4. Enable any new features in snmptt.ini as required 5. Test and report any issues to alex\_b@users.sourceforge.net, or open a bug report at [Sourceforge](http://sourceforge.net/tracker/?group_id=51473&atid=463393). ## **v0.7 to v0.8** To upgrade from v0.7 to v0.8, you should: 1. Replace snmptt and snmpttconvertmib with the new versions contained in the v0.8 package. Make sure the files are executable (chmod +x filename) 2. Replace your /etc/rc.d/init.d/snmptt file (cp snmptt-init.d /etc/rc.d/init.d/snmptt). Make sure the file is executable (chmod +x filename) 3. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it 4. In your snmptt.ini file, configure translate\_log\_trap\_oid with translate\_trap\_oid value from old snmptt.ini 5. In your snmptt.ini file, configure translate\_value\_oids with translate\_oids value from old snmptt.ini 6. In your snmptt.ini file, set dynamic\_nodes to 1 if you want the NODES files to be loaded each time an event is processed which is how previous versions of snmptt worked 7. In your snmptt.conf files, replace any $A with $aA unless you want agent host names to be used instead of IP addresses 8. In your snmptt.conf files, replace any $E with $e unless you want Enterprise trap OID in symbolic format 9. In your snmptt.conf files, replace any $O with $o unless you want Trap OID in symbolic format 10. In your snmptt.conf files, append a g to the end of all REGEX lines to enable global search and replace 11. Review other translate settings in snmptt.ini 12. Enable any new features in snmptt.ini as required 13. If you are using database logging, add a new column called trapoid 14. If you are using database logging and you enable conversions of OIDs to long names, make sure the table columns are wide enough to hold them 15. Test and report any issues to alex\_b@users.sourceforge.net, or open a bug report at [Sourceforge](http://sourceforge.net/tracker/?group_id=51473&atid=463393). ## **v0.6 to v0.7** To upgrade from v0.6 to v0.7, you should: 1. Replace **SNMPTT** and **SNMPTTCONVERTMIB** with the new versions contained in the v0.7 package 2. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it 3. Enable any new features in snmptt.ini as required 4. Test and report any issues to alex\_b@users.sourceforge.net, or open a bug report at [Sourceforge](http://sourceforge.net/tracker/?group_id=51473&atid=463393). ## **v0.5 to v0.6** To upgrade from v0.5 to v0.6, you should: 1. Replace **SNMPTTHANDLER, SNMPTT** and **SNMPTTCONVERTMIB** with the new versions contained in the v0.6 package 2. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it 3. Enable any new features in snmptt.ini as required 4. Test and report any issues to alex\_b@users.sourceforge.net, or open a bug report at [Sourceforge](http://sourceforge.net/tracker/?group_id=51473&atid=463393). ## **v0.4 to v0.5** To upgrade from v0.1, v0.2 to v0.3 to v0.4, you should: 1. Set **use\_trap\_time** to **0** to have SNMPTT operate the same as v0.4, or leave as 1 (recommended default) and test 2. Replace **both** **SNMPTTHANDLER** and **SNMPTT** with the new versions contained in the v0.5 package 3. Backup your **snmptt.ini** file, replace it with the new version, and make any necessary configuration changes to it ## **v0.1, v0.2 or v0.3 to v0.4** To upgrade from v0.1, v0.2 to v0.3 to v0.4, you should: 1. In your snmptt.conf file, replace all **$x** with **$x $X** (see What's New section) 2. In your snmptt.conf file, replace all **$S** with **$N (**see What's New section) 3. Configure the snmptt.ini as described in this file - configuration options are no longer stored in the snmptt and snmptthandler scripts 4. If your snmptt.conf file contained a list of other snmptt.conf files instead of trap definitions, move that list to the snmptt.ini file # Installation ## Overview SNMPTT can be run in either daemon mode or standalone mode. Daemon mode is recommended. See [Modes of Operation](#Modes-of-Operation "Modes of Operation") for more details. The following outlines the general steps required to install and configure SNMPTT: 1. Install Net-SNMP and SNMPTT as described below. 2. Copy the sample snmptt.conf to your configuration folder. 3. [Modify snmptt.ini](#Configuration-Options) to include the **snmptt.conf** file and set any desired options. 4. Start SNMPTT. 5. Test using the sample trap file. 6. Create an **snmptt.conf** file [by hand](#SNMPTT.CONF-Configuration-file-format), or using [snmpttconvertmib](#SNMP-Trap-Translator-Convert-MIB). 7. Configure your network devices to send traps to the Net-SNMP / SNMPTT machine. 8. Initiate a trap on your network device and check the SNMPTT log files for the result. 9. Secure the SNMPTT installation. ## Installation - Unix ### Package Manager Packages are available for most Linux distributions and FreeBSD. Check your package manager to see if they have the latest version. If not, you can manually install using the steps below. ### Manual installation Steps: 1. Install Net-SNMP and the Perl modules as listed int the [Requirements](#Requirements) section. 1. Copy **snmptt** to /usr/sbin/ and ensure it is executable (**chmod +x snmptt**): cp snmptt /usr/sbin/ chmod +x /usr/sbin/snmptt 1. Create snmptt user: 1. RedHat based systems: adduser -r snmptt 2. Debian based systems: adduser --system --group snmptt 1. Create /etc/snmptt and set permissions. Note: Starting with v1.5, you can use /etc/snmptt/ instead of /etc/snmp/ for your snmptt.ini file.m mkdir /etc/snmptt chown -R snmptt.snmptt /etc/snmptt chmod 750 /etc/snmptt 3. Copy **snmptt.ini** to **/etc/snmptt** and edit the options inside the file: cp snmptt.ini /etc/snmptt/ and vi /etc/snmptt/snmptt.ini 4. Either copy **examples/snmptt.conf.generic** to **/etc/snmptt/snmptt.conf** (renaming the file during the copy) or use the touch command to create the file (**touch /etc/snmptt/snmptt.conf**). cp examples/snmptt.conf.generic /etc/snmptt/snmptt.conf or touch /etc/snmptt/snmptt.conf 5. Create the log folder **/var/log/snmptt/**: mkdir /var/log/snmptt chown -R snmptt.snmptt /var/log/snmptt chmod -R 750 /var/log/snmptt 2. Create the spool folder **/var/spool/snmptt/**: mkdir /var/spool/snmptt/ chown -R snmptt.snmptt /var/spool/snmptt chmod -R 750 /var/spool/snmptt 3. Startup scripts are included for SystemD (uses **systemctl** to control services) and SysVinit systems. Select one depending on your distribution. 2. Systemd: 1. Copy the script and remove executable flag if set. cp snmptt.service /usr/lib/systemd/system/snmptt.service chmod -x /usr/lib/systemd/system/snmptt.service 2. Enable the service: systemctl enable snmptt.service 3. Start the service: systemctl start snmptt.service 1. SysVinit: 1. Copy the script: cp snmptt-init.d /etc/rc.d/init.d/snmptt 4. Add the service using chkconfig: chkconfig --add snmptt 5. Configure the service to start at runlevel 2345: chkconfig --level 2345 snmptt on 6. Start the service: service snmptt start 3. Manually starting without SystemD or SysVinit: snmptt --daemon 8. Check syslog to ensure SNMPTT started properly: grep snmptt /var/log/messages grep snmptt /var/log/syslog Example log messages: snmptt-sys[31442]: SNMPTT 1.5.2 started snmptt-sys[31442]: Loading /etc/snmptt/snmptt.conf snmptt-sys[31442]: Finished loading 64 lines from /etc/snmptt/snmptt.conf snmptt: PID file: /var/run/snmptt.pid snmptt-sys[31446]: Configured daemon_uid: snmptt snmptt-sys[31446]: Changing to UID: snmptt (1002), GID: snmptt (1002) Note: If SNMPTT doesn't start, try running it manually from the shell prompt to see if there are any missing Perl modules. 8. A log rotation script is included which can be used to rotate the log files on Red Hat and other systems. Copy the file to the logrotate.d directory (renaming the file during the copy): cp snmptt.logrotate /etc/logrotate.d/snmptt Edit the **/etc/logrotate.d/snmptt** and update the paths and rotate frequency as needed. vi /etc/logrotate.d/snmptt ### Net-SNMP handlers Next we will install the Net-SNMP handler. There are two options: The standard handler and the embedded handler. The embedded handler is recommended. #### Net-SNMP Standard handler The standard handler is a small Perl program that is called by snmptrapd each time a trap is received when using daemon mode. The limitations of this handler are: * Each time a trap is received, a process must be created to run the snmptthandler program and snmptt.ini is read each time * SNMPv3 EngineID and names are not passed by snmptrapd to snmptthandler The benefits of using this handler are: * Does not require Net-SNMP embedded Perl for snmptrapd * Has been around since v0.1 of SNMPTT * Sufficient for most installations Steps: 2. Copy **snmptthandler** to /usr/sbin/ and ensure it is executable (**chmod +x snmptthandler**). cp snmptthandler /usr/sbin/ chmod +x /usr/sbin/snmptthandler 4. Manually start **snmptthandler** to make sure there are no missing Perl modules: /usr/sbin/snmptthandler Missing Perl module example: Can't locate Time/HiRes.pm in @INC (you may need to install the Time::HiRes module) (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at /usr/sbin/snmptthandler-embedded line 42. BEGIN failed--compilation aborted at /usr/sbin/snmptthandler-embedded line 42. No missing Perl modules example. Errors below can be ignored: SNMPTTHANDLER started: Sun Feb 27 10:39:39 2022 s = 1645976379, usec = 360145 s_pad = 1645976379, usec_pad = 360145 Data received: Press control-c to exit the handler. 7. For **SNMPTT daemon mode**: 1. Modify (or create) the Net-SNMP **snmptrapd.conf** file by adding the following lines: disableAuthorization yes traphandle default /usr/sbin/snmptthandler Note: You can locate the **snmptrapd.conf** file by running: snmpconf -i 6. For **SNMPTT standlone mode**: 1. Modify (or create) the Net-SNMP snmptrapd.conf file by adding the following lines: disableAuthorization yes traphandle default /usr/sbin/snmptt Note: You can locate the **snmptrapd.conf** file by running: snmpconf -i Note: It is possible to configure snmptrapd to execute snmptt based on the specific trap received, but using the default option is preferred 10. Permanently change snmptrapd to use the **-On** option by modifying the startup script: 1. Systemd: Edit the unit file and add the **-On** option: systemctl edit --full snmptrapd.service Change: Environment=OPTIONS="-Lsd" to: Environment="OPTIONS=-Lsd -On" Note: Move the first quote to before OPTIONS. 2. SysVinit: Edit the **/etc/rc.d/init.d/snmptrapd** file and add **"-On"** to **OPTIONS**: vi /etc/rc.d/init.d/snmptrapd Change: OPTIONS="-Lsd" to: OPTIONS="-Lsd -On" Note: **The -On option is recommended**. This will make snmptrapd pass OIDs in numeric form and prevent SNMPTT from having to translate the symbolic name to numerical form. If the **Net-SNMP Perl module** is not installed, then you MUST use the **-On** switch. Depending on the version of Net-SNMP, some symbolic names may not translate correctly. See the FAQ for more info. As an alternative, you can edit the Net-SNMP configuration file **/etc/snmp/snmp.conf** to include the line: **printNumericOids 1. ** This setting will take effect no matter what is used on the command line. 10. Start / restart snmptrapd using systemctl or service: systemctl restart snmptrapd service snmptrapd restart 8. Check syslog to ensure SNMPTT started properly: grep snmptrapd /var/log/messages grep snmptrapd /var/log/syslog 10. Follow the steps in the section [Securing SNMPTT](#SecuringSNMPTT) to ensure SNMPTT has been configured securely. #### Net-SNMP Embedded handler The embedded handler is a small Perl program that is loaded directly into snmptrapd when snmptrapd is started. The limitations of this handler are: * Requires embedded Perl for snmptrapd * Only works with daemon mode The benefits of using this handler are: * The handler is loaded and initialized when snmptrapd is started, so there is less overhead as a new process does not need to be created and initialization is done only once (loading of snmptt.ini). * SNMPv3 EngineID and names variables are available in SNMPTT (B\* variables) Steps: 1. Make sure **snmptrapd** has embedded Perl support enabled. To see if it's enabled in your installation, type: snmptrapd -H 2>&1 | grep perl If it returns **perl PERLCODE** then embedded Perl is enabled. If it's not enabled, you will need to find another Net-SNMP package with it enabled, or compile Net-SNMP using the **--enable-embedded-perl** configure option. 3. Copy **snmptthandler-embedded** to /usr/sbin/. It does not need to be executable as it is called directly by snmptrapd. cp snmptthandler-embedded /usr/sbin/ chmod +x /usr/sbin/snmptthandler-embedded 4. Manually start **snmptthandler-embedded** to make sure there are no missing Perl modules: /usr/sbin/snmptthandler-embedded Missing Perl module example: Can't locate Time/HiRes.pm in @INC (you may need to install the Time::HiRes module) (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at /usr/sbin/snmptthandler-embedded line 42. BEGIN failed--compilation aborted at /usr/sbin/snmptthandler-embedded line 42. No missing Perl modules example. Errors below can be ignored: Bareword "NETSNMPTRAPD_HANDLER_OK" not allowed while "strict subs" in use at /usr/sbin/snmptthandler-embedded line 264. Execution of /usr/sbin/snmptthandler-embedded aborted due to compilation errors. 7. Configure snmptrapd and install the service: Modify (or create) the Net-SNMP snmptrapd.conf file by adding the lines below. Note: You can locate the **snmptrapd.conf** file by running **snmpconf -i**. disableAuthorization yes perl do "/usr/sbin/snmptthandler-embedded" 10. Permanently change snmptrapd to use the **-On** option by modifying the startup script: 1. Systemd: Edit the unit file and add the **-On** option: systemctl edit --full snmptrapd.service Change: Environment=OPTIONS="-Lsd" to: Environment="OPTIONS=-Lsd -On" Note: Move the first quote to before OPTIONS. 2. SysVinit: Edit the **/etc/rc.d/init.d/snmptrapd** file and add **"-On"** to **OPTIONS**: vi /etc/rc.d/init.d/snmptrapd Change: OPTIONS="-Lsd" to: OPTIONS="-LsdOn" Note: **The -On option is recommended**. This will make snmptrapd pass OIDs in numeric form and prevent SNMPTT from having to translate the symbolic name to numerical form. If the **Net-SNMP Perl module** is not installed, then you MUST use the **-On** switch. Depending on the version of Net-SNMP, some symbolic names may not translate correctly. See the FAQ for more info. As an alternative, you can edit the Net-SNMP configuration file **/etc/snmp/snmp.conf** to include the line: **printNumericOids 1. ** This setting will take effect no matter what is used on the command line. 10. Start / restart snmptrapd using systemctl or service: systemctl restart snmptrapd service snmptrapd restart 8. Check syslog to ensure SNMPTT started properly: grep snmptrapd /var/log/messages grep snmptrapd /var/log/syslog 10. Follow the steps in the section [Securing SNMPTT](#SecuringSNMPTT) to ensure SNMPTT has been configured securely. Note: The default snmptt.ini enables logging to snmptt.log and also syslog for both trap messages and snmptt system messages. Change the following settings if required: **log\_enable**, **syslog\_enable** and **syslog\_system\_enable**. ### Testing 1. Copy a sample trap file to the spool folder: cp examples/'#sample-trap.generic.daemon' /var/spool/snmptt/ 2. Check the snmptt.log file for the trap. Note: The date is from the sample trap file. tail /var/log/snmptt/snmptt.log Mon Aug 16 10:06:35 2004 .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" router01 - Link down on interface 3. Admin state: 2. Operational state: 3 2. Check syslog for the trap from **snmptt**. Note: The date is from the sample trap file. grep 'snmptt\[' /var/log/messages grep 'snmptt\[' /var/log/syslog Feb 27 10:29:52 server1 snmptt[83096]: .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" router01 - Link down on interface 3. Admin state: 2. Operational state: 3 3. Generate a **linkDown** trap using **snmptrap**: snmptrap -v 2c -c public 127.0.0.1 .1.3.6.1.6.3.1.1.5.3 .1.3.6.1.6.3.1.1.5.3 ifIndex i 2 ifAdminStatus i 1 ifOperStatus i 2 2. Check the syslog file for the trap from **snmptrapd**: grep snmptrapd /var/log/messages grep snmptrapd /var/log/syslog Feb 27 11:04:03 bink snmptrapd[84697]: 2022-02-27 11:04:03 localhost [UDP: [127.0.0.1]:40290->[127.0.0.1]:162]:#012.1.3.6.1.6.3.1.1.4.1.0 = OID: .1.3.6.1.6.3.1.1.5.3#011.1.3.6.1.2.1.2.2.1.1 = INTEGER: 2#011.1.3.6.1.2.1.2.2.1.7 = INTEGER: up(1)#011.1.3.6.1.2.1.2.2.1.8 = INTEGER: down(2) Note: If you see the error 'SELinux is preventing /usr/sbin/snmptrapd from write access on the directory snmptt', then SELinux needs to be configured to allow snmptrapd to write to the spool folder. 2. Check the snmptt.log file for the trap: tail /var/log/snmptt/snmptt.log Sun Feb 27 11:04:03 2022 .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" 127.0.0.1 - Link down on interface 2. Admin state: 1. Operational state: 2 2. Check syslog for the trap from **snmptt**. grep 'snmptt\[' /var/log/messages grep 'snmptt\[' /var/log/syslog Feb 27 11:04:07 server1 snmptt[83096]: .1.3.6.1.6.3.1.1.5.3 Normal "Status Events" 127.0.0.1 - Link down on interface 2. Admin state: 1. Operational state: 2 **Troubleshooting:** 1. Enable debug mode by defining both **DEBUGGING = 0** and **DEBUGGING_FILE = /var/log/snmptt/snmptt.debug** in **/etc/snmptt/snmptt.ini** and restart **snmptt**. If the file is not created, check syslog for errors. Either the DEBUGGING_FILE path is incorrect or there is a permissions error creating the debug log file. 2. Make sure permissions have been set for the various folders and files. 3. SELinux may interfere with **snmptrapd** and **snmptt**. Disable or reconfigure at your own discretion. 4. Test running **snmptt** and the handlers as explained above to make sure there are no missing Perl modules. ## Installation - Windows The Net-SNMP trap receiver does not currently support embedded Perl, so only the standard trap handler can be used with Windows. 1. Create the directory c:\\snmp and copy **snmptt** and **snmptthandler** to it. md c:\snmp copy snmptt c:\snmpt\ copy snmptthandler c:\snmp\ 2. Copy **snmptt.ini-nt** to **%SystemRoot%\\snmptt.ini** (c:\\windows\\snmptt.ini) and edit the options inside the file. cp snmptt.ini-nt %SystemRoot%\snmptt.ini 3. Either copy examples\\snmptt.conf.generic to c:\\snmp\\snmptt.conf (renaming the file during the copy) or create the file using notepad. copy examples\snmptt.conf.generic c:\snmp\snmptt.conf notepad c:\snmp\snmptt.conf 4. Create the log folder **c:\\snmp\\log\\**. md c:\snmp\log 5. For **SNMPTT standlone mode**: 1. Modify (or create) the Net-SNMP snmptrapd.conf file by adding the following line: traphandle default perl c:\snmp\snmptt Note: It is possible to configure snmptrapd to execute snmptt based on the specific trap received, but using the default option is preferred 6. For **SNMPTT daemon mode**: 1. Modify (or create) the Net-SNMP snmptrapd.conf file by adding the following line: traphandle default perl c:\snmp\snmptthandler 7. Create the spool folder **c:\\snmptt\\spool\\**. md c:\snmptt\spool 8. Launch snmptt using: snmptt --daemon 6. Start SNMPTRAPD using the command line: SNMPTRAPD -On: snmptrapd -On Note: **The -On option is recommended**. This will make snmptrapd pass OIDs in numeric form and prevent SNMPTT from having to translate the symbolic name to numerical form. If the **Net-SNMP Perl module** is not installed, then you MUST use the **-On** switch. Depending on the version of Net-SNMP, some symbolic names may not translate correctly. See the FAQ for more info. As an alternative, you can edit the Net-SNMP configuration file **/etc/snmp/snmp.conf** to include the line: **printNumericOids 1. ** This setting will take effect no matter what is used on the command line. 10. Follow the steps in the section [Securing SNMPTT](#SecuringSNMPTT) to ensure SNMPTT has been configured securely. **Windows EventLog:** If you have enabled Windows Event Log support, then you must install an Event Message File to prevent "Event Message Not Found" messages from appearing in the Event Log. Microsoft Knowledge Base article KB166902 contains information on this error. The Event Message File is aincluded with SNMPTT is a pre-compiled binary DLL. To compile the DLL yourself, see 'Compiling' below. To install the DLL: 1. Backup your system 2. Make sure Event Viewer is not open 3. Copy **bin\\snmptt-eventlog.dll** to **%windir%\\system32** copy bin\snmptt-eventlog.dll %windir%\system32\ 4. Launch the Registry Editor 5. Go to '**HKEY\_LOCAL\_MACHINE\\System\\CurrentControlSet\\Services\\Eventlog\\Application**' 6. Create a new subkey (under Application) called **SNMPTT** 7. Inside of the **SNMPTT** key, create a new String Value called **EventMessageFile**. Give it a value of **%windir%\\system32\\snmptt-eventlog.dll.** 8. Inside of the **SNMPTT** key, create a new DWORD Value called **TypesSupported**. Give it a value of **7**. To un-install the DLL: 1. Backup your system 2. Make sure Event Viewer is not open 3. Launch the Registry Editor 4. Go to '**HKEY\_LOCAL\_MACHINE\\System\\CurrentControlSet\\Services\\Eventlog\\Application**' 5. Delete the key **SNMPTT** 6. Delete the file **%windir%\\system32\\snmptt-eventlog.dll** del %windir%\system32\snmptt-eventlog.dll Compiling snmptt-eventlog.dll (MS Visual C++ required) 1. If your environment is not already set up for command line compilation, locate **vcvars32.bat**, start a command prompt, and execute it (vcvars32.bat). 2. cd into the directory where snmptt-eventlog.mc is located (included with SNMPTT) and execute the following commands: mc snmptt-eventlog.mc rc /r snmptt-eventlog.rc link /nodefaultlib /INCREMENTAL:NO /release /nologo -base:0x60000000 -machine:i386 -dll -noentry -out:snmptt-eventlog.dll snmptt-eventlog.res 6. Install the DLL as described above **Windows Service:** To configure SNMPTT as a service under Windows, follow these steps. More information can be obtained from the Windows Resource Kit. 1. Install the Windows resource kit 2. Copy the **srvany.exe** program to **c:\\windows\\system32** from **c:\\Program Files\\Resource Kit \*** copy srvany.exe c:\%windir%\system32\ 3. Install the SNMPTT service using: instsrv SNMPTT c:\windows\\system32\srvany.exe 4. Configure the service: 1. Launch **REGEDIT** 2. Go to **HKLM\\SYSTEM\\CurrentControlSet\\SNMPTT** 3. Create a key called: **Parameters** 4. Inside of Parameters, create a Sting Value (REG\_SZ) called **Application** with the value of: **c:\\perl\\bin\\perl.exe** 5. Inside of Parameters, create a Sting Value (REG\_SZ) called **AppParameters** with the value of: **c:\\snmp\\snmptt --daemon** 5. Start the service from the control panel, or from a command prompt, type: net start snmptt 6. Note: To remove the service, type: instsrv SNMPTT remove # Securing SNMPTT As with most software, SNMPTT should be run without root or administrator privileges. Running with a non privileged account can help restrict what actions can occur when using features such as EXEC and REGEX. For Linux and Unix, if you start SNMPTT as root, a user called 'snmptt' should be created and the **snmptt.ini** option **daemon\_uid** should be set to the numerical user id (eg: 500) or textual user id (snmptt). **Only define daemon\_uid if starting snmptt using root.** If you start SNMPTT as a non-root user, then **daemon\_uid** is not required (and will probably not work). When using **daemon\_uid** in daemon mode, there will be two SNMPTT processes. The first will run as root and will be responsible for creating the .pid file, and for cleaning up the .pid file on exit. The second process will run as the user defined by **daemon\_uid**. If the system syslog (**syslog\_system\_enable**) is enabled, a message will be logged stating the user id has been changed. All processing from that point on will be as the new user id. This can be verified by looking the snmptt processes using **ps**. For Windows, a local or domain user account called 'snmptt' should be created. If running as a Windows service, the service should be configured to use the snmptt user account. Otherwise the system should be logged in locally with the snmptt account before launching SNMPTT in daemon mode. The script **snmptthandler** which is called from Net-SNMP's snmptrapd will be executed in the same security context as **snmptrapd**. The SNMPTT user should be configured with the following permissions: * read / delete access to spool directory to be able to read new traps, and delete processed traps * read access to configuration files (snmptt.ini and all snmptt.conf files) * write access to log folder /var/log/snmptt/ or c:\\snmp\\log\\. * any other permissions required for EXEC statements to execute If **snmptrapd** is run as a non root / administrator, it should be configured with the following permissions below. Note: SELinux may prevent writing to the folder. * write access to spool directory Grant access and secure the spool folder with: chown -R snmptt.snmptt /var/spool/snmptt chmod -R 750 /var/spool/snmptt Grant access and secure the log folder with: chown -R snmptt.snmptt /var/log/snmptt chmod -R 750 /var/log/snmptt If you are using **/etc/snmp** to store the SNMPTT configuration files, secure the folder with: chown -R root.root /etc/snmp chmod 755 /etc/snmp chown snmptt.snmptt /etc/snmp/snmptt* chmod 660 /etc/snmp/snmptt* If you are using **/etc/snmptt** to store the SNMPTT configuration files, secure folder with: chown -R snmptt.snmptt /etc/snmptt chmod 750 /etc/snmptt Note: Starting with v1.5, you can use **/etc/snmptt/** instead of **/etc/snmp/** for your **snmptt.ini** file: Note: It is recommended that only the user running snmptrapd and the snmptt user be given permission to the spool folder. This will prevent other users from placing files into the spool folder such as non-trap related files, or the !reload file which causes SNMPTT to reload. # Configuration Options - snmptt.ini As mentioned throughout this document, configuration options are set by editing the **snmptt.ini** file. For Linux / Unix, the following directories are searched to locate **snmptt.ini**: > **/etc/snmptt/** > **/etc/snmp/** > **/etc/** > **/usr/local/etc/snmp/** > **/usr/local/etc/** Note: **/etc/snmptt/** is new for v1.5. For Windows, the file should be in **%SystemRoot%\\**. For example, **c:\\windows**. If an alternative location is desired, the **snmptt.ini** file path can be set on the command line using the **\-ini=** parameter. See [Command Line Arguments](#Command-line-arguments). A sample **snmptt.ini** is provided in this package. See the [Installation](#Installation) section. This file does not document all configuration options available in **snmptt.ini**. Please view the **snmptt.ini** for a complete description of all options. Note: The default snmptt.ini enables logging to snmptt.log and also syslog for both trap messages and SNMPTT system messages. Change the following settings if required: **log\_enable**, **syslog\_enable** and **syslog\_system\_enable**. # Modes of Operation SNMPTT can be run in two modes: daemon mode and standalone mode. Daemon mode is recommended. ## Daemon mode When SNMPTT is run in daemon mode, the snmptrapd.conf file would contain a traphandle statement such as: traphandle default /usr/sbin/snmptthandler or when using the embedded handler: perl do "/usr/sbin/snmptthandler-embedded" When a trap is received by SNMPTRAPD, the trap is passed to the SNMPTT handler script which performs the following tasks: * reads trap passed from snmptrapd * writes the trap in a new unique file to a spool directory such as /var/spool/snmptt * quits SNMPTT running in daemon mode performs the following tasks: * loads configuration file(s) containing trap definitions at startup * reads traps passed from spool directory * searches traps for a match * logs, executes EXEC statement etc * sleeps for 5 seconds (configurable) * loops back up to 'reads traps passed from spool directory' Using SNMPTTHANDLER and SNMPTT in daemon mode, a large number of traps per minute would be handled easily. Running SNMPTT with the \--daemon command line option or setting the **mode** variable in the **snmptt.ini** file to **daemon** will cause SNMPTT to run in daemon mode. By setting the snmptt.ini variable **use\_trap\_time** to **1** (default), the date and time used for logging will be the date and time passed inside the trap spool file. If **use\_trap\_time** is set to **0**, the date and time that the trap was _processed_ by SNMPTT is used. Setting **use\_trap\_time** to **0** can result in inaccurate time stamps in log files due to the length of time SNMPTT sleeps between spool directory polling. Note: When running on a **non** Windows platform, SNMPTT will fork to the background and create a pid file in /var/run/snmptt.pid if **daemon\_fork** is set to 1. If the user is not able to create the /var/run/snmptt.pid file, it will attempt to create one in the current working directory. Sending the HUP signal to SNMPTT when running as a daemon will cause it to reload the configuration file including the .ini file, snmptt.conf files listed in the .ini file and any NODES files if dynamic\_nodes is disabled. A reload can also be forced by adding a file to the spool directory called !reload. The filename is not case sensitive. If this file is detected, it will flag a reload to occur and will delete the file. This would be the only way to cause a reload when using Windows as Windows does not support signals. Statistical logging of total traps received, total traps translated and total unknown traps can be enabled by setting the **statistics\_interval** snmptt.ini variable to a value greater than 0. At each interval (defined in seconds), the statistics will be logged to syslog or the event log. Sending the USR1 signal will also cause the statistical information for total traps received, total traps translated and total unknown traps to be logged. This could be used for example if you want to log statistics at a set time each day using a task scheduler instead of using the interval time defined in the snmptt.ini variable **statistics\_interval**. A statistics dump can also be forced by adding a file to the spool directory called !statistics which is processed similar to the !reload file. ## Standalone mode To use SNMPTT in standalone mode, the snmptrapd.conf file would contain a traphandle statement such as: traphandle default /usr/sbin/snmptt When a trap is received by snmptrapd, the trap is passed to the **/usr/sbin/snmptt** script. SNMPTT performs the following tasks: * reads trap passed from snmptrapd * loads configuration file(s) containing trap definitions * searches traps for a match * logs, executes EXEC statements etc * snmptt exits With a 450 Mhz PIII (way back in 1998) and a 9000 line snmptt.conf containing 566 unique traps (EVENTs), it took under a second to process the trap including logging and executing the qpage program. The larger the snmptt.conf file is, the longer it will take to process. If there are a large number of traps being received, daemon mode should be used. If it takes 1 second to process one trap, then obviously you shouldn't try to process more than one trap per second. Daemon mode should be used instead. Running SNMPTT without the **\--daemon** command line option will result standalone mode unless the **mode** variable in the **snmptt.ini** file is set to **daemon**. For standalone mode, the **mode** variable in the **snmptt.ini** file should be set **standalone**. Note: Enabling the Net-SNMP Perl module will greatly increase the startup time of SNMPTT. Daemon mode is recommended. # Command line arguments The following command line arguments are supported: Usage: snmptt [] Options: --daemon Start in daemon mode --debug=n Set debug level (1 or 2) --debugfile=filename Set debug output file --dump Dump (display) defined traps --help Display this message --ini=filename Specify path to snmptt.ini file --version Display author and version information --time Use to see how long it takes to load and process trap file (eg: time snmptt --time) # Logging ## Logging - Standard Translated traps can be sent to standard output and to a log file. The output format is: > **_date trap-oid severity category hostname_ - _translated-trap_** To configure standard output or regular logging, edit the **snmptt.ini** file and modify the following variables: stdout_enable log_enable log_file log_format The output format can be changed from the above default by modifying the **log_format** setting. See snmptt.ini for details. ## Logging - Unknown traps Logging of unrecognized traps is possible. This would be used mainly for troubleshooting purposes. To configure unknown trap logging, edit the snmptt.ini file and modify the following variables: enable_unknown_trap_log unknown_trap_log_file Unknown traps can be logged to a SQL table as described in the [Database](#LoggingDatabase) section. ## Logging - Syslog Translated traps can be sent to syslog. The format of the entries will be similar to above without the date (as syslog adds the date): > **_trap-oid severity category hostname_ - _translated-trap_** Syslog entries normally start with: **date hostname snmptt\[pid\]:** To configure syslog, edit the snmptt ini file and modify the following variables: syslog_enable syslog_facility syslog_level syslog_module syslog_remote_dest * syslog_remote_port * syslog_remote_proto * syslog_rfc_format * syslog_app * syslog_format (\*) Only applicable when using **syslog_module** = 1 When using the default **syslog_module** setting of 0, syslog messages are logged to the local system using Unix sockets following the RFC3164 standard. To enable RFC5424 or remote syslog server support, set **syslog_module** to 1 and define the (*) settings. Be sure to enable UDP or TCP syslog reception in your syslog server. See snmptt.ini for details on each setting. SNMPTT system errors and messages such as startup, shutdown, trap statistics etc can be sent to syslog by editing the snmptt.ini file and modifying the following variables: syslog_system_enable syslog_system_facility syslog_system_level Syslog system entries normally start with: **date hostname snmptt-sys\[pid\]:** The following errors are logged: > SNMPTT (version) started **(\*)** > Unable to enter spool dir _x_ **(\*)** > Unable to open spool dir _x_ **(\*)** > Unable to read spool dir _x_ **(\*)** > Could not open trap file _x_ **(\*)** > Unable to delete trap file _x_ from spool dir **(\*)** > Unable to delete !reload file spool dir **(\*) > **Unable to delete !statistics file spool dir **(\*)** > Reloading configuration file(s) **(\*)** > SNMPTT (version) shutdown **(\*)** > Loading _snmpttconfigfile_ **(\*)** > Could not open configuration file: _snmpttconfigfile_**(\*)** > Finished loading _x_ lines from _snmpttconfigfile_ **(\*)** > MySQL error: Unable to connect to database > SQL error: Unable to connect to DSN > Can not open log file _logfile_ > MySQL error: Unable to perform PREPARE > MySQL error: Unable to perform INSERT INTO (EXECUTE) > DBI DBD::ODBC error: Unable to perform INSERT INTO > Win32::ODBC error: Unable to perform INSERT INTO > PostgreSQL error: Unable to connect to database > PostgreSQL error: Unable to perform PREPARE > PostgreSQL error: Unable to perform INSERT INTO (EXECUTE) > > **\* (daemon mode only)** ## Logging - Windows EventLog When running under Windows, translated traps can be sent to the EventLog. All traps are logged under EventID 2 under the source SNMPTT. The format of the entries will be similar to above without the date (as the Event Log logs the date): > **trap-oid severity category hostname translated-trap** To configure eventlog support, edit the snmptt ini file and modify the following variables: eventlog_enable eventlog_type SNMPTT system errors can be sent to the Event Log by editing the snmptt.ini file and modifying the following variables: eventlog_system_enable The following errors are logged. Note that each error contains a unique EventID: > EventID 0: SNMPTT (version) started **(\*)** > EventID 3: Unable to enter spool dir _x_ **(\*)** > EventID 4: Unable to open spool dir _x_ **(\*)** > EventID 5: Unable to read spool dir _x_ **(\*)** > EventID 6: Could not open trap file _x_ **(\*)** > EventID 7: Unable to delete trap file _x_ from spool dir **(\*)** > EventID 20: Unable to delete !reload file spool dir **(\*)** > EventID 21: Unable to delete !statistics file spool dir **(\*)** > EventID 8: Reloading configuration file(s) **(\*)** > EventID 1: SNMPTT (version) shutdown **(\*)** > EventID 9: Loading _snmpttconfigfile_ **(\*)** > EventID 10: Could not open configuration file: _snmpttconfigfile_**(\*)** > EventID 11: Finished loading _x_ lines from _snmpttconfigfile_**(\*)** > EventID 12: MySQL error: Unable to connect to database > EventID 13: SQL error: Unable to connect to DSN _dsn_ > EventID 14: Can not open log file _logfile_ > EventID 23: MySQL error: Unable to perform PREPARE > EventID 15: MySQL error: Unable to perform INSERT INTO (EXECUTE) > EventID 16: DBI DBD::ODBC error: Unable to perform INSERT INTO > EventID 17: Win32::ODBC error: Unable to perform INSERT INTO > EventID 18: PostgreSQL error: Unable to connect to database > EventID 22: PostgreSQL error: Unable to perform PREPARE > EventID 19: PostgreSQL error: Unable to perform INSERT INTO (EXECUTE) > > **\* (daemon mode only)** Note: > To prevent "Event Message Not Found" messages in the Event Viewer, an Event Message File must be used. For information on installing the message file, see the [Installation section for Windows](#Installation%20-%20Windows). ## Logging - Database Translated and unrecognized traps can be sent to a database. MySQL (tested under Linux), PostgreSQL (tested under Linux) and ODBC (tested under Windows) can be used. After configuring database logging below, you can also enable unknown trap logging by editing the **snmptt.ini** file and modifying the following variables: enable_unknown_trap_log db_unknown_trap_format ## DBD::MySQL To configure SNMPTT for MySQL, modify the following variables in the snmptt.ini file. mysql_dbi_enable mysql_dbi_host mysql_dbi_port mysql_dbi_database mysql_dbi_table mysql_dbi_table_unknown mysql_dbi_username mysql_dbi_password Note: Sample values are defined in the default ini file. Defining mysql\_dbi\_table\_unknown is optional. The following MySQL script will create the database and table. Permissions etc should also be defined. Run '**mysql**' as root and enter: CREATE DATABASE snmptt; USE snmptt; DROP TABLE snmptt; CREATE TABLE snmptt ( id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, eventname VARCHAR(50), eventid VARCHAR(50), trapoid VARCHAR(100), enterprise VARCHAR(100), community VARCHAR(20), hostname VARCHAR(100), agentip VARCHAR(16), category VARCHAR(20), severity VARCHAR(20), uptime VARCHAR(20), traptime VARCHAR(30), formatline VARCHAR(255)); Note: To store the traptime as a real date/time (**DATETIME** data type), change 'traptime VARCHAR(30),' to 'traptime DATETIME,' and set **date\_time\_format\_sql** in **snmptt.ini** to **%Y-%m-%d %H:%M:%S**. Note: If you do not want the auto-incrementing id column, remove the 'id INT...' line. If logging of unknown traps to a SQL table is required, create the **snmptt\_unknown** table using: USE snmptt; DROP TABLE snmptt_unknown; CREATE TABLE snmptt_unknown ( trapoid VARCHAR(100), enterprise VARCHAR(100), community VARCHAR(20), hostname VARCHAR(100), agentip VARCHAR(16), uptime VARCHAR(20), traptime VARCHAR(30), formatline VARCHAR(255)); Note: To store the traptime as a real date/time (**DATETIME** data type), change 'traptime VARCHAR(30),' to 'traptime DATETIME,' and set **date\_time\_format\_sql** in **snmptt.ini** to **%Y-%m-%d %H:%M:%S**. If logging of statistics to a SQL table is required, create the **snmptt\_statistics** table using: USE snmptt; DROP TABLE snmptt_statistics; CREATE TABLE snmptt_statistics ( stat_time VARCHAR(30), total_received BIGINT, total_translated BIGINT, total_ignored BIGINT, total_skipped BIGINT, total_unknown BIGINT); Note: Only include **total_skipped** if **unknown_trap_nodes_match_mode** is set to **1**. Note: To store the stat\_time as a real date/time (**DATETIME** data type), change 'stat\_time VARCHAR(30),' to 'stat\_time DATETIME,' and set **stat\_time\_format\_sql** in **snmptt.ini** to **%Y-%m-%d %H:%M:%S**. Note: The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment. To add a user account called '**snmptt**' with a password of '**mytrap**' for use by SNMPTT, use the following SQL statement: GRANT ALL PRIVILEGES ON \*.\* TO 'snmptt'@'localhost' IDENTIFIED BY 'mytrap'; ## DBD::PgPP (PostgreSQL) To configure SNMPTT for PostgreSQL, modify the following variables in the snmptt.ini file. postgresql_dbi_enable postgresql_dbi_module postgresql_dbi_hostport_enable postgresql_dbi_host postgresql_dbi_port postgresql_dbi_database postgresql_dbi_table postgresql_dbi_table_unknown postgresql_dbi_username postgresql_dbi_password Note: Sample values are defined in the default ini file. Defining **postgresql\_dbi\_table\_unknown** is optional. The following shell / PostgreSQL commands will drop the existing database if it exists and then delete the existing snmptt user. It will then create a new snmptt database, a new snmptt user (prompting for a password) and then create the table. Run these commands as root. su - postgres dropdb snmptt dropuser snmptt createuser -P snmptt createdb -O snmptt snmptt psql snmptt DROP TABLE snmptt; CREATE TABLE snmptt ( eventname VARCHAR(50), eventid VARCHAR(50), trapoid VARCHAR(100), enterprise VARCHAR(100), community VARCHAR(20), hostname VARCHAR(100), agentip VARCHAR(16), category VARCHAR(20), severity VARCHAR(20), uptime VARCHAR(20), traptime VARCHAR(30), formatline VARCHAR(255)); GRANT ALL ON snmptt TO snmptt; \q Note: To store the traptime as a real date/time (**timestamp** data type), change 'traptime VARCHAR(30),' to 'traptime timestamp,' and set **date\_time\_format\_sql** in **snmptt.ini** to **%Y-%m-%d %H:%M:%S**. If logging of unknown traps to a SQL table is required, create the snmptt\_unknown table using: su - postgres psql snmptt DROP TABLE snmptt_unknown; CREATE TABLE snmptt_unknown ( trapoid VARCHAR(100), enterprise VARCHAR(100), community VARCHAR(20), hostname VARCHAR(100), agentip VARCHAR(16), uptime VARCHAR(20), traptime VARCHAR(30), formatline VARCHAR(255)); GRANT ALL ON snmptt_unknown TO snmptt; \q Note: To store the traptime as a real date/time (**timestamp** data type), change 'traptime VARCHAR(30),' to 'traptime timestamp,' and set **date\_time\_format\_sql** in **snmptt.ini** to **%Y-%m-%d %H:%M:%S**. If logging of statistics to a SQL table is required, create the snmptt\_statistics table using: su - postgres psql snmptt DROP TABLE snmptt_statistics; CREATE TABLE snmptt_statistics ( stat_time VARCHAR(30), total_received BIGINT, total_translated BIGINT, total_ignored BIGINT, total_skipped BIGINT, total_unknown BIGINT); GRANT ALL ON snmptt_statistics TO snmptt; \q Note: Only include **total_skipped** if **unknown_trap_nodes_match_mode** is set to **1**. Note: To store the stat\_time as a real date/time (**timestamp** data type), change 'stat\_time VARCHAR(30),' to 'stat\_time timestamp,' and set **stat\_time\_format\_sql** in **snmptt.ini** to **%Y-%m-%d %H:%M:%S**. The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment. ## DBD::ODBC SNMPTT can access ODBC data sources using either the DBD::ODBC module on Linux and Windows, or the WIN32::ODBC module on Windows. To configure SNMPTT for ODBC access using the module DBD::ODBC, modify the following variables in the snmptt script. dbd_odbc_enable = 1; dbd_odbc_dsn = 'snmptt'; dbd_odbc_table = 'snmptt'; dbd_odbc_table_unknown = 'snmptt'; dbd_odbc_username = 'snmptt'; dbd_odbc_password = 'password'; Note: SNMPTT does not create the DSN connection. You must define the DSN outside of SNMPTT. See 'Data Sources (ODBC)' in Windows help for information on creating a DSN connection. Defining **dbd\_odbc**\_table\_unknown is optional. Sample values are defined in the default ini file. The following MS SQL Server / Access script can create the table inside an existing database. Permissions etc should also be defined. CREATE TABLE snmptt ( eventname character(50) NULL, eventid character(50) NULL, trapoid character(100) NULL, enterprise character(100) NULL, community character(20) NULL, hostname character(100) NULL, agentip character(16) NULL, category character(20) NULL, severity character(20) NULL, uptime character(20) NULL, traptime character(30) NULL, formatline character(255) NULL); Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set **date\_time\_format\_sql** in **snmptt.ini** to a compatible format. For example: **%Y-%m-%d %H:%M:%S**. If logging of unknown traps to a SQL table is required, create the snmptt\_unknown table using: CREATE TABLE snmptt_unknown ( trapoid character(100) NULL, enterprise character(100) NULL, community character(20) NULL, hostname character(100) NULL, agentip character(16) NULL, uptime character(20) NULL, traptime character(30) NULL, formatline character(255) NULL); Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set **date\_time\_format\_sql** in **snmptt.ini** to a compatible format. For example: **%Y-%m-%d %H:%M:%S**. If logging of statistics to a SQL table is required, create the snmptt\_statistics table using: CREATE TABLE snmptt_statistics ( stat_time character(30) NULL, total_received BIGINT NULL, total_translated BIGINT NULL, total_ignored BIGINT NULL, total_skipped BIGINT NULL, total_unknown BIGINT NULL); Note: Only include **total_skipped** if **unknown_trap_nodes_match_mode** is set to **1**. Note: To store the stat\_time as a real date/time, change 'stat\_time character(30),' to the date/time data type supported by the database and and set **stat\_time\_format\_sql** in **snmptt.ini** to a compatible format. For example: **%Y-%m-%d %H:%M:%S**. All variables are inserted into the database using '**INSERT INTO**' as text including the date and time. The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment. ## Win32::ODBC SNMPTT can access ODBC data sources using either the DBD::ODBC module on Linux and Windows, or the WIN32::ODBC module on Windows. To configure SNMPTT for MS SQL via ODBC on Windows, modify the following variables in the snmptt script. sql_win32_odbc_enable = 1; sql_win32_odbc_dsn = 'snmptt'; sql_win32_odbc_table = 'snmptt'; sql_win32_odbc_username = 'snmptt'; sql_win32_odbc_password = 'password'; Note: SNMPTT does not create the DSN connection. You must define the DSN outside of SNMPTT. See 'Data Sources (ODBC)' in Windows help for information on creating a DSN connection. Defining **sql\_win32\_odbc**\_table\_unknown is optional. Sample values are defined in the default ini file. The following MS SQL Server script can create the table inside an existing database. Permissions etc should also be defined. CREATE TABLE snmptt ( eventname character(50) NULL, eventid character(50) NULL, trapoid character(50) NULL, enterprise character(50) NULL, community character(20) NULL, hostname character(100) NULL, agentip character(16) NULL, category character(20) NULL, severity character(20) NULL, uptime character(20) NULL, traptime character(30) NULL, formatline character(255) NULL); Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set **date\_time\_format\_sql** in **snmptt.ini** to a compatible format. For example: **%Y-%m-%d %H:%M:%S**. If logging of unknown traps to a SQL table is required, create the snmptt\_unknown table using: CREATE TABLE snmptt_unknown ( trapoid character(50) NULL, enterprise character(50) NULL, community character(20) NULL, hostname character(100) NULL, agentip character(16) NULL, uptime character(20) NULL, traptime character(30) NULL, formatline character(255) NULL); Note: To store the traptime as a real date/time, change 'traptime character(30),' to the date/time data type supported by the database and and set **date\_time\_format\_sql** in **snmptt.ini** to a compatible format. For example: **%Y-%m-%d %H:%M:%S**. If logging of statistics to a SQL table is required, create the snmptt\_statistics table using: CREATE TABLE snmptt_statistics ( stat_time character(30) NULL, total_received BIGINT NULL, total_translated BIGINT NULL, total_ignored BIGINT NULL, total_skipped BIGINT NULL, total_unknown BIGINT NULL); Note: Only include **total_skipped** if **unknown_trap_nodes_match_mode** is set to **1**. Note: To store the stat\_time as a real date/time, change 'stat\_time character(30),' to the date/time data type supported by the database and and set **stat\_time\_format\_sql** in **snmptt.ini** to a compatible format. For example: **%Y-%m-%d %H:%M:%S**. All variables are inserted into the database using '**INSERT INTO**' as text including the date and time. The variable lengths I have chosen above should be sufficient, but they may need to be increased depending on your environment. # Executing an external program An external program can be launched when a trap is received. The command line is defined in the **snmptt.conf** configuration file. For example, to send a page using QPAGE ([http://www.qpage.org](http://www.qpage.org)), the following command line could be used: qpage -f TRAP notifygroup1 "$r $x $X Compaq Drive Array Spare Drive on controller $4, bus $5, bay $6 status is $3." $r is translated to the hostname, $x is the current date, and $X is the current time (described in detail below). To enable or disable the execution of EXEC definitions, edit the snmptt.ini file and modify the following variable: exec_enable It is also possible to launch an external program when an unknown trap is received. This can be enabled by defining **unknown\_trap\_exec** in **snmptt.ini**. Passed to the command will be all standard and enterprise variables, similar to **unknown\_trap\_log\_file** but without the newlines. # SNMPTT.CONF Configuration file format The configuration file (usually /etc/snmp/snmptt.conf or c:\\snmp\\snmptt.conf) contains a list of all the defined traps. If your snmptt.conf file is getting rather large and you would like to divide it up into many smaller files, then do the following: * create additional snmptt.conf files * add the file names to the **snmptt\_conf\_files** section in the snmptt.ini file. For example: snmptt_conf_files = < **EVENT** event\_name event\_OID "category" severity > > **FORMAT** format\_string > > \[**EXEC** command\_string\] > > \[**PREEXEC** command\_string\] > > \[**NODES** sources\_list\] > > \[**MATCH** \[MODE=\[or | and\]\] | \[$n:\[!\]\[( ) | n | n-n | > n | < n | x.x.x.x | x.x.x.x-x.x.x.x | x.x.x.x/x\]\] > > \[**REGEX** ( )( )\[i\]\[g\]\[e\]\] > > \[**SDESC**\] > \[**EDESC**\] > Note: Lines starting with a # are treated as comments and will be ignored. Note: The EVENT and FORMAT line are REQUIRED. Commands in \[\] are optional. Do NOT include the \[\]s in the configuration file! ## EVENT **EVENT** event\_name event\_OID "category" severity **event\_name:** > Unique text label (alias) **containing no spaces**. This would match the name on the TRAP-TYPE or NOTIFICATION-TYPE line in the MIB file when converted using **snmpttconvertmib**. **event\_OID:** > Object identifier string in dotted format or symbolic notation **containing no spaces**. > > For example, a Compaq (enterprise .1.3.6.1.4.1.232) cpqHoGenericTrap trap (trap 11001) would be written as: > > > .1.3.6.1.4.1.232.0.11001 > > Symbolic names can also be used if the Net-SNMP Perl module is installed and enabled by setting **net\_snmp\_perl\_enable** in the snmptt.ini file. For example: > > > linkDown > > > IF-MIB::linkDown > > Notes: > > * Net-SNMP 5.0.9 and earlier does not support including the module name (eg: IF-MIB::) when translating an OID. A patch is available for 5.0.8+ that is included in Net-SNMP 5.1.1 and higher. The patch is available from the [Net-SNMP patch page](http://sourceforge.net/tracker/index.php?func=detail&aid=722075&group_id=12694&atid=312694). If the version of Net-SNMP you are using does not support this feature and the event OID is specified with the module name, the event definition will be ignored. Also note that UCD-SNMP may not properly convert symbolic names to numeric OIDs which could result in traps not being matched. > * SNMP V1 traps are in the format of enterprise ID (.1.3.6.1.4.1.232) followed by a 0, and then followed by the trap number (11001). > * There can be multiple entries for the same trap OID in the configuration file. If **multiple\_event** is enabled in the snmptt.ini, then it will process all matching traps. If **multiple\_event** is disabled, only the first matching entry will be used. > > Wildcards in dotted format notation can also be used. For example: > > > .1.3.6.1.4.1.232.1.2.\* > > Notes: > > * Specific trap matches are performed before wildcards so if you have an entry for .1.3.6.1.4.1.232.1.2.5 AND .1.3.6.1.4.1.232.1.2.\*, it will process the .5 trap when received even if the wildcard is defined first. > * If you want to capture all undefined traps, you can create a wildcard event at the end of your last snmptt.conf file using **.\***. For example: EVENT unknown .* "Status Events" Normal > * Wildcard matches are only matched if there are NO exact matches. This takes into consideration the NODES list. Therefore, if there is a matching trap, but the NODES list prevents it from being considered a match, the wildcard entry will only be used if there are no other exact matches. **category:** * Character string enclosed in double quotes ("). Used when logging output (see above). * If the category is "**IGNORE**", no action will take place even if the snmptt.conf contains FORMAT and / or EXEC statements. * If the category is "**LOGONLY**", the trap will be logged as usual, but the EXEC statements will be ignored. Note: If you plan on using an external program such as Nagios, you probably do not want any traps defined with **LOGONLY** as the EXEC line would never be used to submit the passive service check. **severity:** * Character string of the severity of the event. Used in the output when logging. Example: Minor, Major, Normal, Critical, Warning. The **snmptt.ini** contains options to match the syslog level or NT Event Log type to the severity level. ## FORMAT **FORMAT** format\_string There can be only one FORMAT line per EVENT. The format string is used to generate the text that will be logged to any of the supported logging methods. Variable substitution is performed on this string using the following variables: > $A - Trap agent host name (**see Note 1**) > $aA - Trap agent IP address > $Be - securityEngineID (snmpEngineID) (**see Note 7**) > $Bu - securityName (snmpCommunitySecurityName) (**see Note 7**) > $BE - contextEngineID (snmpCommunityContextEngineID) (**see Note 7**) > $Bn - contextName (snmpCommunityContextName) (**see Note 7**) > $c - Category > $C - Trap community string > $D - Description text from SNMPTT.CONF or MIB file (**see Note 6**) > $E - Enterprise trap OID in symbolic format > $e - Enterprise trap OID in number format (.1.3.6.1.4.1._n_) > $j - Enterprise number (_n_) > $Fa - alarm (bell) (BEL) > $Ff - form feed (FF) > $Fn - newline (LF, NL) > $Fr - return (CR) > $Ft - tab (HT, TAB) > $Fz - Translated FORMAT line (EXEC, log_format and syslog_format only) > $G - Generic trap number (0 if enterprise trap) > $S - Specific trap number (0 if generic trap) > $H - Host name of the system running SNMPTT > $N - Event name defined in .conf file of matched entry > $i - Event OID defined in .conf file of matched entry (could be a wildcard OID) > $O - Trap OID in symbolic format (**see Note 4**) > $o - Trap OID in numerical format (**see Note 4**) > $p*n* - PREEXEC result n (1-_n_) > $pu*n* - Unknown trap PREEXEC result n (1-_n_). See **unknown_trap_preexec** setting in **snmptt.ini**. > $R, $r - Trap hostname (**see Note 1**) > $aR, $ar - IP address > $s - Severity > $T - Uptime: Time since network entity was initialized > $X - Time trap was spooled (daemon mode) or current time (standalone mode) > $x - Date trap was spooled (daemon mode) or current date (standalone mode) > $# - Number of (how many) variable-bindings in the trap > $$ - Print a $ > $@ - Number of seconds since the epoch of when the trap was spooled (daemon mode) or the current time (standalone mode) > $_n_ - Expand variable-binding n (1-_n_) (**see Note 2,5**) > $+_n_ - Expand variable-binding n (1-_n_) in the format of _variable name:value_ (**see Note 2,3,5**) > $-_n_ - Expand variable-binding n (1-_n_) in the format of _variable name (variable type):value_ (**see Note 2,3,5**) > $v*n* - Expand variable name of the variable-binding n (1-_n_)(**see Note 3**) > $\* - Expand all variable-bindings (**see Note 5**) > $+\* - Expand all variable-bindings in the format of _variable name:value_ (**see Note 2,3,5**) > $-\* - Expand all variable-bindings in the format of _variable name (variable type):value_ (**see Note 2,3,5**) Example: > FORMAT NIC switchover to slot $3, port $4 from slot $5, port $6 Notes: > For the text log file, the output will be formatted as: > > > **_date time trap-OID severity category hostname - format_** > > For all other log files except MySQL, DBD::ODBC and Win32::ODBC, the output will be formatted as: > > > **_trap-OID severity category hostname - format_** > > For MySQL, DBD::ODBC and Win32::ODBC, the **formatline** column will contain only the **format** text. Note (1): > See the section '[Name Resolution / DNS](#DNS)' for important DNS information. Note (2): > If **translate\_integers** is enabled in the **snmptt.ini** file, SNMPTT will attempt to convert integer values received in traps into text by performing a lookup in the MIB file. > You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling **net\_snmp\_perl\_enable** in the snmptt.ini file. > > For this feature to work, you must ensure Net-SNMP is configured correctly with all the required MIBS. If the option is enabled, but the value can not be found, the integer value will be used. If the MIB files are present, but translations do not occur, ensure Net-SNMP is correctly configured to process all the required mibs. This is configured in the Net-SNMP **/etc/snmp/snmp.conf** file. Alternatively, you can try setting the **mibs\_enviroment** variable in **snmptt.ini** to **ALL** (no quotes) to force all MIBS to be initialized at SNMPTT startup. > > If **translate\_integers** is enabled while using stand-alone mode, it may take longer to process each trap due to the initialization of the MIB files. Note (3): > $v*n*, $+*n* and $-*n* variable names and variable type are translated into the text name by performing a lookup in the MIB file. You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling **net\_snmp\_perl\_enable** in the snmptt.ini file. If **net\_snmp\_perl\_enable** is not enabled, the $v*n* variable will be replaced with the text 'variable*n*' where *n* is the variable number (1+). > > For the name translation to work, you must ensure Net-SNMP is configured correctly with all the required MIBS. If the option is enabled and the correct name is not returned, ensure Net-SNMP is correctly configured to process all the required mibs. This is configured in the Net-SNMP **snmp.conf** file. Alternatively, you can try setting the **mibs\_enviroment** variable in **snmptt.ini** to **ALL** (no quotes) to force all MIBS to be initialized at SNMPTT startup. Note (4): > If **translate\_trap\_oid** is enabled in the **snmptt.ini** file, SNMPTT will attempt to convert the numeric OID of the received trap into symbolic form such as IF-MIB::linkDown. You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling **net\_snmp\_perl\_enable** in the snmptt.ini file. If **net\_snmp\_perl\_enable** is not enabled, it will default to using the numeric OID. > Net-SNMP 5.0.9 and earlier does not support including the module name (eg: IF-MIB::) when translating an OID and most of the 5.0.x versions do not properly tranlsate numeric OIDs to long symbolic names. A patch is available for 5.0.8+ that is included in Net-SNMP 5.1.1 and higher. The patch is available from the [Net-SNMP patch page](http://sourceforge.net/tracker/index.php?func=detail&aid=722075&group_id=12694&atid=312694). Note (5): > If **translate\_oids** is enabled in the **snmptt.ini** file, SNMPTT will attempt to convert any numeric OIDs found inside the variables passed inside the trap to symbolic form. You must have the Net-SNMP Perl module installed for this to work and you must enable support for it by enabling **net\_snmp\_perl\_enable** in the snmptt.ini file. If **net\_snmp\_perl\_enable** is not enabled, it will default to using the numeric OID. > > Net-SNMP 5.0.9 and earlier does not support including the module name (eg: IF-MIB::) when translating an OID and most of the 5.0.x versions do not properly tranlsate numeric OIDs to long symbolic names. A patch is available for 5.0.8+ that is inlcuded in Net-SNMP 5.1.1 and higher. The patch is available from the [Net-SNMP patch page](http://sourceforge.net/tracker/index.php?func=detail&aid=722075&group_id=12694&atid=312694). Note (6): > The **snmptt.ini** **description\_mode** option must be set to either 1 or 2. If set to 1, the description is pulled from the SNMPTT.CONF files. If set to 2, the description is pulled from the MIB file. If using the MIB file, you must have the Net-SNMP Perl module installed and enabled. Note (7): > These variables are only available when using the embedded trap handler for snmptrapd (snmptthandler-embedded). ## EXEC \[**EXEC** command\_string\] There can be multiple EXEC lines per EVENT. Optional string containing a command to execute when a trap is received. The EXEC lines are executed in the order that they appear. EXEC uses the same variable substitution as the FORMAT line. You can use **$Fz** on the EXEC line to add the translated FORMAT line instead of repeating what you already defined on FORMAT. Examples: EXEC /usr/bin/qpage -f TRAP alex "$r: $x $X - NIC switchover to slot $3, port $4 from slot $5, port $6" FORMAT NIC switchover to slot $3, port $4 from slot $5, port $6 EXEC /usr/bin/qpage -f TRAP alex "$r: $x $X - $Fz" EXEC c:\\snmp\\pager netops "$r: $x $X - NIC switchover to slot $3, port $4 from slot $5, port $6" Note: Unlike the FORMAT line, nothing is prepended to the message. If you would like to include the hostname and date in the page above, you must use the variables such as $r, $x and $X. Note: If the trap severity is set to LOGONLY in the snmptt.conf file, EXEC will not be executed. ## PREEXEC \[**PREEXEC** command\_string\] There can be multiple PREEXEC lines per EVENT. Optional string containing a command to execute after a trap is received but **_before_** the FORMAT and EXEC statements are processed. The output of the external program is stored in the **$p*n*** variable where ***n*** is a number starting from 1. Multiple PREEXEC lines are permitted. The first PREEXEC stores the result of the command in **$p1**, the second in **$p2** etc. Any ending newlines are removed. The **snmptt.ini** parameter **pre\_exec\_enable** can be used to enable / disable **PREEXEC** statements. **PREEXEC** uses the same variable substitution as the FORMAT line. Example: EVENT linkDown .1.3.6.1.6.3.1.1.5.3 "Status Events" Normal FORMAT Link down on interface $1($p1). Admin state: $2. Operational state: $3 PREEXEC /usr/local/bin/snmpget -v 1 -Ovq -c public $aA ifDescr.$1 Sample output: > Link down on interface 69("100BaseTX Port 1/6 Name SERVER1"). Admin state up. Operational state: down In the above example the result is in quotes because that is what comes back from snmpget (it is not added by SNMPTT). Note: PREEXEC will execute even if the trap severity is set to LOGONLY in the snmptt.conf file. ## NODES \[**NODES** sources\_list\] Used to limit which devices can be mapped to this EVENT definition. There can be multiple NODES lines per EVENT. Optional string containing any combination of host names, IP addresses, CIDR network address, network IP address ranges, or a filename. If this keyword is omitted then ALL sources will be accepted. Each entry is checked for a match. As soon as one match occurs, searching stops. For example, if you only wanted devices on the subnet 192.168.1.0/24 to trigger this EVENT, you could use a NODES entry of: NODES 192.168.1.0/24 Or, if you wanted devices on IPv4 subnet 192.168.1.0/24 and IPv6 subnet 2001:db8:a::/64: NODES 192.168.1.0/24 2001:db8:a::/64 If a filename is specified, it must be specified with a full path. There are two modes of operation: **POS** (positive - the default) and **NEG** (negative). If set to **POS**, then **NODES** is a 'match' if _any_ of the **NODES** entries match. If set to **NEG**, then **NODES** is a 'match' only if _none_ of the **NODES** entries match. To change the mode of operation, use one of the following statements: NODES MODE=POS NODES MODE=NEG A common use for this feature is when you have devices that implement a trap in a non-standard way (added additional variables for example) such as the linkDown and linkUp traps. By defining two EVENT statements and using NODES statements with NODES MODE, you can have one EVENT statement handle the standard devices, and the other handle the other devices with the extended linkDown / linkUp traps. Example 1: > This example will match any hosts called **fred**, **barney**, **betty** or **wilma**: > NODES fred barney betty wilma Example 2: > This example will match any hosts **not** called **fred**, **barney**, **betty** or **wilma**: > NODES fred barney betty wilma NODES MODE=NEG Example 3: > This example will load the file /etc/snmptt-nodes (see below), and match any hosts called fred, barney, betty, wilma, network ip addresses 192.168.1.1, 192.168.1.2, 192.168.1.3, 192.168.2.1, network range 192.168.50.0/22 or network range 192.168.60.0-192.168.61.255: > NODES /etc/snmptt-nodes Example 4: > This example will load both files /etc/snmptt-nodes and /etc/snmptt-nodes2 (see above example): > NODES /etc/snmptt-nodes /etc/snmptt-nodes2 Example 5: > NODES 192.168.4.0/22 192.168.60.0-192.168.61.255 /etc/snmptt-nodes2 Example 6: > NODES fred /etc/snmptt-nodes pebbles /etc/snmptt-nodes2 barney where snmptt-nodes contains: > fred barney betty # comment lines 192.168.1.1 192.168.1.2 192.168.1.3 192.168.2.1 192.168.50.0/22 192.168.60.0-192.168.61.255 wilma Notes: * The names are NOT case sensitive and comment lines are permitted by starting the line with a #. * CIDR network addresses must be specified using 4 octets followed by a / followed by the number of bits. For example: 172.16.0.0/24. Using 172.16/24 will NOT work. * Do not use any spaces between network ranges as they will be interpreted as two different values. For example, 192.168.1.1 - 192.168.1.20 will not work. Use 192.168.1.1-192.168.1.20 instead. * By default, NODES files are loaded when the snmptt.conf files are loaded (during startup of SNMPTT). The snmptt.ini option **dynamic\_nodes** can be set to 1 to have the nodes files loaded each time an EVENT is processed. * See the section '[Name Resolution / DNS](#DNS)' for important DNS information. * See the section [IPv6](#Notes-ipv6) for important IPv6 information. ## MATCH \[MATCH \[MODE=\[or | and\]\] | \[$n:\[!\]\[( )\[i\] | n | n-n | > n | < n | x.x.x.x | x.x.x.x-x.x.x.x | x.x.x.x/x\]\] Optional match expression that must be evaluated to true for the trap to be considered a match to this EVENT definition. If a MATCH statement exists, and no matches evaluate to true, then the default will be to NOT match this EVENT definition. The following Perl regular expression modifiers are supported: > i - ignore case when trying to match The following command formats are available: > **MATCH MODE=\[or | and\] > MATCH _$x:_ \[!\] _(reg) \[i\]_ > MATCH _$x:_ \[!\] _n_ > MATCH _$x:_ \[!\] _n-n_ > MATCH _$x:_ \[!\] _< n_ > MATCH _$x:_ \[!\] _\> n_ > MATCH _$x:_ \[!\] & _n_ > MATCH _$x:_ \[!\] _x.x.x.x_ > MATCH _$x:_ \[!\] _x.x.x.x-x.x.x.x_ > MATCH _$x:_ \[!\] _x.x.x.x/x_ > MATCH _$x:_ \[!\] _x:x:x_ > MATCH _$x:_ \[!\] _x:x:x/x_ > ** where: > **or** or **and** set the default evaluation mode for ALL matches > **$x** is any variable (example: $3, $A, $\* etc) > **reg** is a regular expression > **!** is used to negate the result (not) > **&** is used to perform a bitwise AND > **n** is a number > **x.x.x.x** is an IP address > **x.x.x.x-x.x.x.x** is an IPv4 network address range > **x.x.x.x/x** is an IPv4 CIDR network addresss > **x:x:x** is an IPv6 addresss > **x:x:x/x** is an IPv6 CIDR network addresss Notes: * To limit which devices can be mapped to this EVENT definition based on the IP address / hostname of the device / agent that sent the trap, the **NODES** keyword is recommended. * If the match mode is 'or', once a match occurs no other matches are performed and the end result is true. * If the match mode is 'and', once a match fails, no other matches are performed and the end result is false. * To use parentheses ( or ) in the search expression, they must be backslashed (\\). * If no MATCH MODE= line exists, it defaults to 'or'. * There can be only one match mode per EVENT. If multiple MATCH MODE= lines exists, the last one in the list is used. * See the section [IPv6](#Notes-ipv6) for important IPv6 information. Examples: > $2 must be between 1000 and 2000: > MATCH $2: 1000-2000 > Any one of the following must match (or): $3 must be 52, or $4 must be an IP address between 192.168.1.10 and 192.168.1.20, or the severity must be 'Major': > MATCH $3: 52 MATCH $4: 192.168.1.10-192.168.1.20 MATCH $s: (Major) > $6 must be IPv6 address 2002:0000:0000:1234:abcd:ffff:c0a8:0101 or in subnet 2002:0000:0000:1234:0000:0000:0000:0000/64: > MATCH $6: 2002::1234:abcd:ffff:c0a8:0101 MATCH $6: 2002:0000:0000:1234:0000:0000:0000:0000/64 > All must match (and): $3 must be greater than 20, and $5 must not contain the words alarm or critical, $6 must contain the string '(1) remaining' and $7 must contain the string 'power' which is not case sensitive: > MATCH $3: >20 MATCH $5: !(alarm|critical) MATCH $6: (\(1\) remaining) MATCH $7: (power)i MATCH MODE=and > The integer $1 must have bit 4 set: > MATCH $1: &8 ## REGEX \[**REGEX**( )( )\[i\]\[g\]\[e\]\] Optional regular expression to perform a search and replace on the translated FORMAT / EXEC line. Multiple REGEX ( )( ) lines are permitted. First ( ) contains the search expression. Second ( ) contains the replacement text The following Perl regular expression modifiers are supported: > i - ignore case when trying to match left side > g - replace all occurances instead of only the first > e - execute the right side (eval) as code To use substitution with captures (memory parenthesis) or the **e** modifier, you must first enable support in the snmptt.ini file by setting **allow\_unsafe\_regex** to 1. Note: This is considered unsafe because the contents of the right expression is executed (eval) by Perl which could contain unsafe code. If this option is enabled, **BE SURE THAT THE SNMPTT CONFIGURATION FILES ARE SECURE!** Each REGEX line is processed in order from top to bottom and are accumulative. The second REGEX operates on the results of the first REGEX etc. **Example:** > FORMAT line before: UPS has detected a building alarm. Cause: UPS1 Alarm #14: Building alarm 3. > REGEX (Building alarm 3)(Computer room high temperature) REGEX (Building alarm 4)(Moisture detection alarm) REGEX (roOm)(ROOM)ig REGEX (UPS)(The big UPS) REGEX (\s+)( )g > > FORMAT line after: The big UPS has detected a building alarm. Cause: UPS1 Alarm #14: Computer ROOM high temperature > To use parentheses ( or ) in the search expression, they must be backslashed (\\) otherwise it is interpreted as a capture (see below). The replacement text does not need to be backslashed. **Example:** > FORMAT line before: Alarm (1) and (2) has been triggered > REGEX (\(1\))(One) REGEX (\(2\))((Two)) > FORMAT line after: Alarm One and (Two) has been triggered If **allow\_unsafe\_regex** is enabled, then captures can be used in the replacement text. **Example:** > FORMAT line before: The system has logged exception error 55 for the service testservice > REGEX (The system has logged exception error (\d+) for the service (\w+))(Service $2 generated error $1) > FORMAT line after: Service testservice generated error 55 If **allow\_unsafe\_regex** is enabled and an e modifier is specified, then the right side is executed (evaluated). This allows you to use Perl functions to perform various tasks such as convert from hex to decimal, format text using sprintf etc. All text must be inside of quotes, and statements can be concatenated together using the dot (.). **Example 1:** > FORMAT line before: Authentication Failure Trap from IP address: C0 A8 1 FE > REGEX (Address: (\w+)\s+(\w+)\s+(\w+)\s+(\w+))("address: ".hex($1).".".hex($2).".".hex($3).".".hex($4))ei > FORMAT line after: Authentication Failure Trap from IP address: 192.168.1.254 **Example 2:** > FORMAT line before: Authentication Failure Trap from IP address: C0 A8 1 FE > REGEX (Address: (\w+)\s+(\w+)\s+(\w+)\s+(\w+))("address:".sprintf("%03d.%03d.%03d.%03d",hex($1),hex($2),hex($3),hex($4)))ie > FORMAT line after: Authentication Failure Trap from IP address: 192.168.001.254 **Example 3** > This example is for a BGP bgpBackwardTranstion trap. The OID for the bgpBackwardTranstion trap has the IP address of the device that transitioned appended to the end of the OID. To create a meaningful trap message, the IP address needs to be separated from the variable OID. Because the IP address is part of the OID variable name instead of the OID value, a REGEX expression is needed. The following uses the $+1 variable on the FORMAT line so REGEX can parse out the IP address. > > FORMAT line before: Peer:$+2 > FORMAT line after substitution, but before REGEX: Peer:bgpPeerState.192.168.1.1:idle > REGEX (Peer:.\*\.(\d+\.\d+\.\d+\.\d+):(.\*))("Peer: $1 has transitioned to $2")e > FORMAT line after: Peer: 192.168.1.1 has transitioned to idle **Example 4** > This example is a sample of using Perl subroutines inside of a REGEX statement. > FORMAT line before: Extremely severe error has occured > REGEX (Extremely severe error has occured)(("Better get a lotto ticket!! Here is a lotto number to try:".sprintf ("%s", lottonumber());sub lottonumber { for(my $i=0;$i<6;$i++) { $temp = $temp . " " . (int(rand 49) +1); } return $temp; } )ie > FORMAT line after: Better get a lotto ticket!! Here is a lotto number to try: 36 27 38 32 29 6 Note: The REGEX expression is executed on the final translated FORMAT / EXEC line, after all variable substitutions have been completed. ## SDESC \[**SDESC**\] Optional start of a description. All text between this line and the line EDESC will be ignored by SNMPTT. This section can be used to enter comments about the trap for your own use. If you use a SDESC, you MUST follow with a EDESC. ## EDESC \[**EDESC**\] Used to end the description section. Example: > SDESC Trap used when power supply fails in a server. EDESC # SNMPTT.CONF Configuration file Notes When there are multiple definitions of the same trap in the configuration file, the following rules apply: **A match occurs when:** * The received trap OID matches a defined OID in the configuration file * **AND** **(** the hostname matches a defined hostname in the NODES entry **OR** there is no NODES entry **)** * **AND** **(** the MATCH statement evaluates to TRUE **OR** the there is no MATCH entry **)** **If multiple\_event is set to 1 in snmptt.ini:** * A trap is handled as many times as it matches in the configuration file * If any number of exact matches exist, the wildcard match is NOT performed * If an exact match does NOT exist, the wildcard match IS performed if **(** the hostname matches a defined hostname in the NODES entry **OR** there is no NODES entry **)** **AND** **(** the MATCH statement evaluates to TRUE **OR** the there is no MATCH entry **)** **If multiple\_event is set to 0 in snmptt.ini:** * A trap is handled once using the first match in the configuration file * If an exact match exists, the wildcard match is NOT performed * If an exact match does NOT exist, the wildcard match IS performed if **(** the hostname matches a defined hostname in the NODES entry **OR** there is no NODES entry **)** **AND** **(** the MATCH statement evaluates to TRUE **OR** the there is no MATCH entry **)** # Name resolution / DNS Snmptrapd passes the IP address of the device sending the trap (host name), the host name of the device sending the trap (host name, if configured to resolve host names) and the IP address of the actual SNMP agent (agent). If the configuration setting **dns\_enable** is set to 0 (dns disabled), then the host name of the AGENT will not be available for the **$A** variable, **NODES** matches, and the hostname column in SQL databases. The only exception to this is if the (host) IP address matches the (agent) IP address and snmptrapd is configured to resolve host names. In that case, the host name of the (host) will be used for the (agent) host name as they are obviously the same host. If the configuration setting **dns\_enable** is set to 1 (dns enabled), then the host name of both the host and the AGENT will be resolved via DNS. **NODES** entries will also be resolved to IP addresses before performing matches. The host name may resolve to the Fully Qualified Domain Name (FQDN). For example: barney.bedrock.com. Adding an entry for the host in your /etc/hosts file or %systemroot%\\system32\\drivers\\etc\\hosts may result in the short name being used instead (barney). You can also enable the **strip\_domain** / **strip\_domain\_list** options to have SNMPTT strip the domain of any FQDN host. See the **snmptt.ini** file for details. To allow IP addresses to be resolved to host names, PTR records must exist in DNS or the local hosts file must contain all hosts. It is recommended that either DNS be installed on the machine running SNMPTT / snmptrapd or a local hosts file be configured will all devices. DNS should be configured as a secondary (authoritive) for the domains that it will receive traps from. This will reduce network resolution traffic, speed up resolution, and remove the dependency of the network for DNS. If a local DNS or hosts file is not used, then the entire network management station could become useless during a DNS / remote network outage and could cause false alarms for network management software. # Sample SNMPTT.CONF files ## Sample1 Note: The examples folder also contains a sample snmptt.conf file. Following is a sample of two defined traps in **snmptt.conf:** # EVENT COMPAQ_11003 .1.3.6.1.4.1.232.0.11003 "LOGONLY" Normal FORMAT Compaq Generic Trap: $* EXEC qpage -f TRAP notifygroup1 "Compaq Generic Trap: $*" NODES /etc/snmp/cpqnodes SDESC Generic test trap EDESC # # EVENT cpqDa3AccelBatteryFailed .1.3.6.1.4.1.232.0.3014 "Error Events" Critical FORMAT Battery status is $3. EXEC qpage -f TRAP notifygroup1 "$s $r $x $X: Battery status is $3" NODES ntserver1 ntserver2 ntserver3 # # ## Sample2 Following is a sample of a list of files to load in **snmptt.ini:** snmptt_conf_files = <Notes ## trapd.conf & MIB files An existing HP Openview trapd.conf can be used in most cases but the file must be a VERSION 3 file. SNMPTT does not support all the variables implemented in HPOV, but most are available. The following variables may or may not match exactly to HPOV: $O, $o, $r, $ar, $R, $aR. Some vendors (such as Compaq and Cisco ) provide a file that can be imported in to HP Openview using an HP Openview utility. snmpttconvert can be used to convert the file to snmptt.conf format. Some vendors provide a MIB file that contains TRAP or NOTIFICATION definitions. snmpttconvertmib can be used to convert the file to snmptt.conf format. ## IPv6 * For incoming traps, a simple Perl regular expression is used to detect an IPv6 address for the trap host and Agent IPs as it is assumed that Net-SNMP is passing a valid IPv6 address. If the address has a zone index such as **%ens160** or **%1** appended to it, the zone index is removed. Zone indexes are not removed from enterprise variable values. * When DNS is enabled to resolve enterprise variables, the trap host and Agent IP **(dns_enabled = 1)**, a complex regular expression is used to confirm that the IP address is valid before passing on to the **IO::Socket::IP** module for DNS resolution. The [Dartware](https://community.helpsystems.com/forums/intermapper/miscellaneous-topics/5acc4fcf-fa83-e511-80cf-0050568460e4?_ga=2.113564423.1432958022.1523882681-2146416484.1523557976) regular expression is used. Also see [here](https://raw.githubusercontent.com/richb-intermapper/IPv6-Regex/master/test-ipv6-regex.pl) for a test suite. * For **NODES** and **MATCH**, the [Net::IP](http://search.cpan.org/search?module=Net::IP) module is used as it allows you to test to see if two IP addresses are the same or if one IP address is within the range of another. IPv6 addresses can be represented in multiple ways so this module is used to ensure that an IP address such as **2002:0000:0000:1234:abcd:ffff:c0a8:0101** is matched to its shorthand version of **2002::1234:abcd:ffff:c0a8:0101**. When running a modified version of the test suite above against Net::IP, it was found that it only passed 316 out of 490 tests compared to Dartmouth which passed 489 out of 490. There may be issues with matching some IP addresses passed in enterprise variables for MATCH but NODES should not be affected as Net-SNMP should be passing a supported format for both the host and agent IP addresses. # Limitations ## **Standalone mode only:** With a 450 Mhz PIII (way back in 1998) and a 9000 line snmptt.conf containing 566 unique traps (EVENTs), it took under a second to process the trap including logging and executing the qpage program. The larger the snmptt.conf file is, the longer it will take to process. If there are a large number of traps being received, daemon mode should be used. If it takes 1 second to process one trap, then obviously you shouldn't try to process more than one trap per second. Daemon mode should be used instead. Note: Enabling the Net-SNMP Perl module will greatly increase the startup time of SNMPTT. Daemon mode is recommended. ## **Standalone or daemon mode:** The SNMPTRAPD program blocks when executing traphandle commands. This means that if the program called never quits, SNMPTRAPD will wait forever. If a trap is received while the traphandler is running, it is buffered and will be processed when the traphandler finishes. I do not know how large this buffer is. The program called by SNMPTT (EXEC) blocks SNMPTT. If you call a program that does not return, SNMPTT will be left waiting. In standalone mode, this would cause snmptrapd to wait forever also. # Feedback & Bugs Please send me any comments - good or bad - to alex\_b@users.sourceforge.net. If you have any problems including converting trap files, please send me an email and include the file you are trying to convert and I will try to take a look at it. Please also send any bug reports, patches or improvements so I can fix / add them and add it to the next release. You can also use Sourceforge for [bugs](http://sourceforge.net/tracker/?group_id=51473&atid=463393) and [feature requests](http://sourceforge.net/tracker/?atid=463396&group_id=51473&func=browse). # Integration with other software ## Nagios ### Overview This section will outline the basic steps to integrate SNMPTT with Nagios Core. If you are using Nagios XI, see [Handling SNMP Traps With Nagios](https://www.nagios.com/solutions/snmp-traps/). Before attempting to integrate SNMPTT with Nagios, please ensure that you have a fully functioning SNMPTT system that can at least log translated traps to a log file. ### Nagios Passive Service Checks Passive service checks allow Nagios to process service check results that are submitted by external applications. Using SNMPTT's EXEC statement, the received trap can be passed to Nagios using the **submit\_check\_result** script included with Nagios. Once received by Nagios, Nagios will handle alerting for the trap. One service is defined for each Nagios host that is to receive traps from SNMPTT. The benefits of using only one service entry is that it makes it easier to set up Nagios. Trying to define every possible trap for every host you have is not recommended. For example, after converting the MIBS from Compaq, there are over 340 traps defined. Trying to define this for every Compaq server would not be a good idea as 40 servers \* 340 traps = 13,600 service definitions. The downside of using only one service entry is that you will only see the last trap that was received on the Nagios console. Alerting will be handled by Nagios for each trap received but the console will only show the last one as being in the warning or critical state. The service will remain in this state until you manually force a service check unless you have freshness checking enabled (Nagios 2.0 and higher). See Clearing received traps in Nagios below. ### Nagios Volatile Services When defining the service for receiving the SNMPTT translated trap, the service must be defined as volatile. When a service is changed from an OK state to a non-OK state, contacts are notified. Normally, a service is Nagios in not defined volatile which means if another service check is performed and the state is still non-OK then NO contacts are notified. Because there is only one service entry for SNMP traps, we need to make sure we are contacted every time a trap comes in. ### Creating the Nagios service entry Following is a sample service entry for Nagios. define service{ host_name server01 # Name of host service_description TRAP # Name of service. What you use here must match the same value for the submit_check_result script is_volatile 1 # Enables volatile services check_command check-host-alive # Used to reset the status to OK when 'Schedule an immediate check of this service' is selected. max_check_attempts 1 # Leave as 1. normal_check_interval 1 # Leave as 1. retry_check_interval 1 # Leave as 1. active_checks_enabled 0 # Prevent active checks from occuring as we are only using passive checks. passive_checks_enabled 1 # Enables passive checks check_period 24x7 # Required for freshness checking. notification_interval 31536000 # Notification interval. Set to a very high number to prevent you from # getting pages of previously received traps (1 year - restart Nagios # at least once a year! - do not set to 0!). notification_period 24x7 # When you can be notified. Can be changed notification_options w,u,c # Notify on warning, unknown and critical. Recovery is not enabled so we do not # get notified when a trap is cleared. notifications_enabled 1 # Enable notifications contact_groups cg_core # Name of contact group to notify } Note: To simplify the configuration, you can create a service template. Note: Previous versions of this documentation defined a **check\_period** of none, and did not set **active\_checks\_enabled** to 0. As of SNMPTT 1.2, setting **active\_checks\_enabled** to 0 instead of setting **check\_period** to none is recommened as freshness checks require it. The recovery notification option has also been removed so we do not get notified when a trap is cleared. ### Creating the SNMPTT EXEC statement The Nagios distribution should contain the script **submit\_check\_result** in the **contrib/eventhandlers** directory. Create a directory called **eventhandlers** under **libexec** (/usr/local/nagios/libexec) and copy the **submit\_check\_result** script to that directory. Make sure the script is executable (**chmod +x submit\_check\_result**). The **submit\_check\_result** script expects the following arguments: > **host\_name** > **svc\_description** > **return\_code** > **plugin\_output** The possible return codes are: **0**=OK, **1**=WARNING, **2**=CRITICAL, **-1**=UNKNOWN. See the top of the **submit\_check\_result** script for a detailed description of each argument. Create an **EXEC** statement such as the following for each **EVENT** entry in your snmptt.conf file: EXEC /usr/local/nagios/libexec/eventhandlers/submit_check_result $r TRAP 1 "xxxxxx" where "xxxxxx" is the text for the trap using the same format as the FORMAT statement. For example: EXEC /usr/local/nagios/libexec/eventhandlers/submit_check_result $r TRAP 1 "Drive $1 in bay $2 has failed" The variable substitution **$r** is used to pass the host name, TRAP matches the service definition defined above, 1 represents a WARNING, and "xxxxxx" is the same text used for your FORMAT line. Instead of repeating the same text as the FORMAT line, you can instead use the **$Fz** variable in the EXEC statement. For example, to generate the EXEC command when using snmpttconvertmib: Create a file called exec-commands.txt with (all on one line): /usr/local/nagios/libexec/eventhandlers/submit_check_result $r TRAP 1 "$Fz" Run snmpttconvertmib using: snmpttconvertmib --in=/usr/share/snmp/mibs/mibname.mib --out=/etc/snmp/snmptt.conf --exec_mode=1 --exec_file=exec-commands.txt Note: Run snmpttconvertmib -h for information on the command line options. You must make sure that the host definition in Nagios matches the hostname that will be passed from SNMPTT using the **$r** variable. See the section '[Name Resolution / DNS](#DNS)' for important DNS information. ### Clearing received traps in Nagios Using the above configuration, once a trap is received for a host, it will remain in the WARNING state. To clear the trap from the Nagios console, open the TRAP service and click 'Schedule an immediate check of this service'. This will cause the defined service check to be run (check-host-alive in the example above) which will then change the status code to OK and clear the warning after a minute or so, assuming of course the system responds OK to the check-host-alive check. An alternative to using check-host-alive is to create a new command called reset-trap with: #!/bin/sh /bin/echo "OK: No recent traps received" exit 0 Be sure to create a command definition in your **commands.cfg** file. See the 'Object configuration file options' section of the Nagios documentation. Nagios 2.0 introduced service and host result freshness checks. Service freshness checks can be used to automatically reset the trap notification to an OK state by defining **check\_freshness** and **freshness\_threshold** in the service definition. Using freshness checks is recommended over normal active checks (defined by **normal\_check\_interval**) because the next check time of a normal active check does not change when a service changes state. Because of this, if you wanted to clear the trap after 24 hours, the last trap would be cleared some time between when it happened at 24 hours, depending on when the last active check was done. With freshness checking, the check command will be run **freshness\_threshold** seconds after the last passive result was received. For freshness checking to work, **normal\_check\_interval** must be set to **1**, **valid check\_period** should be set to **24x7** and the following service definitions should be added. check_freshness 1 # Enable freshness checking freshness_threshold 86400 # Reset trap alert every 24 hours. ### SNMP heartbeat monitoring If you have an application that sends periodic SNMP heartbeats, it is possible to use freshness checking to alert if a heartbeat has not been received. To configure a heartbeat trap, start by creating a new service definition by following 'Creating the Nagios service entry' above, but use a new service\_description such as MyApp\_heartbeat. Next, add / change the following service definitions. check_freshness 1 # Enable freshness checking freshness_threshold 1200 # Check freshness every 20 minutes. check_command myapp_heartbeat_alarm_set # Command to execute when a heartbeat is not received within freshness_threshold seconds. notification_options w,u,c,r # Notify on warning, unknown critical and recovery. Note: For freshness checking to work, **normal\_check\_interval** must be set to **1**, and valid **check\_period** should be set to **24x7**. In this example, it is assumed that the heartbeat trap is received every 15 minutes, so a freshness\_threshold of 20 minutes was selected in case the heartbeat was delayed. Create the new **myapp\_heartbeat\_alarm\_set** command for Nagios: #!/bin/sh /bin/echo "CRITICAL: Heartbeat signal from MyApp was not received!" exit 2 Be sure to create a command definition in your **commands.cfg** file. See the 'Object configuration file options' section of the Nagios documentation. Next, add an **EXEC** statement to the snmptt.conf file for the trap definition: EXEC /usr/local/nagios/libexec/eventhandlers/submit_check_result $r MyApp_heartbeat 1 "Heartbeat signal from MyApp received." As long as the traps are received, the MyApp\_heartbeat service will have an OK status. If the heartbeat is not received, the freshness command will be executed which will set the status to **CRITICAL**. ## Icinga ### Overview This section will outline the basic steps to integrate SNMPTT with Icinga. Some of the configuration and scripts were copied from [Icinga's SNMPTT documentation](https://icinga.com/docs/icinga-2/snapshot/doc/07-agent-based-monitoring/#snmp-traps-and-passive-check-results). Before attempting to integrate SNMPTT with Icinga, please ensure that you have a fully functioning SNMPTT system that can at least log translated traps to a log file. ### Icinga Passive Service Checks Passive service checks allow Icinga to process service check results that are submitted by external applications. Using SNMPTT's EXEC statement, the received trap can be passed to Icinga via the curl program. Once received by Icinga, Icinga will handle alerting for the trap. In this guide, we setup **one** service definition for each Icinga host that is to receive traps from SNMPTT. The benefits of using only one service entry is that it makes it easier to set up Icinga. Trying to define every possible trap for every host you have is not recommended. For example, after converting the MIBS from Compaq, there are over 340 traps defined. Trying to define this for every Compaq server would not be a good idea as 40 servers \* 340 traps = 13,600 service definitions. The downside of using only one service entry is that you will only see the last trap that was received on the Icinga console. Alerting will be handled by Icinga for each trap received but the console will only show the last one as being in the warning or critical state. The service will remain in this state until you manually force a service check unless you have freshness checking enabled. See Clearing received traps in Icinga below. ### Icinga Volatile Services When defining the service for receiving the SNMPTT translated trap, the service must be defined as volatile. When a service is changed from an OK state to a non-OK state, contacts are notified. Normally, a service in Icinga is not defined volatile which means if another service check is performed and the state is still non-OK then NO contacts are notified. Because there is only one service entry for SNMP traps, we need to make sure we are contacted every time a trap comes in. ### Creating the Icinga service entry Following is a sample service entry for Icinga. object Service "TRAP" { host_name = "server1.domain" import "generic-service" check_command = "dummy" event_command = "trap-reset-event" enable_notifications = 1 enable_active_checks = 0 enable_passive_checks = 1 enable_flapping = 0 volatile = 1 enable_perfdata = 0 vars.dummy_state = 0 vars.dummy_text = "Manual reset." vars.sla = "24x7" } Note: To simplify the configuration, you can instead apply the service to hosts using an 'apply Service'. See the [Icinga SNMPTT documentation](https://icinga.com/docs/icinga-2/snapshot/doc/07-agent-based-monitoring/#snmp-traps-and-passive-check-results) for an example. ### Creating the SNMPTT EXEC statement Create an EXEC statement such as the following for each **EVENT** entry in your snmptt.conf file: EXEC /usr/bin/curl -k -s -S -i -u apiuser:password -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/process-check-result' -d '{ "type": "Service", "filter": "host.name==\"$A\" && service.name==\"TRAP\"", "exit_status": 2, "plugin_output": "xxxxxx", "check_source": "$A", "pretty": true }' where **_apiuser_:_password_** is the API username and password, ***xxxxxx*** is the text for the trap using the same format as the FORMAT statement. The variable substitution **$A** is used to pass the host name for **host.name** and **check\_source**, TRAP for **service.name** matches the service definition defined above, **exit\_status** of **1** represents a **WARNING**, and **xxxxxx** is the same text used for your FORMAT line. Instead of repeating the same text as the FORMAT line, you can instead use the **$Fz** variable in the EXEC statement. For example, to generate the EXEC command when using snmpttconvertmib: Create a file called exec-commands.txt with (all on one line): /usr/bin/curl -k -s -S -i -u apiuser:password -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/process-check-result' -d '{ "type": "Service", "filter": "host.name==\"$A\" && service.name==\"TRAP\"", "exit_status": 2, "plugin_output": "$Fz", "check_source": "$A", "pretty": true }' Run snmpttconvertmib using: snmpttconvertmib --in=/usr/share/snmp/mibs/mibname.mib --out=/etc/snmp/snmptt.conf --exec_mode=1 --exec_file=exec-commands.txt Note: Run snmpttconvertmib -h for information on the command line options. An API user must be defined in api-users.conf with the permission **actions/process-check-result**. Example: object ApiUser "snmptt" { password = "xxxxxxxxxxxxxxx" permissions = [ "actions/process-check-result" ] } You must make sure that the host definition in Icinga matches the hostname that will be passed from SNMPTT using the **$A** variable. See the section '[Name Resolution / DNS](#DNS)' for important DNS information. ### Clearing received traps in Icinga Using the above configuration, once a trap is received for a host, it will remain in the WARNING state. To clear the trap from the Icinga console, open the TRAP service and click 'Check Now'. This will cause the defined event check to be run (trap-reset-event in the example above) which will then change the status code to OK and clear the warning. For this to work, you must define an Icinga command: object EventCommand "trap-reset-event" { command = [ ConfigDir + "/scripts/trap_reset_event.sh" ] arguments = { "-i" = "$service.state_id$" "-n" = "$host.name$" "-s" = "$service.name$" } } Create the **trap\_reset\_event.sh** script in **ConfDir** **/scripts** (/etc/icinga2/scripts) and make sure it's executable (**chmod +x**). #!/bin/bash SERVICE_STATE_ID="" HOST_NAME="" SERVICE_NAME="" show_help() { cat <<-EOF Usage: ${0##*/} [-h] -n HOST_NAME -s SERVICE_NAME Writes a coldstart reset event to the Icinga command pipe. -h Display this help and exit. -i SERVICE_STATE_ID The associated service state id. -n HOST_NAME The associated host name. -s SERVICE_NAME The associated service name. EOF } while getopts "hi:n:s:" opt; do case "$opt" in h) show_help exit 0 ;; i) SERVICE_STATE_ID=$OPTARG ;; n) HOST_NAME=$OPTARG ;; s) SERVICE_NAME=$OPTARG ;; '?') show_help exit 0 ;; esac done if [ -z "$SERVICE_STATE_ID" ]; then show_help printf "\n Error: -i required.\n" exit 1 fi if [ -z "$HOST_NAME" ]; then show_help printf "\n Error: -n required.\n" exit 1 fi if [ -z "$SERVICE_NAME" ]; then show_help printf "\n Error: -s required.\n" exit 1 fi if [ "$SERVICE_STATE_ID" -gt 0 ]; then echo "[`date +%s`] PROCESS_SERVICE_CHECK_RESULT;$HOST_NAME;$SERVICE_NAME;0;Auto-reset (`date +"%m-%d-%Y %T"`)." >> /var/run/icinga2/cmd/icinga2.cmd fi To have the TRAP service automatically cleared 20 minutes after the last trap was received, modify the service entry to enable active checks and define a check\_interval: enable_passive_checks = 1 check_interval = 1200 ## Zabbix Information on handling SNMP traps with [Zabbix](https://www.zabbix.com) can be found in the [Zabbix documentation](https://www.zabbix.com/documentation/current/manual/config/items/itemtypes/snmptrap). ## SEC - Simple Event Correlator ### Overview [Simple Event Correlator (SEC)](http://kodu.neti.ee/%7Eristo/sec/) is a free and platform independent event correlation tool. This section will outline the basic steps to integrate SNMPTT with SEC. It will not attempt to explain how SEC works. There is very good documentation available on the [SECs web page](http://kodu.neti.ee/%7Eristo/sec/) and a good introduction to SEC can be found [here](http://simple-evcorr.sourceforge.net/SEC-tutorial/article.html). You should be able to install and configuration SEC before attempting to integrate it with SNMPTT. You should also have a functioning SNMPTT system that can at least log translated traps to a log file. This section outlines one method of integrating SEC with SNMPTT. Another method is documented in the [March 2005 edition](https://web.archive.org/web/20050429210306/http://www.samag.com/articles/2005/0503/) of **Sys Admin Magazine** in an article written by Francois Meehan. A copy of the article is available [here](https://www.drdobbs.com/snmp-trap-handling-with-nagios/199102017). Here are a couple of examples of why you would want to integrate SNMPTT with SEC: 1. You have a 'noisy' device that constantly sends the same trap over and over again. It would be possible to simply disable the trap in SNMPTT, but you want the trap to be logged, just not excessively. The SEC 'SingleWithSupress' could be used to reduce the number of traps logged. 2. Router interfaces often go up and down and you are receiving a trap for each event. You do not want to be alerted every time the interface 'bounces', but you do want to be alerted if it happens many times over a set period of time. You want to be alerted when the interface is down for more than 10 seconds, and then when the interface comes back up. The following outlines how the flow of traps between SNMPTT and SEC could take place: 1. SNMPTT receives a trap. 2. SNMPTT logs the trap to a separate log file such as /var/log/snmptt/snmptt.sec.log using '/bin/echo ...' for the EXEC statement. No FORMAT line is defined so the trap is not logged to the regular snmptt.log log file (or SQL table if a SQL server is used). 3. SEC monitors the log file for new entries. 4. SEC correlates the messages from the log file. 5. When a new alert needs to be generated by SEC based on its rules, SEC will call an external script which will feed the information back into SNMPTT as a trap using a user defined unique trap OID. The unique trap OID is defined in a custom snmptt.conf file (such as /etc/snmp/snmptt.conf.sec). 6. SNMPTT will process the new trap as it would any other trap by logging to snmptt.log, a SQL table etc. ### Configuration Overview The following outlines how example 2 from above could be handled using SEC. This is a slightly modified version of the example from the [SEC Examples page](http://kodu.neti.ee/%7Eristo/sec/examples.html). The example provides the following: * Prevents interface flapping from flooding the log files * Provides an 'unstable' and 'stable' alert based on how often the interface bounces. The following steps need to be completed: 1. Modify the Cisco snmptt.conf file to output linkDown and linkUp messages to a separate log file. 2. Create a new snmptt.conf file to handle incoming alerts from SEC 3. Create a SEC configuration file to correlate the linkDown / linkUp messages and pass new alerts to a script 4. Create a script that will feed the messages from SEC back in to SNMPTT 5. Test ### 1\. Modify the Cisco SNMPTT.CONF file The existing SNMPTT.CONF file needs to be modified to output the linkDown and linkUp messages to a separate log file for processing by SEC. Following is an example snmptt.conf.cisco file modified to log a linkdown or linkup message to /var/log/snmptt/snmptt.sec.log. As you can see there are no FORMAT lines so the trap will not be logged to the regular SNMPTT log system. EVENT Cisco_Link_Down .1.3.6.1.6.3.1.1.5.3.1.3.6.1.4.1.9 "Cisco Events" Minor EXEC /bin/echo "node=$A msg_text=cisco linkdown trap on interface $1" >> /var/log/snmptt/snmptt.sec.log SDESC This event occurs when the Cisco agent detects an interface has gone down. A linkDown trap signifies that the sending protocol entity recognizes a failure in one of the communication links represented in the agent's configuration. EDESC # # # EVENT Cisco_Link_Up .1.3.6.1.6.3.1.1.5.4.1.3.6.1.4.1.9 "Cisco Events" Normal EXEC /bin/echo "node=$A msg_text=cisco linkup trap on interface $1" >> /var/log/snmptt/snmptt.sec.log SDESC This event occurs when the Cisco agent detects an interface has come back up. A linkUp trap signifies that the sending protocol entity recognizes that one of the communication links represented in the agent's configuration has come up. EDESC # # # ### 2\. Create a new SNMPTT.CONF file for incoming SEC alerts A new SNMPTT.CONF file needs to be created which will handle the incoming traps from SEC. Following is an example snmptt.conf.sec file to accept incoming traps from SEC. Use an enterprise OID that will not interferre with any other OIDs already configured on your system. For example, .1.3.6.1.4.1.9999. EVENT Cisco_Link_DownUp .1.3.6.1.4.1.9999.1 "Cisco Events" Normal FORMAT $1 # # # EVENT Cisco_Link_DownUp .1.3.6.1.4.1.9999.2 "Cisco Events" Major FORMAT $1 # # # ### 3\. Create a SEC configuration file Following is a SEC configuration file that handles the even correlation for the Cisco traps. This file is the same as the file available on the [SEC Examples page](http://kodu.neti.ee/%7Eristo/sec/examples.html) except comments and file paths have been modified. ######################################################## Sample SEC ruleset for SNMPTT ######################################################## # process Cisco linkDown/linkUp trap events received from # SNMPTT via log file type=PairWithWindow ptype=RegExp pattern=node=(\S+).*msg_text=cisco linkdown trap on interface (\S+) desc=CISCO $1 INTERFACE $2 DOWN action=event %s; continue2=TakeNext ptype2=RegExp pattern2=node=$1.*msg_text=cisco linkup trap on interface $2 desc2=CISCO %1 INTERFACE %2 BOUNCE action2=event %s; window=20 type=SingleWithSuppress continue=TakeNext ptype=RegExp pattern=CISCO (\S+) INTERFACE (\S+) DOWN desc=cisco $1 interface $2 down action=reset +1 %s window=60 type=Pair ptype=RegExp pattern=CISCO (\S+) INTERFACE (\S+) DOWN desc=cisco $1 interface $2 down action=shellcmd /home/snmptt/cisco_msg $1 $2 major down ptype2=RegExp pattern2=node=$1.*msg_text=cisco linkup trap on interface $2 desc2=cisco %1 interface %2 up action2=shellcmd /home/snmptt/cisco_msg %1 %2 normal up window=86400 type=SingleWith2Thresholds ptype=RegExp pattern=CISCO (\S+) INTERFACE (\S+) BOUNCE desc=cisco $1 interface $2 is unstable action=shellcmd /home/snmptt/cisco_msg $1 $2 major unstable window=3600 thresh=10 desc2=cisco $1 interface $2 is stable again action2=shellcmd /home/snmptt/cisco_msg $1 $2 normal stable window2=10800 thresh2=0 Here is a quick breakdown of what each rule does: First rule: * If a linkDown is received (node=x msg\_text=cisco linkdown trap on interface x from SNMPTT), and then a linkUp is received within 20 seconds, it is considered a BOUNCE. A new 'event' is created with the internal SEC event 'CISCO %1 INTERFACE %2 BOUNCE' is created which is passed to the other rules. * If a linkDown is received and a linkUp is not received within 20 seconds, a new 'down' internal SEC event is created (CISCO $1 INTERFACE $2 DOWN) which is passed to the other rules. Second rule: * Allows only one 'CISCO x INTERFACE x DOWN' message to be processed over 60 seconds. Third rule: * When a SEC internally generated 'CISCO $1 INTERFACE $2 DOWN' message is found, it passes the host name, interface number and 'major down' to the cisco\_msg script. * When a SEC internally generated 'CISCO $1 INTERFACE $2 UP' message is found, it passes the host name, interface number and 'normal up' to the cisco\_msg script. Fourth rule: * If ten 'CISCO %1 INTERFACE %2 BOUNCE' messages are detected over the span of 1 hour, it passes the host name, interface number and 'major unstable' to the cisco\_msg script. * If after the last unstable alert there are no 'CISCO %1 INTERFACE %2 BOUNCE' messages for 3 hours, it passes the host name, interface number and 'normal stable' to the cisco\_msg script. ### 4\. Create a script to pass a trap back to SNMPTT Following is a Perl script that passes the information passed from SEC back to SNMPTT by calling **snmptthandler** directly. This file is basically a modified Perl version of the shell script available on the [SEC Examples page](http://kodu.neti.ee/%7Eristo/sec/examples.html). #!/usr/bin/perl # # the cisco_msg script: # use Socket; node = shift(@ARGV); interface = shift(@ARGV); severity = shift(@ARGV); text = shift(@ARGV); temp_ipaddr = gethostbyname($node); if (defined($temp_ipaddr)) { $ipaddr = Socket::inet_ntoa(scalar($temp_ipaddr)); } else { $ipaddr = "0.0.0.0"; } # use snmpget utility from Net-SNMP package ifname=`/usr/bin/snmpget -c public -OQv $NODE .1.3.6.1.2.1.2.2.1.2.$IF` description=`/usr/bin/snmpget -c public -OQv $NODE .1.3.6.1.4.1.9.2.2.1.1.28.$IF` message="Interface $ifname ($description) $text"; message=~s/\"/\'/g; open (TRAP, "|/usr/sbin/snmptthandler"); select TRAP; print "$node\n"; print "$ipaddr\n"; print ".1.3.6.1.2.1.1.3.0 00:00:00:00.00\n"; if ($severity=~/normal/i) { print ".1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.9999.1\n"; } else { print ".1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.9999.2\n"; } print ".1.3.6.1.4.1.9999.1.1 $message\n"; print ".1.3.6.1.6.3.18.1.3.0 $ipaddr\n"; print ".1.3.6.1.6.3.18.1.4.0 public\n"; print ".1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.9999\n"; close TRAP; ## Windows Event Log forwarding ### Overview The Windows utility Event to Trap Translator (**evntwin.exe** and **evntcmd.exe)** can be used to configure Windows to forward user selectable Event Log entries to an SNMP manager when using the Microsoft SNMP service. SNMPTT can be configured to process these traps like any other trap. If the Event to Trap Translator is not already installed on your machine, it should be available from the Microsoft Resource Kit, SMS or after installation of the Microsoft SNMP service (Windows 2000 AS and Windows XP or higher). **This section will outline the basic steps to configure Windows to forward event log entries to Net-SNMP / SNMPTT when using the Microsoft SNMP server (not the Net-SNMP snmpd.exe agent). It will not attempt to explain how evntwin.exe and evntcmd.exe function. Documentation on using evntwin.exe and evntcmd.exe is available on the Microsoft web site and should be reviewed. You should have a functioning SNMPTT system that can at least log translated traps to a log file before attempting this.** ### SNMP Service The Windows SNMP Service is the Microsoft SNMP agent which is responsible for handling SNMP requests from management stations such as queries for CPU utilization, disk space etc. The agent is also responsible for sending traps to management stations when an event occurs. Note: The Microsoft SNMP Trap Service is used to RECEIVE SNMP traps which is similar to the Net-SNMP **snmptrapd.exe** daemon. The Microsoft SNMP Trap Service is NOT used to send traps and is not required. ### Configuring the trap destination The Windows SNMP agent needs to be configured to forward traps to your Net-SNMP / SNMPTT management station. This is done using the following steps: * Open **Administrative Tools** * Open **Services** * Open **Local Policies** * Open **SNMP Service** * Click the **Traps** tab * Enter a community name and click Add to List * Click Add and enter the IP address of the management station * Click Apply * Click OK * Right-click on **SNMP Service** and select **Restart** After the service is restarted, a **coldStart** trap will be sent to the management station. If SNMPTT has been configured to translate **coldStart** messages, you should see a log entry similar to the following: **Thu Sep 9 21:33:06 2004 .1.3.6.1.6.3.1.1.5.1 Normal "Status Events" server1 - Device reinitialized (coldStart)** Note:If the SNMP Service is not listed in the Services Control Panel, then it needs to be installed using Add/Remove Programs. Under Add/Remove Windows Components, select **Management and Monitoring Tools** and then **Simple Network Management Protocol**. ### Configuring the Event to Trap Translator The following steps explain how to configure the Event to Trap Translator to forward system logon failures to SNMPTT: * Launch **evntwin.exe** * For **Configuration Type** select **Custom** * Click the **Edit** button * Inside **Event Sources**, expand **Security** and then click **Security** * Locate Event ID **529** (Logon Failure:%n%tReason:%t%tUnknown username or bad password%n.) * Click **Add** * Click **OK** * Click **Apply** The SNMP agent should now forward all logon failures to the SNMP management station. A restart of the SNMP service should not be necessary. ### Configuring SNMPTT to accept the Microsoft traps An SNMPTT.CONF file needs to be created to handle the Microsoft traps. All Microsoft traps start with .1.3.6.1.4.1.311.1.13.1. For simplicity, a single SNMPTT.CONF EVENT entry will be used with a wildcard to accept all Microsoft traps. Following is an example **snmptt.conf.microsoft** file which needs to be included in the list of .conf files in the **TrapFiles** section in **snmptt.ini**: EVENT EventLog .1.3.6.1.4.1.311.1.13.1.* "Regular" Normal FORMAT EventLog entry: $1 The first enterprise variable (**$1**) contains the complete text that is displayed in the Event Log Description box. Variables are described in more detail in the **Advanced Section**. After creating the **snmptt.conf.microsoft** file and adding it to the **snmptt.ini**, restart SNMPTT. ### Testing To test that the trap is received by SNMPTT, a logon failure in Windows should be created. Your default installation of Windows may not create Event Log entries for unsuccessful logins. To configure Windows to log all failed logins, follow these steps: * Open **Administrative Tools** * Open **Local Security Policy** * Open **Local Policies** * Open **Audit Policy** * Enable auditing of failures for **Audit account logon events** * Enable auditing of failures for **Audit logon events** The settings should take effect immediately, and a reboot should not be required. To generate an event log entry, you can either log off and try to log on to the system with an invalid username and password, or use the **runas.exe** command from command prompt. For example: runas /user:fakeuser cmd When prompted for a password, press **Enter**. SNMPTT should log something similar to the following: **Thu Sep 9 21:05:40 2004 .1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121.0.529 Normal "Regular" server1 - Event Log entry: Logon Failure:.....Reason:..Unknown user name or bad password.....User Name:.fakeuser.....Domain:.......Logon Type:.joint-iso-ccitt.....Logon Process:.seclogon.....Authentication Package:.Negotiate.....Workstation Name:.SERVER1.** The text in the log entry should match the text in the **Description** field of the Event Log entry but without the formatting. ### Advanced Configuration #### Specific EVENTs Instead of using a wildcard EVENT entry to match all Microsoft traps, it is possible to create EVENT entries for each trap. As SNMPTT will only match using wildcard entries if there is no exact EVENT match, it may be desirable to create EVENT entries for a select number of important events, and keep the wildcard to catch any others. To determine the trap OID that will be used for the EVENT, display the entry in **evntwin.exe** and combine the **Enterprise OID**, a **0** and the **Trap Specific ID**. For example, for the security event ID 529 used above: > Enterprise OID: 1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121 > Trap Specific ID: 529 Based on the information above, the following EVENT line would be used:: EVENT EventLog 1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121.0.529 "Regular" Normal #### Enterprise variables Each trap sent from the Event to Trap Translator contains the text displayed in the Description, User and Computer fields for the Event Log. Also passed are the individual variables which are used by the Windows SNMP Service to create the Description field in the Event Log. The following lists the enterprise variables that can be used in SNMPTT for each trap: * $1:Event Log Description * $2:Event Log User * $3:Event Log Computer * $4:? * $5:? * $6:Event to Trap Translator variable %1 * $7:Event to Trap Translator variable %2 * $8:Event to Trap Translator variable %3 * $9:Event to Trap Translator variable %4 * $_n_:Event to Trap Translator variable %_n-5_ As the individual variables are passed in the trap, it is possible to recreate the FORMAT line instead of using the passed Description ($1) field. For example, $1 in the previous example contains: **Logon Failure:.....Reason:..Unknown user name or bad password.....User Name:.fakeuser.....Domain:.......Logon Type:.joint-iso-ccitt.....Logon Process:.seclogon.....Authentication Package:.Negotiate.....Workstation Name:.SERVER1.** By reviewing the Description field as defined in the **evntwin.exe** utility, a new cleaned up FORMAT line can be used that does not contain all the dots. Following is the text from the Description field in **evntwin.exe** which will be used as a reference. Notice the use of %_n_ variables which are equivalent to the SNMPTT $n variables +5 (%1 = SNMPTT's $6). Note: In the example below, %n is a newline and %t is a tab while %_n_ is a variable number. Logon Failure:%n %tReason:%t%tUnknown user name or bad password%n %tUser Name:%t%1%n %tDomain:%t%t%2%n %tLogon Type:%t%3%n %tLogon Process:%t%4%n %tAuthentication Package:%t%5%n %tWorkstation Name:%t%6 The EVENT entry could be cleaned up using: EVENT EventLog 1.3.6.1.4.1.311.1.13.1.8.83.101.99.117.114.105.116.121.0.529 "Regular" Normal FORMAT Logon Failure: Reason: Unknown user name or bad password. User Name: $6, Domain: $7, Logon Type: $8, Logon Process: $9, Auth package: $10, Workstation name: $11 ## Xymon / Hobbit Information on handling SNMP traps with [Xymon](https://xymon.sourceforge.io/) (formerly [Hobbit](http://hobbitmon.sourceforge.net/)) can be found at [http://cerebro.victoriacollege.edu/hobbit-trap.html](http://cerebro.victoriacollege.edu/hobbit-trap.html).snmptt_1.5/docs/snmpttconvert.epub0000664000000000000000000015057214277306025016241 0ustar rootrootPKѥUoa,mimetypeapplication/epub+zipPKѥU META-INF/PKѥUtMETA-INF/container.xmlU D~٫i+[VU"ѿklqgg88'+H%9K"*Zʼnv Zmj+ ЙSYϽ".aZGihrW"wk[#.㕎 W䎥6yPKѥU" content.opfo0WXyJF T^iR[ioؗ6q)Ez{( ZM PRIc3?ܮR3ˁZV/): :qJgވ H|)@YIW^H[_``u%:ԅN3=j-ԥyʃg!yD};(/LqNqVm )Rtu ~ճs޺*&Xo9ԎimMD}阬lᕲ8R00! r: bͯC+\5(pGq#ǥpv9u$1!0^$,gV|bq"nL,\#*I3*NK6)a=CZ(]=aWCYbK=R̷s97}Ґ#)5;[$u!9 _ƽ4ns6va!z|}]Oo mk&pS (~/3؎/ǎpCO25Z* '0;߫bN\ƗP+uP_ ;{n]׊JS">' Kᚊ`B|sviĩOWuۜJ};q.}PKѥU7 ''3page_styles.csss(HLOURPM,JM/)ϵR0-(F@jPKѥU{')snmpttconvert_split_002.htmlTMo8W ٴMvx8F"{ hid+*9lwFr$~`gߛ]]}0N& \nfvR1xi>ajLDew]ugxrqq$FGu.Hq6hkr^A,Io?Cj=΃|ƶ'^pNU|$C+Hnd`X894gf;Sqo_ +H^ieౚ@ CH 1Y JHNo7zCIڱ\Y{ʬ=*hzl֏T/[._A'ټJ<0W#os4mM0 n'Xrp5h;hܬG*W2o4Y *qIg\0`O' WNi -Ȟe&K(DӸy[k*ib3!sю`1< y|AͧW?9&\k[4θ';+ƠHS age0zcfw5w0oa֏J8>7m-a)=y#<<,d(om-A:\33 w``gݒT?GOO0@XGJN ZԘ\ߖ>aUl=K;V9GBǒ|I3G6$:Rʯ۫?p(-YW0. ZudƻwwKGx7Оs,4w4Onan/0^M<,OPKѥU/9cover_image.jpgy<?|- b*"$D!I֑mdg̔d$d:vJȾ9^u~q߿:<9~a4p&<: 8ejS6֏>;{1@`r؉Ǹ?yIS'xxy~#x aÇYx88x:zF!.F.z3 ـc<| <8pQL\Oy[ߚ;ϕ 5Ed> ba=w_Eq 9 הoihj־chdl7ofckа7 IoS22?f}[\RZV^񵲪޾љٹťU>/? #o>9#'n3[s(Z쇢aY<ç='Ef.~kFvFp@TgD\eٿ_/e۬ gդ;/t}nP6ޒ.?r) BmǦd+o^e+7 7J6 )~yRfzn?~ZWU%yZ)bmkk"JkMj Z8\{Te|zc3f#S*t1ҳy<^UTG|%Ĭg gndo| tYq&d00mݳ>!;+ؒϾ bb&g)}- Zm 1vBo~aP% ]-U+'L*@#U;hѪ6u W&Q၇VC;Η#q"bG83db̑wfMznnډ=jfD357?Ot95c(ma#?;ߟX-t?.6r*SDҁ2|=}.ZyjE2[oh^G//=HkU*:%qp#5K"YzWV!poh?ɵB`x1Hr˝);wӘȄ%m@lڌqKW½$_rG)*zH*9-SjnQDu$æC >_9.},hvg_Cx}|Ő-C^sLsmAvGt</3`|K3zQgjUJi}mr C,go|(]~hh'L2?X@ \ɭO9c4MA MP9n-?ѥ]O˸>t ]#_%j͡bf{WK޾`;2B=NQl1M9VdzJX<}5kKyS88:-GClUޮF>I\C4s;iQﮛ͏>y ޜq+,τ@daG+ح@ gtM4SG|<7& ax⋽J"dftO)8YhʥWHhLИcӫu~Y;(t_Q,E~j/o INQ_THV m?by̨h6On7]qg2 "!M2i 'FobRXccE{3r%1]-79;m!X^yRy[YE=FOЁ%b(DZLJR{ƅGQZ{;{f%?dFKa$7w5X:4¸./Bt.wxV Z39j0]s]VЅ=s<Z1y ZhE!}zcX/]r;sS睿>KfI/)t5BxBw`1] kf+!D7\p] ]J/gsG=9WCì|. +v?qÎ19C`Uk}."|Iw>١n_d=kVKSոͺ?-)6p wwPfbƯq)GR-[eD"Rb\(%XxxN{JMزm L*߳TύsIAxH͜N[+`'?}Js+Av$7+uPK3!$(2ߓymS(_pS۲G$n[E`Qrաčˆ6pD,ӠAN{z/`eWucI JOixOX9| DHsW-b݃Uhg_4x%ts?}$t̋ڭw:!UAF?Ё j o?L8Cø5v=|﹝/|[W!{,ڭ]t`-1,]$^(NC EX"hܔp/uYt"?#bJ;q(Ȣ[oѬ0'H;\ g(Twǻ{ʩSkQ9.$Ċy2pL7+nblR̸{V1(MIN ag>lh<7d#;XrfXV@FC\MLJ6J+<Τt(*Z PF:9י4|D[QuAH)70mۜUWR;#Y I"aŽ)YO^kVz_ģ=jKpQ;WJzĽ];SjPguSb:@v@Rۂb/Rc!' *֤zG)]v7w!6 j>+e($`p{҇njJ5Ů@XB| t;qx|xNNV\"+ p,֚D"n wzaՊE:"̟F9|B:,9b2FeCݯT/βqiUHi՗64=W~ V&NvZ3{0w?8gEIX|V<ʹd)0](ei\ԇR$_6▣u_Eꙹs[i@itTYva;J9̻nug&`=qwt 5{?t^h"Ǘ鶥-&iy]gΜ$! iDkszDqY @(Ip 3 _E+Isn  SQW Z?P#puA~,61׽n⳩ASt@,[#xO45сLo)aPIvYm+Cv"&9i1j*W[3?xWswަzf·(mm&F9#N7*Ec<>(./`Mj3q!_6>G}h;V:WU4yGkl;( '+fmnL'Ӂwx_J9艎ζEg~zjk~2" Mj$ YG\ľVJ:\K(=,%vHDϹ3*8Wa'Tlz]c~'Ke `$Pt6Ro5uXt(b"gloh Դ>E+Ka =J&)7Βޅq/A8l;%Q_vMx-#TM1`сsJԨ$Ic =Ý( YKz9[6C8Eǂ6/`6Pj}tL}=_IkڃF~o020zLG&&O>2Ku7߄\@CFi x6%.s',$ev}GpeK?|rnK> ӓ;H98ױj|l'rPvDΩ}ʮjHQ!)+ `yG2,T0|rKl !FsO,CYDιzۈ~"_3љXE+`̐uY@ F*dTief ">WymKTj%N%l% ߟyCCX6Mw Nt> CkJXh$s~?Ơh~([Ab|yR,M~=`q=Qs6e]=TL`-1Փ&|W=9p`gs"CJ]BUQ)*@suQfum$$2XhʆzaZG#Ai\Ι?>P|g+7K-[_//ҝ ۄM n"=E3p0sXxV7~ Hw-a8ʙ q#|qX~0Ӵ&`D"kgi2v21*h]>fv *h\k<2aPx6ۀ]SS碈#H wŌ5rT&(Jv]1èZY#L=7[ n 蟵0.|rN1E INU%Z%"lg/-BK WJ)j B` &:hkRA{̙6=";fp<6 WݩxG]{[r[Mn^ElRʹ[CNKsZJt`J.CXk HNs2Ǟ[YhVc;TNcy&I}0,]ºd.gDL_o&cok6 M q޹3nz}l)FS~=?QTzlk,}{C[r3^vEL\k;1{)׏Qu(%j #kLӜg(MSCf?1"|B}IpG/%"+5WBC0]u7ȁ76?LNQgS̓ F(GOX-iXZi6Ċ6vUn?OU?(/.qC*ulB/kJ#v΅\鶧Z#xB>{FG]܋/Ё_%# #N6Õd1y:"' yݸx9K5`yW/,z革L L<^ sVq"^ _4t*+ v ̍=64U`` Bi#^9MdRD75F%$&혍|xwDmo:ᨮ-6Ϸ'b/R{뢩pS_^tེĕ1xIlCW!GL;NE[°.Gh_tBQĄHρ>BՄA0qB^ IYĄG2=Fæ~H59==M"&ËIF@ަW;ӭ(h1^q'~2iU*2G4!H$][H0<\Y\x>v&AGĀxa5ųuVN`rVy;JqymkJRq{86iӦdV4~;D?0//>f+>tkj8Szz1܀O;rW5B9|$^+ZomVz%̔`Dm=I%)%Jy0k^X0%n'J%ȾH]*M`2[wHu(NlrюzC=TwߴK 筲@7//2 3$ek$d:PٿC{\B_ރ^ [*\tɤ]+H/h9UY&,HIh-S#jP IҶvo`&:hΏ36($wIG 4o9ͫDroCS Զyo&}7vg͖}gO鲋-;S0AqYfXY@׀j =Dskg8 xJtW8\P0bC?~ѭ=k J UZ:lYN{x]m>2jW:?=F+ k釉IKWb8=Ŝ^{>YцغN 5*@Hyp"b~v }J ;ٗP`s3\/ U9~N||Kv)>dKI/9d1sN#S]uE}IW^ݭH|V@ϸ@Abn$h#ǟF7ExuQ{QYVSC:%$]2)Omm)٠@1M>+T:Kjmjx]qfҦI)ȍ;>I֦O벉1MncMj6I$xZ:*$)2bc^fr?8$y|kj̙[w20ۘĩONVݍP;oٲ"VɧiZAR eQɅ;kYq-Eu ߣmcDTáRf- ,?3XP%7it rE;A[ח #3 6=틧?ﵹ=;I'Ӿ[V9F%#%,K.[/Qksuȣt§K5PMĪ7bkO%EnۇU'j\onrxgmbZ8K.G H㷕No!9Cug1Nkޣ ;>{Ƀb§o^T.vBOњ!EТpjzlV_цY!l$KٮpG'1s qk {EGfUwohFMU}~ 5gzҜ{5JNoX{I\ ܑRhz\f&MLVM/5;Ф=?z•^%yS1~%[/SѪwB}'8AL$t] ՚S9]^B[͛ٳ M5%% 1QMJ|FN8F:<! [0u]l1}8t;d 68^vrsׯʮ'2xVN>v;iG?iV'}PZ s4r;P-Na/S&Q`ΎBL0brTe_qP?0u*U|߸| 29co~)}*QZ.|1fÏkE8 .ѸM *IVCE%l X幁\@x Mq?y^ĔhDžw(fKԓң:1mzB_1_dݿ b뛠aQ;IQP˜~fhwveu`v/]k,/C3Y`@7NuUyar??/@gLnfilU;~F`Hz۟\ٝd3aV:Tzoc/k͞Io[[/õuJ쥝U VgOᮥ-{: ?f$\ [v}KOfK7+4{6AvAckG%:]Y=gG)C@@**6TE5xݨ'vkZfd ZSM u ]ִ-2H/B K}r,[޾!yCKC:_x5tDf-~)+Sd=39EiE0 L Ozl[,'ڻJ@ ih9pn#66ڲpGTWP^H!Hh]|:t}=u.6H%#2jË-ԬUj=y >*s OX#O36US#.r_[{[kƅ:kf1ߖ%{N0LCȇ5(~ Da{);).# I9/n;厖+"i}=CQOǛD?u{sc 7BƷ*;32TNjʨ \^{M9 RB[cªBr~yF q\̝XH5[hKY_1j|TufGk&aA"K0?[-R-41)0j.Gum< "jQFiQ/P? /x:1D0LpɬCo(G݄qɛDX"vf| ^:TA&;Q^EI73]3]q)|ۼ>y&Fj% h9l>ׇ|y"zV};H?`6nߩǟ=S **+!~LOLN]Ut"tAq61ЕGZ x?u2`8=l^ӱՏ͙Mm.TiiFu),`i3TGVÜ3WJΕ: LQ_o9+;?Oƈ4V-:pv uN^&#^d[p ۔G_^%7XL8(SZS֭x0 zQD &$d6(^Nt.g9%,vs|A54]>U~]a&Z[7Iprr?Fk7;bh7L?MQoYOS1.jT[Tھ~1[[̳7,k#7zך#S*r%hCJ udeb*[yqTIG,;{ѤgĽ/W:En۶ӯyl9KSߕAÙZMϫ`oWRd,껶12 WVӬmVAtGfUEҺ>\oYXM-x\ohm횘xIVmm bZF7irק񆠇U™=rtyoT{N2&!uz2?֣6I knE33u Dfo#E?bNz6ˊih^eq|) AHQBJg|j3#9ym)c˷Q0Iz MvXx{q۬JyHW`2-umSRwYxWH2G:0sfuұ|6]ڜC52_ ^5=a\!Uo $(Cڊ+ ^v_RD;JxaF8g)AM JM~2sGبI(pr Zn:w0x%UhSBO}nXzh;Ո=ݲŭYC+ߔY˺GfB}ئ,&[4q}ޘrIEkcP`okFq}+L f׆y݋cVzA ga>|?؍iHt`Kˍ [ݯ|Zjp˧!Ō7]#3ݣbf!-qZG]ck_߽0Msܭ($fyɦܜblt2=^DN$|odE\S,o~JbS{\9*;NI0mRℾw%mIQ{N Z7P˫O7h9ʽ30zz}d3wOgÂI;🗣k=,I3"OKq.~yw猪XU:: 7I\e:z+G %he_s. Mӵ^f՝WT&R*&'9ZS}ƻvuo*@sm2<7~i&cc~Qwoe`N%r0M'}mBgq|CZxS&PQ8r8=t U` Ԍ&S2|BNrl{7\D[4QT9uCĒϸd685EUWM^fkY ݋O']HQ`瑊=A(nw)yk@ߢ~eQھ!|rO^Z<;*lYZm-xT7Q\&\qZ{x0e k\l!-Xvټ2MZK3,]|c!2'C6t4nqm4aw2H6Xg&B H0?/\1Ndϰ8BONRW7ezˎLxӽ`o)Usuz;}/A YAJxޠU(ZiJ6 FZ= =3a)L53hn"TO]+ǂLTYDV3j?Y҇Y4"d;]̏2\wKytt;>8RPZF~4>tJ|vɘ9GEe'}|4g~V;AMY_K{GǰvtN98QV6sR!<ʛ4yﻖFPîȳD-,ҁZz/7Ac(G[gJϮɓTj1D=Z(uu:#;vKx lGw:dedj#vd֠,D!TG %gE.U myK.*H,!%K  x;}fD #]f%r wx%ehVF79+Q*m c捈Γs-,ygТmT`ֱgQע):pk6^xr U[ԩ0eY:M}IǮ :z n6\7Îb^Cz̕N 5?,FzCO} ڹNBRP]<*̼;M}wN[~iYόi3's4ѥ*N^N:_ =bӧփ3b+\!k2Qku;mzziI d:#F; Hϟ { 4͹G!S1uu \7oԒ{^>DH5L콈o d0&]1(K/1 N9+Fػv2dwc_$xswGuqaJrԱ&!f]僕벬[ iPݯi)e-,t`tEu֞In_#F9jQJN b[u|, ד\ ~d:U)亩[_@̳iIƖ:`ծ}")/gCf ƁHD ǏoDkԘ:F,Xd֐dKJ8z5y^yYIKS{>zбF[R';[ΘvΆ].ZxJ"L*q]`f7!/* Hk̾ນPmJLja\+c&{(+E샌ނ9$<QS|sM.F.x1s;ɩض#ّަ?XgCBF@ qO!׹Bb.w+w"6wMТgac7 wp>zߧcGNMEm֨C"/ /0<&=`2Š" kʴ xII:0R)}>(X)s\iu)oע oc: 91n ]b^)dv??⯎f?kGMC?A,jtN̎tv\?mXh/_d%e $qljNY;o-]̥o.0mƵ)Kܞ~]Ւkո"}$eF/l^ZƵc0~5D v=Ǎ6ZdJV(GOJ/OFqټ}9׳q탉iC`]ƾA NNq)usE[{N/i4_ߜpeqy]Z}&HGiwbR1T63MZiKPTE-^WT&XlA|0_cϪn$z4ħΣ_hjEdn܏&C"R^jjJHQ$i_Pq)K['3/ӳn*O.;]($_ IhXuB&psFpXs¦شG ֚(a=Gz} EVY!:@'+6ڧ ~Ba?(/u? J{Oeؾмe7E-9a l[tXGb߸JfC0G 7fZ u 7߽9o}TsY6}UC^,ꜝsP 9Jc.}zB/X}KkoOi$k,{{yyOۄjсr]1TagZyWLÊғkP{v>L@Իf 4v-"k>#^ /z"5xYɒugH,+jk@y_L3߹ $H,ФoPqڻWyҭe21nv#(t J̤NqKB-&bCkg#qFO:XZT=sިQYps|*7<5rȇL[B{I{Sȁk?'7iʟJnP&4>,]Ҫov6%蒫h$3P>*U&kMFoEdexWr~Z=Y1Ul+{goQp5 ò$ Pa;8Fߗȕ3SHʑE U;y\n!]HUɾ l&E*.6o`X.kp@dPiU?]iҖqb؛6_ hPx\.le7㘈]s)%)B1UꗂOTgjl>w`~NyT^(h8eG^## PNY_]2+5&%L$)>O}&Q3Ix$*Xt bTT/Ӡ޾MC{F5FۢCEQ@I RUHI DҤDAzkH ^ !!}ξw1ȏ$##ow\YeE¿ /XV^`<ONIVwpj" 4̬+Ed7Y<fEgL1BuY~ k o",OHdJkZ<7Ss;|+T&r#e掾ұ /,!=Lw݄#9%U# åP_'$HrdQrCЄԏl};@to&'=GVD{ӯap,N;t.[Lwcջk]=ykNט{^/مn-*tU tOIY32J#F_üF6Kا&)/F<ž0]jw*2TZ$82IqSg~N{͈~ri~'Nr$u-dNqFdf=Wv Ψf!BIވԽ0(+l~jB+JB*R"];c͸e% kWG!,{៸40]fiB즊`g&, wwACb4@}ZH3!c-X퉓 5iώoTD'))Tdlwtp$ڐ' .<>&|GuC˪[/0_e*Te*rZOU싞I:YjUiϦO0'=l{qւ˧l0wMSM~xk&ҫO}S }[*w"%Gx},q~n񷚙 `lA2pxj,Ǭe%^K6?@_c Ϥwԇ*qpw41$ \mNv0ɖ?47Gui J"Qb) $Ve=H0,KeKmŧ%'ߠӉ=WIe|?K=*u5Y zc8fx^[X7C:A}ZCF6Ē",]Zy]qv_w:_6֒)v0:76Pp`#$%q;B#ŰM@t`D vnJUwg!bA]B0鉅O۬-e7LĮ~ֳdk.@-K|+5W^Zqo+fȤ>(]ns#z5j{``_B!: D^]R.%!?I]{5\~8dchhN]tpZeZҩl()1Mkv1>)Щ<*ap1 ~yQ4XIԳQ:/\HH& xn]kqwE*R7==5|4~'PQcs"fPt{,$nBgq9lKFgN ͼ ׏X!:(;XLJ$ARG\U">ǙعKA_EN{|c$3}VRiyy&{c-sK1&q@]Y5?8F}2^l;>-6gdQSt 7~x#Pgb#go ""[MI$x/]y8ds5|ulZI&3g^(&<$ zklԦ9E%yg98 h Hʁ2 1Esf ?[r:hYNuhPR *թ-8Qyg#eh5q =Ѭ@ujw3 qNan Vt^ۊ(is1ץ U 2AmwlTg d Il|<ƷVh0𩔅.O:(CzIZc[._qvlc{ƈ~riTys2X'p.喪ߛJzY\[Lvo}$Ӎ "VcӦPŅ;U8+OAHi2ifL/ ~є՚[f*ʄs2r/)TXlb;؀sw'r{7a/C+Bx-Кި'3=D,&C3%{ _gcblc.`ٛ\@ʁlZQg5nY /{H}y{liIIzq7!QjevA!8d!Gk01MwIJcoݸc-%Óla^ln(~zr1ɥȌؾ7}cwU'rTce:C'Kh$Z^-"`vi).LW 3Xfwǜ$~8M1S`%,/]yYvSTb@s }0Ne8O$#VtYAo{;[I1fs}΋Ϝ<gyH_sCׯL=رwOS75wP;42GBR7nȜ1g[cOHf^^v%Ʃ8Er`/_e*Yp&o&lU!$ A2Y!zc|v=x3y66-BX ]e&cJ;&OY]=Phg ެ_[3;Iͺ $ٺjlS!ɧM_jYo;\ }.:j_V߁`*2e`vkT>n[8zaX=f>r+yN>FBkT0pBT,e@:Q ]^\݂cD{=d|f>,7e?lV}-tTfp~|Nl",c&rbpscRJ^:4G;.'ByA+ijoXItǠQG@TN;!#m2D ?\?C$Mhj], iI] QwSιfDO6+|/gӐ >16իe PELbޝZ\ڥL,퐯tJl:ue:e&dJ~}H|u؜8+l$\1eS3pN䛹9R+>ݪZL^ Yh;޷\th3rUR¢]N(2'-'wPnaqVNYu#DܭH4 {5Gj3c6tH|tڋW%N2~c!4er_/B%ܝt?# ikLn U7gX`t qە.o ]v<ʻ҅,DBQ@f zk mecz0rZ`#+6 p]1*7kTS%)V%~T W)5a> 7 1\uڙ԰2f k4a_goxC0&^L#L="e>C>mX  |![IGC-K8Aj_|As$a?|BcVhv̉<镪wXSZ69-W86kו54L@*6jAcud7OG_ϻ_`8'T~}d"21Oyx3$ߵ\-v?9f1>kw7t/E C}yd7=2ڪapw64o [Ӷ%JC1Έ/z{Jdr4_~~ zǕt DXu=͢+pvjc2H̿@v1 xuI3EO6y*9;"sa3o}f\[kpYno$@}& ъk9v6.1'gL>bɵX/(khE3Q||sW?H~u6 P醏R&':3m‬FJL٧4hQTF !2htw@XyhV 1uQ{]6׷f.y}ritO0Y}'$)#ʭ+\t F;\X/Fi؎<"*SSf[\BiӼӣaџ;8b&6J\W}|463To}-sk}c5Tuַ\z邿]nf@`ώ:Tn0k^qBnffYj԰J2қKQDaxPd%֡8dvW(c\,?2Dz\W_dIO]\:g|4$ ̫H)<бb#O΃쨼/%c/LlAS'e7͜fv 'OBBJb 6e +\0< /P|5q u&W:^o,|rxyIqMlSIk\j<{ <ޙr2|ھe]GpSaLё`ɐoٯ7FB 8SBJ{hR"BQqkvȬC$gSvFsD%$3n p#IFMγCƟ6=dX0 8U ;'@%L4:x[J Tib>95$Wnkč/8r[ejr)aW}4:Wl5r,5Fvmq1|ge뒎UǞќʗ >R|d5FE.7ny ld?RX2iu1&l LIPE+`e HSMOE rdQRVޙ˲U38G4|L_4ofRͷ??S+YG˙3DݙnnYjIKzD?PuB=3[U^e!-+66d9b $(0 #b_Rav 2 Yp#٬FuGLwiDt!S >@I,'*ljXB^F/T@.i|q,-Hq^sX15N6%f -(jew4%v'0 x'cEېf!s+jM,ܰFK!pV䢲"ٍ:c{gUΑUϝmڶ>?_KzRW5c.GW35ZNOkn 6`*_ >_)R1%)s`+"dQ?9(+^y<|㩇U]Y7^PMҘ/iXRC )$@h4:F@,+!9ޜO?khoh$oY2[-J&q2U ApNz('eDJ2\LPWhy&L\hC%9'|t,5e:5 N&"($_}GZ֢U ~Xu{-But +SS1%BzwzpЃ7wYu[kV  girkZS* cպ~鎆~Ó9MRp"z6o}ǣ=,mɇA%|;6&/S&!j ?JvktdfmS֯*qp3FQC<yM5Cw^Τ3LS,3d&/ t_?>~N:ysקސ̧'ٙuǤ4\q1E O PdW$H~=B%7:V+7!x<%w+EL%R>˦S$;dhR@Q;[0^][OSqY< #ajE ]iJ/Ԑ{V!5%G1"o܎TU4d#Ac~x54  ;g]iS<tY!Qj듢Xqvpgi;oe m4=4cѭ^L+ D7]Expm~uhbIql~sgj D_?rWJJ2Ye\.ŕk&O0vR5ںV䱯S<ů^-e4+(ˏۗßhR`D]ѐ,Ǜ:=wHih;rNc \M{ڶ ~UZG''=7^~~s$J.Feŷְu8]_ W3,9hJ=lXI/Z f{l`޽䚚9LWjb HHB$15)>ȧCsށ|s Lg/[_~2ZS ugiIwwq"u'ͽ)?)SPF 痐3Z}=) `CT^FTQC @ZׁSV4+y`*g /|ʁ<Ն@]AQ [{ta-5j3 $P1O,>"\QWw映S~T9]hF-i8 z=bx>Jlk$wO@а:P 2j$ڻ9  fy.@);Z:GmIN=ІF/%Q3ծhȆ(${f.VK˃XHl@ƢTayH䌪psB3Ym͠d𐷆SF: V[+_}u= :Eq#v* 9F.Xp;,m:{(-7bٻ5&59SICCt: X1GWK79}rEƘ 5oB͑Έlʰj̫W]5F/t&&xbN%a!=BC6nX]SB˸Z߱\&5k!RW}-&;eօCfXUn-;b5|ȩq;eB `^a \FkZA Pu^W\#'eXvYYFSmR\fkj}N0Db"$tC}V>G7GSBXImk>ly1x>'cʃ_C≂T K#eB'hh[4ŭT$1*=GՂ q][4_rf*= }JJcxErF~5."UA䳮`Ҋ58*K~4^'S5c4+ni9ğ{YjGp2v9Ce'G72# 9k4;7EI8G@7 8e5oܵram׀a Q b$aggc3fj +8 6ڦ*\Q%v5Jg> 9Oq"!wJ!<_`VJS(6joYj rfe]2Cl{W^MbCo0K^FU}Մ/,,jgv $ܑb}܆+38mE"~OUX-0:g:t_B{*1yL ƆOE R*Zxv<?sdA ` }\cH0gש_68&kX |IKQc&JN UO|sJ|*PcA}:t΅0uPCKOp>US9*7;{%4KD> ?IŽn6EHix_VSD".GA,MAudGD@c)tE2~?hJD B.3.8ʍj7=YmYt;B t@r8gu͙܆gޜ\۰??kȋ 61}-BcM_&${|a>n?Yc[OB8P| tVkCʇb. aQA'$G)-ٛ%"w KSxopS"|4|vZQ81Ҏ(@4{K.ntS&j_G<*fH4k0kw uǠ#\XK*<=;.$EJV)1c{ұv.e]hQǬ&q~c2o+͠߈T>pW_|'kDqLIX&cOzNk#C♺o lQ'xQ%[AY ۻ%R26gq:lZͶ8rAt:\qb&z}-:zhA6ùyWKIߑif4y4j I1%%#W9N[{lG,H°n^** X!I ;rEf] ofƿ0Z}=gU[LƊb/&{B*E''dxy8ڥ7rrmzh9a׵]?}r#=I^0r D +z}Cb':V|d#@A ոLx&:|~Y%ș4Olw5{"~^:k(?#VWEM贔xFʁd2R63H~m:=`;:e!6q!$Mt!3ǾDB hXyJPN1-V‚lkئDB p1M?Ӑ_T oLEea #o"FYt&cέ=ScfOC_YiNt/i]*u11g/R#?z{T ,_j,rx̍P@b} 5/J(^5hⴃYQl > O\CVM.Z-tD_Rq1^aP`{3m%Smy\>rCDÌ|B1+T[ѩ{rށ 1Ld:25>~/he%x ׺)ՠ$#+wkMC:Xt1% ioh>;o`jW6(YwI ?$ ^ !76+VcJTEރ~zw)Z4348Mz-Sp-A.l^& h/׎1䗄\m%AtrkO&܄u wX/eNAW#p]7k9LxtaotdYqf+vC1@a9u'hT Phk"2oG>iz^WMf7[dp48-oر^6Ux|)l. h(:\=g. k/YN#o,kQX/!o|^sʐO5WDxdJ𞇴G!X6- `D߁2 `BvE);b@EBbi@s-ۅ( 3O !yH';7BJ/)C]D$tAoKrMK.Tk*ݮ\֪K0~] ʤx7d+Ao69Uv:E ZQߓuaa2_&0Lw?"x-=o&9^5ƝM -ik &{ۓ3łPAXRlcHƾs7֪;ΦnȐ~vAyib@U9aY^VV7f>-yZx>ُVg#]]BOZK7|ߓ]nB_SȪ&rtFUvV OQ $ i'2F#[QesYt̳!yŔK 9zҖClܧW 3˒^C"$7P,՟To0}yK%^xhL> 6&+A[_{RXA 8X81G׏45yh Z>134@<24NJFKEpjNh@a^ R0ʇy h&rd~/IG 9ز%# ǧq@4 p.)tlZgCwP;bqe5Zێ&b4M>jxsAUSG*k70j'(孿1ͺ3מ_9Sj5g%avfBg'59BJߞ||J^Ej>B\g*GJ$ ՍdRK[ګyd>;zxx֒ob~4f/TW{ϏC2qBbn me-IooYg퍳Er|`)M i0ꀧYvvG3\*/q#HUt>g%&` `ws5ɩ+ 3w^wi>}y#_Oy oP-`9V; ׊pk@=SAuq5-o똏ȅ8X9|F($#_M# kpThk׼g1ז_zD%lIߟ;Pkőb>!gDg 9F8BsNmbZ: e,F|*X1߉ۄF`0(Z* 5 Nbmj2I56\Y >%e9-j{yVG&eZ 'HG};7 ˳x,?ۂW1~T(VT^ :奻м{ǰf=5FO))JFy;;4?0y3g,#iGTc`ykKJxytV3BF&FC")F"zP%'y0jUj#%×(+YꎮͦY|@zRf.-Ap]4OOpƱdUK8(+m ~R`HiYmOm{n1̍Eѓ6C|wV񵷛Y5/Fķ>:'qIg5HlSgV#YzoRܔm{~tk5$s na]MH헹 *EgN S'TԈR6(y8;/3>@DAZժf#Z@ 9`ie!Y^}jUzh$EBsllmZ30gZ…CZ CJo~g^qksL6n bv0=nysB5Zԋy 3`D!"J,_lW 8-Wt )&"PJ b+u%d8ˆ;3 L1a,7FfD-S'_x'H< vK/ - +?N^"Bc BS%%"n5EM`9Oj*HΧ,w8r2@ 9rKa@LAicU~_ԭǣ[Hֳ}ҩ)[EDx΋P;בBg /BEV!6APcV`aQZ~ʚi%s0:Rh[ I.7`" vDQn%4 !`45= )$)Wɪ HNZU RݣA8Zc$ Oݼsn1¿mg}#lQ(Y$K["݄j~$z|j-ez I߽XgUwC%ܔXnKvf W预[>SQ7BLLmX"ɒ%_*`Vq,5FADⅷ JHv {>zA'ffsgs_Y u p'>%ȩu j} *kI}{΁Yz \U1'=^cD[_Q:x ^A>@__l =bnQQ$зZK4;bi$ȍ\gI"z5=d^͢c1T(Y>#tJ> SF6YLRCʀ.L5C?GçBrw~%F:q L3 z?`oy͉)T&v::s /#j}@ OE\D!Tj2v@<[CDk)QUe^܉]o8 *qI",dDZ 9JՄ!O)B|_|ɗ[O!z`1`b BT-x EHCJx:Nń\ ?9<}SN2?#t+v`v3o TY?IfVIqRa?Pr+ص LZ;t0p?2MTvVQo].t}әrR'V7{FмEFcҲD:lj%iP aNٲ:Z-ϢVIo-6:ozOo:`_iWyzÖ[ԁ<<O1TB^4P=EKVRI|*|qLo||; , װ!;%pvss  ྭ  Xȇv<'ҍ.7ޫ UrGNWd@r7kq o$s M*hF6oeи9j*0 o#EIyZrP2q mev~wT#w2:? ,m3R/숨jNFMyqٹ,e4)=gR&3zm{u>k4;=z҆4ZhF,s@U(׮Ȼ.$di0[ʾ0T'rj?a5y^a ֫bI*IBaOz#lEYpW@sW=vJ~TPwIˉ1W `,#7 pMV`hz6nC츢Щ*c"PiA`"96禈~==⭔wr8sS-F\%٤w5dog~0z~>$#Ysg<̜qjo3E:3"ItmH\މO2Ya Ī!3Czݜ,= N=aeehTdId!ʹtΦ&o{ǴSD-b[/N~rC"X(Ʌ]!O\u$ ٱ#ylEF6}\?ӫkn2b9!<1=b0sw+;dL|붏wxj%1ΏhM[2kRgiL[Kv!n՘6sO=rl֘EꗜR6*.Hrf#T 8{ׯ EU=-I0[\M˱2 nQЍU(HFuj<񃊖:!X+yG-y>\?w*Weţ;W}gї∵Mh.Z͌MʍW(;3_U)鸹}C#C~Ոq| ,5!ّUݟι[eiGHrmf_GsҖUG#r3`Y)/P=Wgƾ@0?Qc m"nòÊX@PqŸeL44f5){9Ws#1@2jE (Afgۣ\KAPڣi݇5 )Lnܢ X0O_1uut0 A3yACO,VX1 2>ohan^FVEh8Z5dGE'[n^G荖dϷ+P0K(etۏ)3`%EWV&a-<6}a^)OMN$'bCz$.C4 erOP(X^x&vfv*gYmA%*A7 *BÝ bDŽh| a$O1bFOe;DÀmICE_-}0hn:SV` C28[zt ǃyv]s7EY?rլ:/tNKJS~sKV ~xkxz,7璪J93n"VQ 1D%bJ)ʜ9} ^whp_E69a_`lS/XV^W*e+/KءUwXHV {;@>9$5)ʹL2Vå6C^?$Mm@u(%~lj#*yN7߿XnuqEn]6w\5TfWL֎=h 8ٵ|WJdPo&uDg"4ܱ*_m=:hTl}V;dNvċgѪQ߱BE$+@Q#w}G8:z;i^QUz}[g%\y5\iExO/mK{f-sEsϑowqj] 4b'_߷p3b0:ki܋~iDaAw<Ḗz~(<$Cwħ=4bbKY+0:x\D2jU)gbybnqr9\ YFtS?mvMob %*JmmQNg?>X wo`?iL%?yښZYqzg1yh•g8MT*ܓe8Kl_Ue ּTJ/ wg݂K"S;eo:HF!ؖ+gՆnjW/Bdf2bBpz`T`Yi>jxAN~0>j&V}Jn* Ĝf6 3ڭƄ[ y[[luWzĥlW|{Mz5ӼXIr$ ; qďc;# +"^ s ?S̩H>Z`^=m{hշgN@3vף0BCD32c nur"hk7HI5ݡZ f%Ӌ鐃jUK㮄w*ogy03'Wt MmYnl~H#{ cg6%zJlvKxT5~D׋'osҁLX NXS6O{5_wYЗ6kxt@1<Ѿ*n몐ѩ_-ϛF&X1 CC2eFEu PS+jj! Oyfc^ُ%j gէt<::4}AE8P`Z=[u{ gM/|_BFhWjPth𠃌Õt\|?&7*; tT_51T+MlB\DmM)d`]#84 Si҇£Ne\{MRl嗄'~W{ZLc%=q Ǫy蠷qs|vub^W4?ɴptXF8;@ư9JNNj%P?D@EZ".e܍!NTKB æ1گ#"Ka9d~T }yf "gưTyH GI9&z x+ \g84D[rdH$&-݄L6 BO8X?{H\7NV0fi0،+xa:2_ *ᧇ64Η-\r^g+rÞ"p]  rR'\h]mL'!1O |Ui;f8[u`gtJ`k 'ͫzUYHD쪌uԶ6u4݅'. 5xa\(^:2FZoz%{'.Y5B F}0E]\{ᴬHnX7,0&Ys%X)99@rDp\ е*^+Z&F vV QېOPp:Dy"(GʍJ OZ( ^zj1JJ}˺b=[OɻN:hDp4m9*|Mre/'pXΚ4ͮ5XӗfNmσmXد8^?6 ~|xIn)\9" C t^:(A  : 8#<&L᳇D@d^CI{(2Eh~3ȻzM_s\G߫' + SƲSSW~aD"AfטYZ, Βr9p 2nKgUs(!{OV]Gmv8SҠdyԏy_X?^oIcPQxUwLkF=&1!vbh8 9hZWr^%΃Ee`l'{doJȆd?hW-J(`]LO$l{bR[g(a``i"[c~7lQEZe3Y%7?Ӓы d.ju9X ?bKY?s68#$H A $H A $H A $H A $H A $H A $cL5|=b0&[?Vp$-8lw "Ƥ@t T@ k!TV\؎AcۡFy>r +@dx(p0K\.9ly>xzL"vT,OC' <{.~M\)+'hon='[wK~BaS]?ۚ{@WZ.=-|_;ӏjRI&jRI&jRIjG1m%O+ A $H A $H A $H n>&Mc{wkX@ik=]K^Uop>VݶX[ڹL_ @% At1-[%8xuZԟgwN ge::[Nq` W~Vc7vq> fza F]PLѨ_e}+@b|v˩8Q R[a5y=cC=s"DoL*_/h+gl`1s~>lH GEu r%]Ctx1<Tqhf:2B)2!) #qL>ʚ4R{,nk+ٽ2U1 #Ɖ^'fŢ(/s*yk8?8!8̺!7O%T'ϓVmn\1O_JY@h>"„lB>ljMk!My֯tksmo*8qB.k<oXD{}sBira0cAW>&I+ a㢒ﭷаK6~ gq/>l XƸﴛsqslC() A38G`6mP oyfG<~(qXxΤv z34܍A"|[ۈd`8϶l-O?MU+\0T|eq}Ǿ'7ZCf5asm+igTW$B ROst<_=%_D_HW>g8ʋ*zoɓe)ϩ hFuJhP B cYb.c3DA `Җy=rz6oq"H|d9Rs®>&8.>~|~%pAZJ4 ۬&FM&c~6[$'-NbΏR gt~ IS̳Ԩ+.b@"o{a̳']i>v`]L z감~zH'.JԷpQu*x>n51\r>ZV Մi ID?/^%;` OF'vpOYu^h:P D F5bZC7YJʯ x܊sTglB'YEk !ό'3K']-8GccPl҄+%G_ u= ¨<meӾE|$dE?hQ)CIDXg5e5 PtNEc {Af tpOD/ҵlsB?.ވrj) Pɴi-/yM ;ټJfrB3ha֊7鷻Ɯ,ǯ)N[%]=?U6(:$eh r=b9Uo/+Zۃţ3  yIialol4q3̾UJ0_S%fNW624ܟwF>{.Rs)@ovLlBCkW0\5X]HuV<[GL.  V1EO<-]v"%z&xb =Re]KGbY/'YC˞D~OvD|[ůoYN.Ȗbd>jVy+lyeؗ/SLjD!~#H8ԐR6?#s_7ɞS^q@gwaw;8kʂyȘct5ǘ/K$gjȍI9/?.jmKOY|U(r>$}Y^ b4mƱ: oG<픱GUIO~Iq|"BJ1 Jj\9IH<f.-A> _K7ȸOf3묉>݇\ᷪVS);KouZ-/C+h$Qй RGX5qDRZ]ӳAoNL$(=1ιak*[bր:%&}͟D|~,C]D˗/pΞrTϨKwsk9hPa-\XPġ@_XE`ڵ0") =8(\]a}FQ]%hF16 jB @f*Κւ`h3c+v mP>\MZjQ:qa0N#H|{sށ†a=34 [`8,kΦ ~fN`)qj7%ɼC TZɄ,L1׹:vTev}+fk];0ET/K(Y5?#9p[BOj QSl鼸t\uTFUWSɚ7՞IMq vW aI~zh,?kqg/TL6wUq=tQOnY`~`|b]ݕ&6ԱRW Jb .C'G=D}!e?H( M|/dU.CrV%uOY.}I?4hط|ZUCy>Cu]4VM A'UG] f+Qդ_i,7Qz^DS2ۖ " =’|¤-nWŞ.tKؕ؃e{DVF{UcnM%|W_s0pZD7}5K ̔4 ؈$eOo֎,NG fk[]RʭOUޓm0em3 d[7O4/;74GN5o-y3[mɖ4?;hם"?&ר?^(T&V;"\W:Ua7:,ul)YI1 l.BBUC>+匔?21hPi^`_4$G\'pT$f-ؤ5t e;FQ$?btzrJ\?K[ԋ+,Ef@jĎXC7d9Eb.Ix mx9*E:y[.=q }i!%k\zR],:j(8ޗʀ:U_+'XڊT`0~>,H1-(D8YM2ydJ_o)VhUl{F(p-$)tW3|Y-cѹq. a{<#BWտc}#DZQ-_&?W(7>C)}ǢN\goDG,џ&ѶA6[E/N14so=*UsX8g$4)!777Ȃ3jc;th keN"',Ic$fXdmag2 }+ͺP^Y;xu8 6\p1iJFWwħRG[Lc;ķ>̥֟ + PD㝝B6/sPU”somя\cx$^ xQtm<{:C3v_!@qqڹp%Ϟ&W`eh c K-OkV+~Te0Rx*qvă ~d > iDtMpJU~[i<{/7g_TܽPW/Y$RC]ww ? VJ뙖_*I97kzX> {9JE>Mn/X)/V #bh(Pi8-o5 #В@܏4e4k黅}^IHьb/LcI1 L^S `bǍִ}F-fH!$:B&pGs1X x}a!υi+.{7jpoP6gjm_=0Jܺ;}bg2cgp1]M5N">1#NJd*K2^@GU:p}ێJZtL)$ZCK3se&ҙj+Ec"+.W.V8Ŧ`M)Ό78}S aY/`ƷLR~1:#tXE#7f?6_22-)Yxr'C;Qբƣ.6ESM\!;B|C@fm&=XF/`6ᝠL.w␢!t}6h0Ş>q0&`kL#< w6)LoNTOra|b;N<7a>uwjўm$[<':ӱUW0W 6_A$gFe31b1B_,ܖ>s\ _2jR2Խd\aSF\zn֩ ߭_Y ̑KM x]9ROȻ'X9%Ӎ1;1P#U?*,G?VT-g;Ϳg $uGE×ꋽߛ,(}*gNl߰Ș6A ?)X4(P$ǥUGvRmX$fEBAÉm|&Ww-H7 iҗEZҡUtwT۫op.L>x|ڂ9)6Pnׄ,- Ħ|gAJ#ϧoɋR؎srm9NeC1B'OO:+;J`x!uV${.(-xp:3T/qdA{`#A-k )hŬDChZRRx9gfԆ< 0*#=*Z|4+U|^<=*Qt5c{KaiqB +m'ѩJfYDԭЈObh%SG{rOTy[PsO{"!2sfZK[g~F/=;+LiqK _ӠϧA} .՞ot. ?"Fٲ&DVL,qr O!bc|Z'g0eC -8՘ 2\0!qqQNn&K")]_'B!n 9inv =8XiD=ux`IՏ=,_N7?*E[uZ )QixRa笖7!#ubxINWm$H A8PKѥU6W(titlepage.xhtmluR]o0|_Z'I*KHU9`NQ{mHמ7{riZ'\ټ--zϯa*0ŠK1?ǭ+El0@i=n~ebeC!Dwoe=:BȗJ!"n!կ=bV70׵yqе ״{ L,CMA3Ja8Ss8E/JcSw~iAЏҦ{p/bL.KYzLa[Y?䝔Rx.t[(N>1l^iQKƝzM kD4#|zGq!D4ko R21Q eLtN٭ΫxM'lJ_$y=7iYn/>zQuy͙4ΖiN8-9r" Yڐ\yu3u k鲤ͨW0Xed# 2)S>7}bU=H!"tӡFS`E0}<97Éӥ3rĈ O |QR3vբ"GfGiX%AU{PKѥU'/snmpttconvert_split_001.htmlTn6}W %nM}*F18-cFQTIʶ~sAZ@R3gq'`p%WQлʹ܎j[t:F0 fVp@m܄GO p.<\ 7hJD%KjfcDN Wl/簷UQHL0CȘFߵ(to*}ɜg( \D9է?|[ZznC,ޢSGQKGmcQΐڸ@mI%(IRrV[v@B#Q=0ChT i΍<-d*ERj["X;8m-6dT:H/ߡ„G6R+ϡB]Ɩ}Iq4xU xh); ORm[t "1 1PK47׈(xэq.|~j~\e+E)ZŠ甁.Yb`Ds(jFlԑժ ܱ]Pqjk3 c~q04Qo/(^soWބÀGE#ld<3 @՝wbj>4RnZ6N񇓏PKѥUoa,mimetypePKѥU :META-INF/PKѥUtcMETA-INF/container.xmlPKѥU" 1content.opfPKѥU"{wtoc.ncxPKѥU7 ''3page_styles.cssPKѥU{')snmpttconvert_split_002.htmlPKѥU/9C cover_image.jpgPKѥU6W(titlepage.xhtmlPKѥUn}pjstylesheet.cssPKѥU'/Rsnmpttconvert_split_001.htmlPKѥU9Psnmpttconvert_split_000.htmlPK tsnmptt_1.5/docs/snmpttconvert.html0000664000000000000000000000550414277306025016244 0ustar rootroot SNMP Trap Translator Convert

SNMP Trap Translator Convert v1.5

(SNMPTTCONVERT)
This file was last updated on: August 30th, 2004

License

Copyright 2002-2022 Alex Burger
alex_b@users.sourceforge.net
4/3/2002

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

SNMPTTCONVERT

Some vendors provide a file that can be imported into HP Openview using a HP Openview utiltity. SNMPTTCONVERT is a simple Perl script which will convert one of these files into the format used by SNMPTT. The file can contain multiple traps.

For example, if the file ciscotrap.txt contained:

 rpsFailed {.1.3.6.1.4.1.437.1.1.3} 6 5 - "Status Events" 1  
 Trap received from enterprise $E with $# arguments: sysName=$1  
 SDESC  
 "A redundant power source is connected to the switch but a failure exists in  
 the power system."  
 EDESC

Executing snmpttconvert ciscotrap.txt would output:

 #  
 #  
 #  
 EVENT rpsFailed .1.3.6.1.4.1.437.1.1.3.0.5 "Status Events" Normal  
 FORMAT Trap received from enterprise $E with $# arguments: sysName=$1  
 #EXEC qpage -f TRAP notifygroup1 "Trap received from enterprise $E with $# arguments: sysName=$1"  
 SDESC  
 "A redundant power source is connected to the switch but a failure exists in  
 the power system."  
 EDESC

Note: The #EXEC line is added by default. This can be changed by editing the SNMPTTCONVERT script.

snmptt_1.5/docs/snmpttconvert.md0000664000000000000000000000513314277306025015676 0ustar rootroot SNMP Trap Translator Convert #SNMP Trap Translator Convert v1.5 **(**[**SNMPTTCONVERT**](http://www.snmptt.org)**)** This file was last updated on: August 30th, 2004 #License Copyright 2002-2022 Alex Burger alex\_b@users.sourceforge.net 4/3/2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #SNMPTTCONVERT Some vendors provide a file that can be imported into HP Openview using a HP Openview utiltity. SNMPTTCONVERT is a simple Perl script which will convert one of these files into the format used by SNMPTT. The file can contain multiple traps. For example, if the file ciscotrap.txt contained: rpsFailed {.1.3.6.1.4.1.437.1.1.3} 6 5 - "Status Events" 1 Trap received from enterprise $E with $# arguments: sysName=$1 SDESC "A redundant power source is connected to the switch but a failure exists in the power system." EDESC Executing snmpttconvert ciscotrap.txt would output: # # # EVENT rpsFailed .1.3.6.1.4.1.437.1.1.3.0.5 "Status Events" Normal FORMAT Trap received from enterprise $E with $# arguments: sysName=$1 #EXEC qpage -f TRAP notifygroup1 "Trap received from enterprise $E with $# arguments: sysName=$1" SDESC "A redundant power source is connected to the switch but a failure exists in the power system." EDESC Note: The #EXEC line is added by default. This can be changed by editing the SNMPTTCONVERT script.snmptt_1.5/docs/snmpttconvertmib.epub0000664000000000000000000014454614277306025016735 0ustar rootrootPKѥUoa,mimetypeapplication/epub+zipPKѥU META-INF/PKѥUtMETA-INF/container.xmlU D~٫i+[VU"ѿklqgg88'+H%9K"*Zʼnv Zmj+ ЙSYϽ".aZGihrW"wk[#.㕎 W䎥6yPKѥU content.opfUMo@W|ٻd*5URoh;6k^;^J j3yYV^N|MhQH1{;[]%<h]/1v)Y&^Q4`etN14ZjUQj4k%!qKnxǽ@sPW6Uf1RP GOXU^xVFVGpχb9w Sg깂d{o%gӲ׺GR,2=|'O/]g\h!_c:BǓ ù,vl*27Y}`G*r{/y}CD ) ݺch^h~1cwq66+VpG~+ŰK: 8s9K7 w>M].d*CY= 恙.鶒kCm=æ*GKK:$kJԲiRB:ddO4&1/L nNQ[ƭ{OVamO5r5c̻H :+_&. ghwd-zаqm2=>43;9`o̿hw jǕ{GxW\JA@ P5sD1:U OSEdm؟)T4h'sywmb<_d6=eLPKѥUBtoc.ncxuo0+,OC4)РF5it奏JR9v\ _CxΧPlʍ7dS#s59Û iZbS!ۭ+!vkc{HR?9sOTE슶,i(0撒iKR 5|x(}P:W~0qpeO!gݓhpT}O6}-FaYeӘ"n*Q$@7B}iiʚ:QXeP%?*1ɢPKѥU)8+ snmpttconvertmib_split_002.htmlVmoH_1rS%M 1Olת"cxu~:oPH흷}y}HbxD.X`d!KgBNo]t#IfdI_Z\ڗѮ`Oaܗ Ri=x,\4.ʫ,-B?OV=&B?|J/dDYv[_,ֹ |0 =t>W+ڵ1xO*bAd).2oʮV{i̱ąl)@B\ͨwc &2F!J $XBhq-t'ܟ}eoܝdԺ "c6)5`>w:OPEmrη|ɲ+$ٮ50> r ̩u?U)4TЗ-t7/cG P#g|;'┥LTfPufE,QObܟBc_x`\Icشtfo#6ڻR V8nH~z:qk MT(puՃδJ٘xx.@y>$9g?Cgd3M2E|d11<) xuls&L( 1 3caCmS17 6FAaޜ܍3٨Z3.Lo wcV͋HFR R7Aȓ^ Y%odŐ 46[&H ${DekOKn6_0ke!ɪm܏w2Mǀ# xIj/LyT}ԶNpހ4pL XFϴ Ӿ5>emY>mU&p-RcA!E_"Mr)W 5B<+a*T;_6}~ VccOêbDv? l%6~sv:^BHď)pX}j=<:<[1ƣËWj|0>xPJhNai&t9YR)ʺJ#GJ/igŪEF+[O/ JZN8P (DUy*A!ml|X5|'TS:A(ɉJr5Ա+=#U{ul! v:IsMuџ \#:CAft*bNwDu>PKѥU9-Jcover_image.jpgwT6 (]4ARDAz4"E"" "["  (G*ED:^J3w5oo$?O4NihS ~-W6ol[;{ؼQⷶzi 5kIS̙?FE9{,59:z:zžY3s0\`cggg``becgs J5TgiϞecgd/o-o J Rۀg8C~s4t*3g(5?8@r s \ؤ.ke7D <{HKuUr Jwjhji3zdlyljfOлa ?ffe~/kkshxdt 9>1?8<cONEPRߵl**J*?vQs 7αP[\aVP ͅ+?ͲaWծIZd1w[4fL&4 v̭ո*:%J4k3Xc'Hhb46]N#.i  SDzL"3A7: r*"|;0^> 7vIb;8x*%d%*:H@'';I'M3BBoÿގcBe(g {f-\W_y0x3g`{.'_[ w17r+j-ntODggXPY^N}Y6MuwɎ ׽$vn;\ ۓXeTtj*_ޚKxǜnMW ixEæwqjggu4+n /5_u\X9U+~fjv{\$ X&: ރ@d+>yˏwdbTl@#QxbW4 y#@AMء,P1R0g[V}/\i4/>:Hm hcٚ=wIg.i(dH~lF[I ^!ٴ2.$s!V5[7E?NH Yc1(D{ \Lz'ʇf'%]6,=dW+4T}`!\D+B[IӽU XMy${R͙V~_l"rZgbM%`0ᡛIZ.WJϔDDۼڮ$ ӓ+fь,7a3#)cthE1A4Tv\eq2p~Q&mNLZɀVc_9m+,c͎VПYXMMFvI`]Wsƃ#oOݗ6E}u79 vNPYCvh\w{h^ʄ`@bh>q'do\3ݲ7:zhB&OǑt#R-׃Y7N<5e{TR s bz fIHAlxg5Q0u{#06ð^[UkՐK̽g2~{C{Bo#z?r\+J9=tYͱ}]fq1Mj! BUGA6mA~>(~ 3Fx/`PR ukP*v-j5'˴L<"] <.}Ŕزh2}@3r匂0%Xcz%fv^&ʳQf9eJDl 4v܎<_B-m#B֎|pwQ#^wv ~ꥸȱ5Qa*ՁL, v9jVܐQZ2nݯ+渞"H@yeCR~LLAmY]7"I}7A,1qJU/5ΰdA$(}"P|8"$i: cߴ  Ϯ n[4 [׵^k#`OP1"{?weHݸqd9E]2>MUGk.V:2?x83g<~/;\o!犜: Io9A{`:G8ۋ*r.aAoh ýw e.{Mj2˂ 4zI#,D "hun<`#i)%dӤ(?qrn_Kl!ܧMe1KyS5P9] >ND|1y<"2ll9=] &ۣ/Gf(owӃUEZ֯ V?eyg U%h{#+ ztciirB Vd?qfɱBTh0A>ڞJkeGO 3~6Z̈Zzϫ[irca$*~Z!G,fo(?I_xWxiZIP~p Ⅺuj<;9jđ>׌A'zv&CZCvm 7ۄ'w%\ BM}f;0e*{6] ǨG֬ck5dBH.$=N`-#;,_q4 _̶l`qO W!=n l>m,TQ\Qvg& 1VR`n!G YNsVzƚ\Wי:aun"J[P nQkXa,y0#udnwPWI5FcB_aG5|Oi6.?qcYmǐILdf'OJ8>.J$IbX&Q#PQ(# Fjf[l-T;ɵx ?ȓo_N4*>jH?rԜ}qYGhAc&ML&eʇ$GžN"oZ[f$Pf1 Em#{`ZCFv1NR,jzxpU%%eX똦^LcF6 SHFhة`dGfi@2j1z"`T(t\Т,MVśKY;7(l-<%ѪI=N 4f hftys8>d6%|r{w>ybܩuLgvY̴=q( /$y8]a`Ƀ "-9PeByintgّEJ}r%$`]1j~ uKKi`t}%2MGJA75F&B nlo0Ta\')n=D}py/?K^ n:OW!g`'?NFs=K|91Bn©b[ A Y"΁X`ID,Vse7ӇOh2Ɏh3*jK^=vRB e ѹLD7G3P}4%0Ly*|=R)26?*( nU o^{Pg?sJ:n9?E |yު56FސeWk` h'^e VqI=-,5]/aJ:DU8P_m<\J'w&4aU_TUi=x֏ bt}f|eQME8֨}2g﹑2c8Tu /2TiU'@p< "V{:aa~w|(e8y!UwJ6yx 8LÞguk=u.įǃę4< e| 4>}Q /ǚ0ph٬QfdhmmEx!#s <iTwp&8[IF@~7 ; Y:hxOl>Rb 꾹LJu4d|/ YGI"KX_Eb.-N8 =:k~KчQit Nu{\lz4|h.ly-zX>,UZYG68+/!`,*{:^2ٻā#14ki-z/ .B =˛֞%=*ةБBKSqyP ԧNMrhݥw~qU{T,M"wE E3Á?p SV;gNle%!=\#[sduNoU"3{YIbç+<[L|ὑ] M<ûWn>AUwǑ=`Vǃo)qt%Pxh1F؞;X\ؽVd hZ \X{97G7ƺL=J9Hτww$| #!Y|oAkyxǀ]oJMx#Tq8ď;7˸MsFG+11ScQ8y=Y0 X5/ހ _.˕݌nyމ{tW6 ySh嚻Wל: |{D:|Jq3eQjrʇ눉J1ro. gE+}6lnU$8\I&L "ץ@ƈ%^ߕ+c%©[SFĭqL]p$r1Jn9kr"vE 72VS"|rOX2,FkDrf"[#'+!,bA8~,^N㉄/xJ@G_81,3|`S-Z"b%ޕ83/j"+<ļwFʗ ΙfԯE3 -b,uYp[ t".xOXʣ;A*OH.eLIH&tdCsDMʉ=JTPT{ O@5;^̚9\ )ߗu9F[wMT (]kO%5)lg֝ñ }Kdydrє}>6 . ǧۍ>ǩ(v΃)h>h{Qu#iw 'QJ'̆m5jk'ccF2]TKWERٌT'':х_\ڃ5xDEW>J n[$/XFUjT@LNy4콮b?)mϷp0:̷fv'P( 8!9)+u&X`¸낙hwyA |i ty(i'R,o0}UHxa q&0;`ˉ%vf[2lLDOQ2DmrEj8dk yhbѽAqGE>14,j>ꑤPrM6;8U >R{W.Ѿ5wW@0aK?}t >Aq#m,Bu/Pܘom jj؆/)]ו=#;ދ/RCR%d8Ӑ4$-T^<;r!Alߵ9RjA7hs %~fkPf7UdZDk̥c3QG^"#w+%cptؓP$\3u = Sik8q E!dKjS?4^@v_a;PN7FMԍdr};m NDkƨf߫9pQoŦN[/$伧8Ge]& od~a/-.Ws>T{ >ڴꌑA6͡&u(9$F"q5Q6W މ=[Sۦ9,7`(y =?fr[7Lv}{l-[J ^T:ȿKs83 f\xJmՂlV5 ĉJVbt(};w ?|H(ԗʪZReh[ =˫M~GؐJy5ƖǮĺO0zT6߰Ԓ/|j==Gr&&6iN WYnefnߺ?Vxa\^Lu=;ﳡ= ȃ]tأo%gnL?@OxOwAkR;;$[ji>7bzMu>To:_`NYd&j)<]~lbej)jX3ͮOf WoMHw\bp fP1u͂77TRҫŗx #bŮwCM~UqᙢcP VVݑҗR_|{ZZ+w>ko{ՋvK^O=nwaы;,;4.>ՁJJQˍ:-/ 17LcIQ\&pv=ڨ ƁpZT3UJ%O19O-@3+}u֓J5_),ًLz:9Ynϣ!4B~xd:s0u\Im+4@d'eT>ZU+t,e`k3;!5DŽ]lB/p3*پYv'3\\IT%4xŹ(p bK,/VqO!V9 ]$[էMs:iWxNpq)SIˆd&F! C -1o%Ch Wqƕo?=N^i?$h[)& Q`9VbHl*B]Dv9Mҟ̲k*c[EV}Ns-yGXkn0k /Ϣ].i4~ckx ;,?w>,?h0&Of}("QNsyE7 9D(Q GgQcEYUuc'"auVOe>;5ݬ\D3E$@'J\I$ںf!m6T:xy9ɫr ?Rd«_UIt%U*噿 {"PpI*yq^7n珍֍W.O@{W|"d8ytCg$R2H@;: S}3Z>~N9R?|{Y}HIy? |78}RD J#,69 -N(Vvyhװ? Ԧ쓀$u e1)˟m k-ϸQ>+>7v@-qN. &[BBS ݐ #]kZTW {P@Gڏp~3>U[gXV|^OLfwt;DžX`԰WO8'#Q{`SLxRaV$Q#$4ؾP f<{ZB_V;)e]_"GC>1_kR$5S/-x7;,X=܍vTu EQ=rg$v~ݨ J{$С]r X@\INs0o"n'sS5]F/oˉ{R:B+;,-DZlhMX^ LM6n֏ 6#FK eԞI,JQoW*`ǭ QL-ܷՔцƛjWby/MV؄b?"kQ_w\T! Ա'i]yS %\/_Wфq>t鑀V"]tҕPێn]ijbg"''eI@cQ>n} Al3mAy0%S /OtX7^`a8%"iIZt { l]  \6r+--o6G "K7v;22$B!{<<v>F}‚Xz%5Q4۹'tfG[7Zo0]lewi/=, W_{KoУ1ۦYU4mPBdҜ,(ːl]W3ԗY>MtLidiwnge-7ӝU&xUtW:פD]:m! ʁ 6}}u֫h bxዟ=XPZțbJce]xD?X[g1a9I,30Ks:f\W.Gg7K41yj6ʷp~tUʩ@88FTI'FCRE hO "9n4I4ڵǎYq n{ZW)fT@w_L.x/vRdy: K“ȡCCwmr?iu&q^Vr&A JEIx"cD{,,/@QezAECSIU)M窴*kbEBݶkĒk3+J9ם(50bWܱC½G=?@5YE/\[V+hew̠; .|#2VakA\F\M&_?nve_S4Ҋą.;DZ塡ϖz+$j*봰 _yT5,y&|lʌа]My}U_7Թ0 w"v)+aYM"'KlI}'SxÈ8x?:GC QЈx_F_{Ʃ5hn=35ᵎP|,]q $Gp!+s0x ^-ciG??_ {: \&g;k-yJcP؂:E)" gkd5p n噣%g~u䵕e(=7%({]]}_n[zx"Uxywښ.c[Hu*o&rv|a8N{ՓeF}*O8(eԲ\Z]:1Sq~/H(5?,ӟD.l}j7 r߀*m5';ɵ'nGgjVzӵ`yA\ؗZJ'θlJ"zyyuꂃn7 MxxaڡMAmv׊sHERY'8rՋWm/?b3|-4ICb#9|j#%ӷoeq@TY{;Aât1HUЎh9]F|mv>Bf`83ȢZIX#\8k_'# ^7 iN%O/'mf`.]u(<1"K{1^M)_n߂Ū`a?K%?_8x$w(79ć/nGz+mI-퇘EP/HO9lⒾ\ǩ"Ps+ъњߡg1Lwʵ^3KY W8]Gp ݘA|Z'[573/ Ժ|?q`c^wY.7 K=c#tgFuPִ6u0M.Dkʢ;h[cM!)ý^hm-*=mKE,zdt,d 5Wz?r->~8X{[~PxRoyrNvK>qQ͞о P]VE"8@GCBflƃUպma|_5@C$o)AyS䡿HJWxx4FL[iVILdKN(1*N-hw3[=Bf%*۱KxL{W+/l,\ ` /(_;>c S &_<.] 숾#ҳj^A]Awt&9W J@;p/\Ncr`[ź%_@i8V24)m¿O^T| 8;Y[7. |Oy"W*~‹땧oObmu+dPPtX1`[Ԯcg8Hj.fYA?)\͞fo:|:ߢ֣їJ 㽧K̿pU8zu%P}|/:35ZOdLG]GemMzT%oKإmj݇{l +C ,%p.rIT & ͳyP`=|WebfW3o2! X}GOX+_Lޛ$/Xx ,rfk^fGG #wKsjW]l_Wz$[3Y5JB^ njW:: / |!>b9%#,3`ռd)n2\}cU&'rMīt~ r'*bĶO>nyV/w_j @!tԫ&15Y/K'= ;no2ֻ$lSvӽrf9ofues4 kE|Mf迤^YE_M`l TcP(Jݭ^H^UtA4eLquTvCoJ(z_#  ޞoo,̍]fTvr[Y9.ۡGeq/}=V(m<\H[J?*-ؖzdH΢6wұx[b4թѥ "Ksء=33dRJAvdf/@?D 2U^Yͣ "D.M,LKxyNj!mv+a /:_ Vo.kmjnŮ;Sw6*[25j5 VQ+ilH/NxcI0-4'=Dݥ⧚e#|hf}td?eqG A& F.@fY˿KÞ<>o'`zH ⛒Dᆟ: nt?T> ·%ָjs[[(B]et7:#C λ1fqO/-dj}a)=/Eo ;|Q;9Om:j'?]8I{D+Er~C:, ڊX<(x*iP}z QkǝY,] \]O _p.8;-_'Ojs/7zÇᘉVjQ\$@/J#YVyd` #%@=[ÖP=gm# )h*`0Vl~zdChyōiyH†kN N{zUF?6,ÊqJhvuh @X5S4+(%MWC:\5A3ͱ-g|db57*m~ǯX"]Cr~5>d[}!.a&0' ^L ]%T'{ebW}¬teM^aLe%'vz=fъC>TK<S"4/:hP.ٚ]-A ݳ~3.hqQ#sgzpVB" ?بo%瓈i3Z$5@x _D3ktФDuLe12?2]7 [b=%S]Μ7Z'GY,nm;>`$xt$;J$!Ɏ> |9l`V4w- PIQ@N&3Al?~OP?(*=YFNtITRu ރ\҆>y ]'nAFZtfox7vtynxJ M4󄦿&/>1 Tg]aU'#\slgdf,!7`? ?dEF~qu>tcP=mqd?J]^@Œ~zJϟ4UzDz%OMhS=۲)\?: vXVY/p3T fs5HnJ|>w ? $n&SVT_#ڦ{؉;-/sӥF͡kWoE*0d@!fmRmyXd xfM} =(H}KqDF`@k-p ` 9FRL[o`9JDsRrWZ6k|PmIG5{q:p-F<ܶ6 7`эB*1o5, 4?h>PNmP/7"MZU:!'x5g@'I^ ϟ<{/ŀ 5y` V$ 1CPv֔҆_@\jlh!Q ~n+[R.Si7aBڸCFX=V|=p!O=l*l}#,5g5Ƌ"j ]w)t,gcWD\< ߔCF`@҇XB@s 2`Q Pmv?% p6=׆ lud[e4y @tk'S# Pɬzq-dd#h3Q[Scĉ&w7G!)sd aaEP2JVк:?}`!i ȰuCE+L̊L2$lBo}_ H z}-ߥwg KAիmuO}8%Vl+=v4̢)QFE JowLrGWgX7~Gm*DBV!ӷ  Ѯ C\;+%yCbHKAz7\neE]ˆAl}ӟ9qr6;(h6Vj#z5VnH2Š$a@DCKe&aOObceր,@sT(_w=dn  PΖ7CL8/Mpyju$H T4?KH"P%,-'o _۲y䃏%hIY8;;Yo<~vd. ThQ=+7j[ѐ%j=sTeB~AjB'k=ɩF"ޛ7IOG4lY"zu)"+V={9YD}$y> j5J-XLkx}~ [ܠfqX{vӆE GY2;T\[[9#c3f/pM$ IG=2*Dޟ_w31Іblv#fS+ wX?$@co9! flprsPx!t3i`8fnkM"S&Nx-au;w͒jK%SX <^ȋ7BΆ@B]`_?ҩ> :k*;‹X%,AU}x{ $Fq٩nr>zmmiԩ`C+q; 4o4JM΁ኰdfxݐ meAJiiY>*X$_B.mAN)1%B]4}/8T%DJ;ܾeK2?bu@JԞ4 8,d \B'5hzWƆ7R~|?u7F_*vѶ> hR])ʴ`qJ9Y9o$yHMIrPDc |c(ZSbO5^OPÁ$I]q\$[159 < %Rnox}#avH>΄~Ej/"ZIa׬/0Ӟ͒pNp@7^wK-EV?q9'jG:]-4:9 -XHs-u%xό lK6)a>8^aQǰ @&#~w!Ѫ-14@$[31T̵O"ikB7ˏu/? {nN'BQcڻ5JY=*(Krx%@^wvCJ;6zVe.~i;~o| :|ۜkXhYGO`=Gct1 ,UrI"&AA&f|RۄVDbr՝s]WK~x=0[t%w}(Љxm^7] 2Qء>$.=. [ PZ1EGP60N m?E&jNjtBm/Lu pW韷  O;z_ΫՃ's{p vuϖ5 ٲ 2 iχE2%yޮ/.Xedm-~*T/Q weuzLHnn r 7E:n72x.{H¥T{G~Z}NWtn<|U<ߙ\Ӫ*D|D%+d7(!' s&^7}CjTIJ.*[O LK<ذ*~qS[,_r1Qn@l~μҞ6 , }? L=M~˸)-z)o*+޸`IeԸ߼lneGGIBH$_j?V-.?*oYj1RO{wjI7E1Q/x՞*5 NM Mŵ1o_Pڒlgztw 9K^,xͺ%yl M ^o/>2H隁V|ߒoJe " 2X{cJ>gM8Ipk}(O<+,uiy ?pG^F4w1l~getm-K XM{d$|&^"H=&,dOaf~ZpMF2E[.2-BFʿ̊NϺk$3]L2LlPӇ@+⽷ >>,}#.k&-G!) Iq?N Mt(b[M],Cq3- [dKN>g8/Q٢iAtۀ2rt';q֬SQGCz!q XY 9gҟ9P0`8 Wj x9oAq"fz ,;;4_ jGËyҪW*>C|;-lbtgM s7xl'OuǣhgzRˋz.2G>de[ :=#0T)Q5;y*{QWDz 0lu(4r~~AW"Ή *tkr/=U>&tX,F ^' >̉jxBBNŕ[,?"z/\-o&è3BJƴbgb넦][Iawľ8{9dڲ$Ի8s;%ȵ͞;[{*)Hk BQBZҨn;v%{Ky՞DllDI\d?"aSzaRxx?;0+]dh|wbjFx{XMDFDynzoޣ[w oµGG WzxЬ#).}/E䘞iά?']b9¬߇{ Y澂8j3^/%C aRRcZc6vԱW}/.ů<9 7{ |t5bt) *a}t 2 p zRNTh GéպRmDL͒iT8e-5p=6 5b k֨#M 'bU|[q_?F^S7‰Apݫ}7> _)V3^M}iD 0eȗsjss۩ -V߈٤.3D'~0GsTC>&h~g𮴨 [ pN>2whYm|#N`}$Y߀/2Ylɍdn_*iI6qBA+ o{kTHs0QӖQL~ =|#7(g<4wAз`'}OδJدOp{rMEZ%S>/όz͎ܙȀiH d.qt7.|~/?4NWʭ]_z`6!gO +sA˜5Bߢf sDck]: Щ;PtFZO~\ `nբBSunj2}Ň)=D0e^qp[ u --Zg1*I?>=;[%ȶ@/ێ,8^8`9`a\U]h>rn#f/&n#D-s]ҙ}=uJk֠v } QLp"uJñlxe'^%tAfyݐtdķ-uj|J/57 3+@>W>߫w \sZݢٱ^i`Sïi)%(jzEh)^Kzs6;k9-<>ʨ|8y[uk$AA蛒ԫd"B KBizG&QW/ƫmڦͿW+L'xvb2q̓U_)>蜹׫ھsIg36 Uo,Zۍ{z&xȏY']'ca'⺝Ъ//5!H)*Ŵ3HA%c{P4 n?Y l2MK.uqwAfaON)2u_v)O5n?:+].: e7jY18B0lVmsƬTj[{=WMPz8-%bORpyK IM=βhs&2h7h~OSDz4p +gG T4zJX*o|__ h{ϓγ-qTsia(G( Lԧ290dݾmV̳&- %(IUV^sp$ qm|ߒ~'P1A|5E>J73D*dޑ%g@:N}" DbbU,#qN@6%LpvFDfeo+}k*BOzuW,T%ͮ-BN{Gf~Ó^5?F]rڭ!KZ>.V~w wI2h ] LPI7ʠ-ǧ"#BCb푽 j p)]q ߡpIJڿdzfo1 TQ]IMEv4StpǏ=ɤzTt;47ө*&U#j#'_1jV[^m} hoNVLV-% 4w|/N958pzm$/ xy=7OѦc|g*a9bTֺقޗI?mWC"[&d3z[XWzMv8z%C ])'(!SD%ϑY ?lgCG7)^4@h bK 3;t^J+AS4.NY؀iw\XjƫހT*nJ:܁.g$"N4@z`wpKhѿ0G4XZ5Yk1ZJ]is9"tYA|-a9*^3zx/|3MXpy̎{Hs?م'54"J E*@riOScɟwG/)|#2ڍ._UQGN5z>Kft>tW&#>TNX3KmXЛixO~{ xvaCmt~$  bg=r66_OrZ/`hAM 'ʠkJ *%Mg۟G|Idv%(SU%ʹJ?XDhO+s8QEBPˠ:h}eǯ;X׹uʏ +.Κj%n_RD~2K[*5iVz*R`1F#LǤj']I&s|^Xlx SsMPG -M"jf®TEsLrZ?U p5%Cf RR;F()/Tj9ȿmJapY &̀O`MxO ,j!AZo` FQnm1p.}$cIݲEyzQ1H#t䪆LUvggfs7VJ ן3j/":'4 Pw|7G|1۰GϸV{4S2Aǣ50ӿϙ0-+%` ԵAW7MJ2)V?jO^;:@O.ɩ1ZW KZt!|CS^2M <̂?S1 Xr>}lw|t[+mn8֌`]D\qY"9ڊ}tQ~LEcz=! wo?z" h_8 ̢CQJ]lL`f!=L8R6o*^#\gfu2= 9:N4^eT 3].7+"GVsuuww,`glK6؟oV;ߵiHڦM>.K^2/~]k4D)PP( h9FSi~;GQkw-B7֓EB\8q'g"cm0}BP>sI:ag^BjU"z5vêU}eY87uf|4Ѹ |秉a,>Z"b*NbܵmD^=]3A|nV<-秓^rϕSycϚ\~x[`!S-,̗Ӻ /v6-K]ʹS]IE-%Fg${>WUWYIP.NDQ$6C\P}]=eLFI(cɻ+m8LAwX_mfQf΀=)9eڧl5)o".;K>T2`pi;7ކ褁Xj/=+eRI C r-tiX(!/㾡@G~LnE[h(kʭe6d34^H*O/H{Vrs^_0l)3^GO#;o1J K.@Zj\xr!r!r!r!r!r!r!r!r!r!?^+hs7,KCT ;W-G]\Wk,EJ4hi [VCkQ?? ǿ~yبF6Q:lauبF5J묡CC9C9C9C9C9C9CbnmR:>Zǝp u(EڄS/ > {&˖>fD~ga/vBb~',T6l]K:ʞa H4h: d=4$*CQ<Y1D"^ş@)V_O;NCY [%DVIN\+ _~P:=^A3.SZ/8=C@6р2y-ڱv7Eܿv D#,55}nDdOXm&b?.cu=B gWjKv+yϺSk\RKg{wրM練% %ĩxQKui?A˩i_Uq#KΩ~VN k?ʼn*Si!츾W!((sTf8LǛX^>z)j]QMGA^^Q X 4&DD@jDFt齃#-DzL~37̬]yY~~/ާ2A\0ߡN a]bܽb>3Ԝ>/Eגm( XxN87&(՘ς/#8Qu-Ib<8Yff~rR/cA}!saVG?w$kvs6㦪[Žbqx!1v"I5E*Ivm r|S@Z\vǡ7Y]QS=^՟ _~9wK~Ys]OjW'nb $C稙HTEphxOF8ȃs `kյfDPTͯ}^דBJF-$`nQ˸{Zʝ|ᤤ^yh?7 e` ހWe;,7'6F?jۿCrk Og5XG`_{~iKo $z3|Y>\y3v[\P%@ⓑ WB#xEUY4,3|0{RZ;8$/W"|OPXcn\D~:.j+T{ɏ&;0`9it;韷CG_ntp$R]\2>ck3P˲c ʤlԖS6/eb^lR04/߄52)22j < gWI8; G+4!MdWx@pӶ؈~Fʦ*]L#8~ve㲅nߊgPjۤPx.6Q)[Nʍ Eu8ˎs[5W[rnbi/6s -;\D#Q BN16Pr$ܤSu:/7(.|_"Jk]S(G>GZiimN& žrtX7~eµpg@:ZO4X>^XKǠ+]D6,ЋEnּ3O^-{udkSy;z_uҎV9J򝨈f $Su[Z^IO .3IAgm .\I_M=$U&ySZx6뙳]YA]]~_}-;E> cYh5JoK'ʆAM*yCxvŖI>.x4{-j3us *W@/On #߁[j7~_B@L`n%eNZP\SO<uI `x7 ԧ xj#4)9.l2d~bbu7 a; S#=CNz߃u|'8tDhD %gQ+lx᧥tlgPiI%{ =ǏrT5^iB_Foz)ȵ?_ng375V-بZw tN\TWߦWnz .&go ~  s#/P,=K~dM:F>ߍBg2HkAus~_/N\]^1֐{g&[$@9)7e#KSc;ًpڪ ~s^lcK)Y?& cE8#,*8my}R#߫>a5u)Yi4;X+mC0[ K;31K~f'}ʣY,/ <(w7ێe gE系)O-?|FC])#f˯֬F RZM|qstgvE|7&y~ʸJ46:ӻg6E]:'hg}peU2дc^#avND&.D>D- Oq3 B= hsP5uܘ;J˜^$ JNՐBtGͮۿp╸ګ{TSm:OU,Ny4$[aMq{mͼ*0xWm{,]@)}H% !$@PAi)@5<_ᅏ&v4.O U@z#HG##k/fc$@UXjHPuM^ r'/Vx$ #pۻD ̪<9^ϰޖKў62"߅ drÀr{fO8i.fFW|)=QwOfeX酘IkS g˖ADJ4D.m*:F}.X|^jʣG;hN$BIț*pO9U;.AؒqyYNm]ta}:* P‰-Yp򯆘hR FGwvOAX7篒ȂMCEFޟ4SqzT˥čH@|DU9ǯV=/)5.TXZ-۬RHŕ' Is15]n@sё#B4_Q'%NgwVZddnpGݺ;ȁkopHWrApRͨ78Y%+XqǬ'MK皆O~z w'|_ Oeg@<]L*$)8lɴt4wrixl%pYluF%f:%kj./S^ BGi@@m|l% Ԓ(wMV_uhnA7Ɋ^4}`!K\UrEn1{#H;x̹a,H}F%`>ɷG*Z(ȮDnb/F;IIF\p1rX>oל 'j0f;dVu+Q L1P <~$CBIQ&ԗV^[^)[O5G:XGlc+%>FDƱ&ҭ^/jVt{42tX>uһôa8K@ryOrq~I _ 7ݑʁ}n/n[O!-rY h"҉bIٓ'H]8 )K^Ra7f^2|-T\?/ ԅ4A7{ LY^>/{M/Cr/V3o^@F,Ul)dO~N )Uvg\jM^G}cҥQ.Ux }1$`ݤ/y\˻M?>.% 0םLA= 3^\%s㴧r7 `R^0&.:0 vwOedvbn1b?+e&5L}@/Y6; T^w0\eaZ#E*(v/;xCF07mC'j3vwEBc;ͱsp(0'^N+ّ& d8xls8&1"GY/5C}FJ+CK^X"Ll 9hM3H,R:TAxV Αd/ӤqG~1TMWK ֺon5e0_gEuo'4@(dcsvA:{W<QmS{כ$H}.CƲwns Fm%4DW-OGHE]so!C',Z?kJiNq+TΚ!wD>A}mt5[ׇu[DEpBeVG]RMz~o߳zibeV;]mcM?sM^fiZd6Z&@$"#u>JMv'>Aݒ;lVfj0{hG{&~ҤتokKPku#;8K%YQ^TBdB(LBޒ%*'К%yפNūxBOK3a܁>3%YW2 7*>m;s:SjiOF**&TPZ4_kxɌ%aoٹEuw?o#$ʸL3y!ww$%ӇWƉοM5DhE< 糼:zgwn6jXM"SscSl:,s-!;ϾHR,a/7m(7c,E݄yS}Z߈rn?I,e: g;aҎ P[s9B'~Zfz98yosCf$ f}.IABAt.[$n;L Z*V 4rNma$o vcʡ+uOJTzkBThvbބFPu-{*iq-ù2N8%~@"2weEU'1(MS e>a;VUJa[UG}rIǦ`O9u![.6+n :HwHu0a˰SY u"G}>mc;CλED*8 NbL`]WaV{a.D"GT'墸P QPcaqyͩ?^ fbW:x:Q`ib=N.1#lYjj|>o(&Ϯ#hӵJ50H거j?t+kCn?n0f(yy(Ȝ .v(Zja31|=ILT]E!(!"ώw+.c,>Ҡ+ 2=itF|;(M+:[$_?Vlo#|S1.{ڇ^Lti-яѲ z٨u%:.JDdZ(ݟ ^u* y{\  ۱ tt3Mj&150|;$/]acZN'm=7w#/ſo4?PKѥU6W(titlepage.xhtmluR]o0|_Z'I*KHU9`NQ{mHמ7{riZ'\ټ--zϯa*0ŠK1?ǭ+El0@i=n~ebeC!Dwoe=:BȗJ!"n!կ=bV70׵yqе ״{ L,CMA3Ja8Ss8E/JcSw~iAЏҦ{p/bL.KYzLa[Y?䝔Rx.t[B<^ xqiDhCnAȚ6e+9YpXme u/Ocl wnÉJ7u7㿛1?ꐬV%k+ Vͻ!\_fePKѥU7 ''3page_styles.csss(HLOURPM,JM/)ϵR0-(F@jPKѥUaId "+snmpttconvertmib_split_004.htmlZmo8_AxS8),iM@cbhx(U7CR$$B 噙C7G!TX'WU„\ܶ8謹4`JTplV{{wk*4F}#(4b-,*N+ċbB*FɈ 2S?}"zo*nYd!9 䚍ڙ `/?yRmww& GgFӫ_1ǔhȅ~:?(*Bv:]^QJ!dX\v?4] :a2>mUΌ^h_++JZ:ffIYتH S@}ՓBM+<޲x:/pѐSVA^{xR!/I7o+K5BUdC_dM1FloC,™ܛjgN,a8v./8G2MAu xfk:jK`DvOeLR(8,b)ܗd 9 @UmI bk%7Ny *SLMz3A1RpDhHL;8y)c  la!V &mF&Zt}9 ǰtn# ڕs {"IG.ZM DQMg)3c1'ɔ:8cK39[M!Ǒ$Ȋ*#bY=T˔GQ2-;OhZfl$pP)\q6Գ]q39sq=?* ˔g1:mGo`;u[؝+$R'B˔@AP$Liu_/B.tq *wp|=|\i%^3o]/&i+'5%PKjLɀezPo䕄]BT\j?qĒO aǃ SԱA6jX^as7<1gx\TLfjX^I*mu#ȃZ=8A×{bh FƙZru$hmtXWslr`OgM}Ȱ%`P9寕{ NFfNAr?#DAvMբl,!Ꙧ;{ş+m*6:mgjܣW9%!5tn 4M@6uR$<pwCJa@殼_ o|\۬:hI2(YZӸ>O'r5e7Q26aWm2.î3%z$!!mIu_(ol[f#rZ@52,v^hO(ڻׂωkQpQ9 XoW 2⦇ϑ}αV?F5߮ڮY?kqz'nfGr.$8? //ۃa No4\brt{3̱m0JaRx}]#ѧ_ g ;7;b1hRț/%4rT,nJS%9jq5Z)N`\f?0FdlmzX/4h&ZA[j۱666Wa|Ƿ~s4\,J00SSX}X~E9f X엳m W#"]Coa,L{ٜ^28X !K\̐pD+(7)|C~* 1e:x _!: o@ '1 o>)+}pqwps6 [-.^k[sGʪΒ_ܺ?4֘D=YYg*AGaD!OvB B)Qԏ 矛|-ڐq^3t+)hd|WOk; J+AXKvdEtYlJ1B"ԖUF怲T_MN- ,m<><*oGb>whEyL4k9 2z.rlUN_LsnI4ۂhq F`z#)95޶ KgW!b⎪.ߺ|I$KHnhg|’>̤p9rQSP̮-k!o}ݎ0%kJטh"vLBk |^nVuX2&ҾJS PKѥUf0Fsnmpttconvertmib_split_001.htmlTmo8_1ʗJhe/EE V$b]~CKQos/_"ۙyfg`Js)ۺ/ 4^~\RCfd*tI~;TϿ\Rd=q`[;ʁH p:%$R]W -/vܖC p[ 1PP~*YƼ+W|z"{ " ghtrz?A8n2,g9#tf r6gw}gx̫9b`3ʼT|hZfnY{kڦe{I=$AWq: _g\CF-1QebLaJY@qsm_`"H[MfQI T `^3+e(Ba]mAlLNR,fhӐ¾hc3d2PHhfHeNLӴڙY&#q !=SXK /ηy)AkJ*t:Z %O3wp6Z.q̃E8&q9rhiR)Ӓ䦩,d!QMvvrT{4`8 Hjր\@y"RٲNRk9 Zm~` Yٝ3UqpPKѥUYOsnmpttconvertmib_split_000.htmlRn@}+FB"vDj+>DZjvC)B/ٙ3-|ܮ ؠ6Bɨx&LU&2jiѺk>ƍ0' Pi"_UWxJ/[aq ̑gΰ +%jNJ3H$`4_wo= uA׻?<1 Z/kʭ#Cow 8J| /c$_kwpO&/ *04}D=A(ik'}x 7~_MZɮijnw}4 EeBo]&G$d91 rs编KvseWػHy!;07+6`x]+XАVryaUBqȏ]ݬ-S˚-ԮE5$pw^Y׿`s}9'0B 88aJLRRsБ;An;n)VڝO PKѥUoa,mimetypePKѥU :META-INF/PKѥUtcMETA-INF/container.xmlPKѥU 1content.opfPKѥUB toc.ncxPKѥU)8+ snmpttconvertmib_split_002.htmlPKѥU9-J cover_image.jpgPKѥU6W(}titlepage.xhtmlPKѥU SmLGstylesheet.cssPKѥU7 ''3"page_styles.cssPKѥUaId "+vsnmpttconvertmib_split_004.htmlPKѥUc15Isnmpttconvertmib_split_003.htmlPKѥUf0Fsnmpttconvertmib_split_001.htmlPKѥUYOsnmpttconvertmib_split_000.htmlPKsnmptt_1.5/docs/snmpttconvertmib.html0000664000000000000000000003120014277306025016724 0ustar rootroot

SNMP Trap Translator Convert MIB

SNMP Trap Translator Convert MIB v1.5

(SNMPTTCONVERTMIB)
This file was last updated on: October 2nd, 2021

License

Copyright 2002-2022 Alex Burger
alex_b@users.sourceforge.net
4/3/2002

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

SNMPTTCONVERTMIB

SNMPTTCONVERTMIB is a Perl script which will read a MIB file and convert the TRAP-TYPE (v1) or NOTIFICATION-TYPE (v2) definitions into a configuration file readable by SNMPTT.

For example, if the file CPQHOST.mib (v1) contained:

CPQHOST-MIB DEFINITIONS ::= BEGIN

    IMPORTS  
        enterprises             FROM RFC1155-SMI  
.  
.  
. (lines removed)  
.  
.  
    cpqHo2NicSwitchoverOccurred2 TRAP-TYPE  
        ENTERPRISE compaq  
        VARIABLES { sysName, cpqHoTrapFlags, cpqHoIfPhysMapSlot,  
                    cpqHoIfPhysMapPort, cpqHoIfPhysMapSlot,  
                    cpqHoIfPhysMapPort }  
        DESCRIPTION  
            "This trap will be sent any time the configured redundant NIC  
            becomes the active NIC."

             --#TYPE "Status Trap"  
             --#SUMMARY "NIC switchover to slot %s, port %s from slot %s, port %s."  
             --#ARGUMENTS {2, 3, 4, 5}  
             --#SEVERITY MAJOR  
             --#TIMEINDEX 99  
        ::= 11010

Executing snmpttconvertmib CPQHOST.mib snmptt.conf would APPEND to the end of the snmptt.conf file (specified on the command line):

#  
#  
#  
EVENT cpqHo2NicSwitchoverOccurred2 .1.3.6.1.4.1.232.0.11010 "Status Events" Normal  
FORMAT Status Trap: NIC switchover to slot $3, port $4 from slot $5, port $6.  
#EXEC qpage -f TRAP notifygroup1 "Status Trap: NIC switchover to slot $3, port $4 from slot $5, port $6."  
SDESC  
This trap will be sent any time the configured redundant NIC  
becomes the active NIC.  
EDESC

Notes:

  • To specifiy an EXEC statement, use the --exec= command line option.
  • To prevent the --#TYPE text from being prepended to the --#SUMMARY line, change $prepend_type to 0 in the SNMPTTCONVERTMIB script.
  • See the help screen for more options (snmpttconvertmib --h).

Requirements

Snmpttconvertmib converts a MIB file using the snmptranslate utility.

If the Net-SNMP Perl module is enabled using --net_snmp_perl on the command line, it can provide more detailed variable descriptions in the DESC sestion if available such as:

  • variable syntax
  • variable description
  • variable enums

For example:

2: globalStatus  
   Syntax="INTEGER"  
      2: ok  
      4: failure  
   Descr="Current status of the entire library system"

Converting a MIB file

See the snmpttconvertmib help screen for all possible command line options (snmpttconvertmib -h) before converting a MIB file. Depending on what type of information is available in the MIB file, you may want to change how the FORMAT / EXEC lines are generated.

Before trying to convert MIB file, you should ensure that the MIB file can be parsed by Net-SNMP

  1. Copy the MIB file to the Net-SNMP mibs folder
  2. Type: export MIBS=ALL to ensure all the mibs will be read in by snmptranslate
  3. Make sure the MIB file can be interpreted by snmptranslate correctly. Simply typing snmptranslate should tell you if it was able to read the mib file correctly. If it can't, an error will be produced at the top of the help screen.
  4. Try to translate a TRAP-TYPE or NOTIFICATION-TYPE entry contained inside the MIB file. For example, if the MIB file contains the Notification definition of 'rptrHealth NOTIFICATION-TYPE', then type: snmptranslate rptrHealth -IR -Td. If you get 'Unknown object identifier: xxx' then the MIB file was not found or parsed correctly.

Running snmpttconvertmib:

  1. Make sure the MIB file has been successfully installed (see above)
  2. Edit the options between OPTIONS START and OPTIONS END in snmpttconvertmib if needed
  3. If you are using UCD-SNMP, or Net-SNMP v5.0.1, then add the folllowing to your snmp.conf file: printNumericOids 1 (note: this will affect all snmp commands). This ensures the OIDs are returned in numerical format. Other versions of Net-SNMP do not require this change, as snmpttconvertmib will use a command line switch to force it on when calling snmptranslate.
  4. Convert the mib file with: snmpttconvertmib --in=path-to-mib --out=output-file-name. Note: the output-file-name is appended to, so remember to delete it first if needed.

Examples:

Convert a MIB file without defining an EXEC command:

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq

If the Net-SNMP Perl module is installed and you want more descriptive variable descriptions, add --net_snmp_perl to the command line:

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --net_snmp_perl

Append an EXEC command. The generated FORMAT line from the MIB will be appended to the end of the line surrounded by quotes. You can disable the quotes by modifying the setting at the top of the snmpttconvertmib script.

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec 'qpage -f TRAP notifygroup1'

Result: EXEC qpage -f TRAP notifygroup1 "A linkUp trap signifies that the SNMP entity, acting in an $*"

Same as above, but read the EXEC line(s) from the file exec-commands.txt. Multiple commands can be added to the file to generate multiple EXEC lines per trap.

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec_file exec-commands.txt

Append an EXEC command but don't add the generated FORMAT line. The variable substitution $Fz is used in the --exec line which will cause SNMPTT to replace it with the generated FORMAT line when a trap arrives.

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec_mode=1 --exec '/usr/bin/myscript { $Fz }'

Same as above but instead of SNMPTT having to replace $Fz with the FORMAT line, snmpttconvertmib will do the substitution:

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec_mode=2 --exec '/usr/bin/myscript { $Fz }'

Append a PREEXEC command.

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --preexec '/usr/local/bin/snmpget -v 1 -Ovq -c public $aA ifDescr.$1'

Result: PREEXEC /usr/local/bin/snmpget -v 1 -Ovq -c public $aA ifDescr.$1

Same as above, but read the PREEXEC line(s) from the file preexec-commands.txt. Multiple commands can be added to the file to generate multiple PREEXEC lines per trap.

snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --preexec_file preexec-commands.txt

To convert all the CPQ* files in the current folder, you can use:

Unix / Linux:

for i in CPQ*  
  do  
  /usr/local/sbin/snmpttconvertmib --in=$i --out=snmptt.conf.compaq  
done

Windows:

for %i in (CPQ*) do perl snmpttconvertmib --in=%i --out=snmptt.conf.compaq

How it works

Some MIB files contain --#SUMMARY and --#ARGUMENTS lines which are used by Novell's Network Management system. These MIB files convert very well to SNMPTT as they contain detailed information that can be used on the FORMAT and EXEC lines. Compaq's MIBs usually have these lines.

Other MIBS contain only a DESCRIPTION section where the first line contains the FORMAT string. In some MIBS, this line also contains variables similar to the --#SUMMARY lines.

The mib file is searched for the name of the MIB file. This should be at the top of the file and contain 'name DEFINITIONS ::=BEGIN'. This name will be used when looking up the TRAP / NOTIFICATION to ensure the correct MIB file is accessed.

The mib file is also searched for lines containing TRAP-TYPE or NOTIFICATION-TYPE. If it finds one that appears to be a valid trap definition, it reads in the following lines until a ::= is found while looking for the DESCRIPTION section. It then looks for the --#SUMMARY and --#ARGUMENTS line if enabled.

SNMPTRANSLATE is used with the following syntax to find the OID of the trap:

snmptranslate -IR -Ts mib-name::trapname -m mib-filename

Notes:

  • If Net-SNMP 5.0.2 or newer is detected, the command line also includes the -On switch. See the FAQ.
  • If --#SUMMARY and --#ARGUMENTS are found, the %letter variables are replaced with $number variables based on the values lists in the --#ARGUMENTS section incremented by 1 (ARGUMENTS starts with 0, SNMPTT starts with 1). This will be used to define the FORMAT and EXEC lines.
  • If there is no --#SUMMARY and --#ARGUMENTS lines, but the first line of the DESCRIPTION contains %letter variables, then that line will be used to define the FORMAT and EXEC lines. The %letter variables are replaced with $number variables starting at 1 and going up.
  • If there is no --#SUMMARY and --#ARGUMENTS lines, and the first line of the DESCRIPTION does not contain %letter variables, then that line will be sed to define the FORMAT and EXEC lines followed by a $* which will dump all received variables.
  • If the entry contains variables, the variables are listed in the DESC section. If --net_snmp_perl is specified, the syntax, description and enums for each variable is used.
  • Note: This can be changed by specifying a --format=n command line option. See the snmpttconvertmib help screen for all possible command line options (snmpttconvertmib --h).
snmptt_1.5/docs/snmpttconvertmib.md0000664000000000000000000002622014277306025016366 0ustar rootroot SNMP Trap Translator Convert MIB #SNMP Trap Translator Convert MIB v1.5 **(**[**SNMPTTCONVERTMIB**](http://www.snmptt.org)**)** This file was last updated on: October 2nd, 2021 #License Copyright 2002-2022 Alex Burger alex\_b@users.sourceforge.net 4/3/2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #SNMPTTCONVERTMIB **SNMPTTCONVERTMIB** is a Perl script which will read a MIB file and convert the **TRAP-TYPE** (v1) or **NOTIFICATION-TYPE** (v2) definitions into a configuration file readable by **SNMPTT**. For example, if the file **CPQHOST.mib** (v1) contained: CPQHOST-MIB DEFINITIONS ::= BEGIN IMPORTS enterprises FROM RFC1155-SMI . . . (lines removed) . . cpqHo2NicSwitchoverOccurred2 TRAP-TYPE ENTERPRISE compaq VARIABLES { sysName, cpqHoTrapFlags, cpqHoIfPhysMapSlot, cpqHoIfPhysMapPort, cpqHoIfPhysMapSlot, cpqHoIfPhysMapPort } DESCRIPTION "This trap will be sent any time the configured redundant NIC becomes the active NIC." --#TYPE "Status Trap" --#SUMMARY "NIC switchover to slot %s, port %s from slot %s, port %s." --#ARGUMENTS {2, 3, 4, 5} --#SEVERITY MAJOR --#TIMEINDEX 99 ::= 11010 Executing **snmpttconvertmib CPQHOST.mib snmptt.conf** would APPEND to the end of the **snmptt.conf** file (specified on the command line): # # # EVENT cpqHo2NicSwitchoverOccurred2 .1.3.6.1.4.1.232.0.11010 "Status Events" Normal FORMAT Status Trap: NIC switchover to slot $3, port $4 from slot $5, port $6. #EXEC qpage -f TRAP notifygroup1 "Status Trap: NIC switchover to slot $3, port $4 from slot $5, port $6." SDESC This trap will be sent any time the configured redundant NIC becomes the active NIC. EDESC Notes: * To specifiy an EXEC statement, use the \--exec= command line option. * To prevent the --#TYPE text from being prepended to the --#SUMMARY line, change **$prepend\_type** to **0** in the **SNMPTTCONVERTMIB** script. * See the help screen for more options (snmpttconvertmib --h). ## Requirements * Net-SNMP [**snmptranslate**](http://www.net-snmp.org/man/snmptranslate.html) utility * **Optional**: Net-SNMP **[Perl module](http://www.net-snmp.org/FAQ.html#Where_can_I_get_the_perl_SNMP_package_)** Snmpttconvertmib converts a MIB file using the snmptranslate utility. If the Net-SNMP Perl module is enabled using **\--net\_snmp\_perl** on the command line, it can provide more detailed variable descriptions in the DESC sestion if available such as: * variable syntax * variable description * variable enums For example: 2: globalStatus Syntax="INTEGER" 2: ok 4: failure Descr="Current status of the entire library system" ## Converting a MIB file See the snmpttconvertmib help screen for all possible command line options (snmpttconvertmib -h) before converting a MIB file. Depending on what type of information is available in the MIB file, you may want to change how the FORMAT / EXEC lines are generated. Before trying to convert MIB file, you should ensure that the MIB file can be parsed by Net-SNMP 1. Copy the MIB file to the Net-SNMP mibs folder 2. Type: **export MIBS=ALL** to ensure all the mibs will be read in by **snmptranslate** 3. Make sure the MIB file can be interpreted by **snmptranslate** correctly. Simply typing **snmptranslate** should tell you if it was able to read the mib file correctly. If it can't, an error will be produced at the top of the help screen. 4. Try to translate a TRAP-TYPE or NOTIFICATION-TYPE entry contained inside the MIB file. For example, if the MIB file contains the Notification definition of 'rptrHealth NOTIFICATION-TYPE', then type: snmptranslate rptrHealth -IR -Td. If you get 'Unknown object identifier: xxx' then the MIB file was not found or parsed correctly. Running snmpttconvertmib: 1. Make sure the MIB file has been successfully installed (see above) 2. Edit the options between OPTIONS START and OPTIONS END in snmpttconvertmib if needed 3. If you are using **UCD-SNMP**, or **Net-SNMP v5.0.1**, then add the folllowing to your snmp.conf file: **printNumericOids 1** (note: this will affect all snmp commands). This ensures the OIDs are returned in numerical format. Other versions of Net-SNMP do not require this change, as **snmpttconvertmib** will use a command line switch to force it on when calling **snmptranslate**. 4. Convert the mib file with: **snmpttconvertmib --in=_path-to-mib_ --out=_output-file-name_**. Note: the **output-file-name** is appended to, so remember to delete it first if needed. Examples: Convert a MIB file without defining an EXEC command: snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq If the Net-SNMP Perl module is installed and you want more descriptive variable descriptions, add --net_snmp_perl to the command line: snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --net_snmp_perl Append an EXEC command. The generated FORMAT line from the MIB will be appended to the end of the line surrounded by quotes. You can disable the quotes by modifying the setting at the top of the snmpttconvertmib script. snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec 'qpage -f TRAP notifygroup1' Result: EXEC qpage -f TRAP notifygroup1 "A linkUp trap signifies that the SNMP entity, acting in an $*" Same as above, but read the EXEC line(s) from the file exec-commands.txt. Multiple commands can be added to the file to generate multiple EXEC lines per trap. snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec_file exec-commands.txt Append an EXEC command but don't add the generated FORMAT line. The variable substitution **$Fz** is used in the --exec line which will cause SNMPTT to replace it with the generated FORMAT line when a trap arrives. snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec_mode=1 --exec '/usr/bin/myscript { $Fz }' Same as above but instead of SNMPTT having to replace **$Fz** with the FORMAT line, snmpttconvertmib will do the substitution: snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --exec_mode=2 --exec '/usr/bin/myscript { $Fz }' Append a PREEXEC command. snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --preexec '/usr/local/bin/snmpget -v 1 -Ovq -c public $aA ifDescr.$1' Result: PREEXEC /usr/local/bin/snmpget -v 1 -Ovq -c public $aA ifDescr.$1 Same as above, but read the PREEXEC line(s) from the file preexec-commands.txt. Multiple commands can be added to the file to generate multiple PREEXEC lines per trap. snmpttconvertmib --in=/usr/share/snmp/mibs/CPQHOST.mib --out=/etc/snmp/snmptt.conf.compaq --preexec_file preexec-commands.txt To convert all the CPQ\* files in the current folder, you can use: Unix / Linux: for i in CPQ* do /usr/local/sbin/snmpttconvertmib --in=$i --out=snmptt.conf.compaq done Windows: for %i in (CPQ*) do perl snmpttconvertmib --in=%i --out=snmptt.conf.compaq ### How it works Some MIB files contain **\--#SUMMARY** and **\--#ARGUMENTS** lines which are used by Novell's Network Management system. These MIB files convert very well to **SNMPTT** as they contain detailed information that can be used on the **FORMAT** and **EXEC** lines. Compaq's MIBs usually have these lines. Other MIBS contain only a **DESCRIPTION** section where the first line contains the **FORMAT** string. In some MIBS, this line also contains variables similar to the **\--#SUMMARY** lines. The mib file is searched for the name of the MIB file. This should be at the top of the file and contain 'name DEFINITIONS ::=BEGIN'. This name will be used when looking up the TRAP / NOTIFICATION to ensure the correct MIB file is accessed. The mib file is also searched for lines containing **TRAP-TYPE** or NOTIFICATION-TYPE. If it finds one that appears to be a valid trap definition, it reads in the following lines until a ::= is found while looking for the **DESCRIPTION** section. It then looks for the **\--#SUMMARY** and **\--#ARGUMENTS** line if enabled. **SNMPTRANSLATE** is used with the following syntax to find the **OID** of the trap: snmptranslate -IR -Ts mib-name::trapname -m mib-filename Notes: * If Net-SNMP 5.0.2 or newer is detected, the command line also includes the -On switch. See the [FAQ](file:///h:/cvs/snmptt/readme.html#FAQ-Troubleshooting). * If **\--#SUMMARY** and **\--#ARGUMENTS** are found, the **%_letter_** variables are replaced with **$_number_** variables based on the values lists in the **\--#ARGUMENTS** section incremented by 1 (ARGUMENTS starts with 0, SNMPTT starts with 1). This will be used to define the **FORMAT** and **EXEC** lines. * If there is no **\--#SUMMARY** and **\--#ARGUMENTS** lines, but the first line of the **DESCRIPTION** contains **%_letter_** variables, then that line will be used to define the **FORMAT** and **EXEC** lines. The **%_letter_** variables are replaced with **$_number_** variables starting at 1 and going up. * If there is no **\--#SUMMARY** and **\--#ARGUMENTS** lines, and the first line of the **DESCRIPTION** does not contain **%_letter_** variables, then that line will be sed to define the **FORMAT** and **EXEC** lines followed by a **$\*** which will dump all received variables. * If the entry contains variables, the variables are listed in the DESC section. If \--net\_snmp\_perl is specified, the syntax, description and enums for each variable is used. * Note: This can be changed by specifying a --format=n command line option. See the snmpttconvertmib help screen for all possible command line options (snmpttconvertmib --h). snmptt_1.5/examples/0000775000000000000000000000000014277306167013321 5ustar rootrootsnmptt_1.5/examples/#sample-trap.generic.daemon0000664000000000000000000000027214277306025020403 0ustar rootroot1092665195 router01 192.168.1.1 .1.3.6.1.2.1.1.3.0 31:18:27:02.00 .1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.6.3.1.1.5.3 .1.3.6.1.2.1.2.2.1.1.3 3 .1.3.6.1.2.1.2.2.1.7.3 2 .1.3.6.1.2.1.2.2.1.8.3 3 snmptt_1.5/examples/sample-trap.generic0000664000000000000000000000025714277306025017101 0ustar rootrootrouter01 192.168.1.1 .1.3.6.1.2.1.1.3.0 31:18:27:02.00 .1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.6.3.1.1.5.3 .1.3.6.1.2.1.2.2.1.1.3 3 .1.3.6.1.2.1.2.2.1.7.3 2 .1.3.6.1.2.1.2.2.1.8.3 3 snmptt_1.5/examples/snmptt.conf.generic0000664000000000000000000000437714277306025017134 0ustar rootroot# # # EVENT coldStart .1.3.6.1.6.3.1.1.5.1 "Status Events" Normal FORMAT Device reinitialized (coldStart) #EXEC qpage -f TRAP notifygroup1 "Device reinitialized (coldStart)" SDESC A coldStart trap signifies that the SNMPv2 entity, acting in an agent role, is reinitializing itself and that its configuration may have been altered. EDESC # # # EVENT warmStart .1.3.6.1.6.3.1.1.5.2 "Status Events" Normal FORMAT Device reinitialized (warmStart) #EXEC qpage -f TRAP notifygroup1 "Device reinitialized (warmStart)" SDESC A warmStart trap signifies that the SNMPv2 entity, acting in an agent role, is reinitializing itself such that its configuration is unaltered. EDESC # # # EVENT linkDown .1.3.6.1.6.3.1.1.5.3 "Status Events" Normal FORMAT Link down on interface $1. Admin state: $2. Operational state: $3 #EXEC qpage -f TRAP notifygroup1 "Link down on interface $1. Admin state: $2. Operational state: $3" SDESC A linkDown trap signifies that the SNMP entity, acting in an agent role, has detected that the ifOperStatus object for one of its communication links is about to enter the down state from some other state (but not from the notPresent state). This other state is indicated by the included value of ifOperStatus. EDESC # # # EVENT linkUp .1.3.6.1.6.3.1.1.5.4 "Status Events" Normal FORMAT Link up on interface $1. Admin state: $2. Operational state: $3 #EXEC qpage -f TRAP notifygroup1 "Link up on interface $1. Admin state: $2. Operational state: $3" SDESC A linkUp trap signifies that the SNMP entity, acting in an agent role, has detected that the ifOperStatus object for one of its communication links left the down state and transitioned into some other state (but not into the notPresent state). This other state is indicated by the included value of ifOperStatus. EDESC # # # EVENT authenticationFailure .1.3.6.1.6.3.1.1.5.5 "Status Events" Normal FORMAT SNMP athentication failure #EXEC qpage -f TRAP notifygroup1 "SNMP authentication failure" SDESC An authenticationFailure trap signifies that the SNMPv2 entity, acting in an agent role, has received a protocol message that is not properly authenticated. While all implementations of the SNMPv2 must be capable of generating this trap, the snmpEnableAuthenTraps object indicates whether this trap will be generated. EDESC snmptt_1.5/sample-trap0000664000000000000000000000060014277306025013640 0ustar rootrootserver01.domain.com 192.168.1.1 .1.3.6.1.2.1.1.3.0 111:21:48:19.07 .1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.4.1.232.0.11003 .1.3.6.1.2.1.1.5.0 SERVER01 .1.3.6.1.4.1.232.11.2.11.1.0 0 .1.3.6.1.4.1.232.11.2.8.1.0 "Compaq Management Agents Test Trap sent - Friday, August 16, 2002 2:05:15 PM" .1.3.6.1.6.3.18.1.3.0 192.168.1.1 .1.3.6.1.6.3.18.1.4.0 public .1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.232 snmptt_1.5/sample-unknown-trap0000664000000000000000000000050114277306025015335 0ustar rootrootserver01.domain.com 192.168.1.1 .1.3.6.1.2.1.1.3.0 111:21:48:19.07 .1.3.6.1.6.3.1.1.4.1.0 .1.3.6.1.0.0.0.0.0 .1.3.6.1.2.1.1.5.0 SERVER01 .1.3.6.1.4.1.232.11.2.11.1.0 0 .1.3.6.1.4.1.232.11.2.8.1.0 "Sample Unknown Trap" .1.3.6.1.6.3.18.1.3.0 192.168.1.1 .1.3.6.1.6.3.18.1.4.0 public .1.3.6.1.6.3.1.1.4.3.0 .1.3.6.1.4.1.232 snmptt_1.5/snmptt0000775000000000000000000063030314277306025012754 0ustar rootroot#!/usr/bin/perl # # SNMPTT 1.5 # # Copyright 2002-2022 Alex Burger # alex_b@users.sourceforge.net # 4/11/2002 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################## # # http://www.sourceforge.net/projects/snmptt # # This script is a snmp trap handler for use with the NET-SNMP / UCD-SNMP # snmptrapd program. # # The script is called by defining a 'traphandle' in snmptrapd.conf. # For example: # # traphandle default /sbin/snmptt # # SNMPTRAPD feeds details about the trap to the launched program's standard # input in the following format (see snmptrapd.conf man page for a complete # descriptipon) # # HOSTNAME: The name of the host in question that sent the trap # IPADDRESS: The IP address of the host that sent the trap # VARBINDS: A list of variable bindings that describe the trap and # the variables enclosed in it. # # See the SNMPTT documentation (readme.html) for more information. # ############################################################################## # use strict; use warnings; my $snmptt_version = "1.5"; sub showversion { print "\nSNMPTT $snmptt_version\n"; print "(c) 2002-2021 Alex Burger\n"; print "http://snmptt.sourceforge.net\n\n"; } ############################################################################## # Process command line arguments use Getopt::Long; my $version = 0; my $daemon = 0; my $debug = 0; my $debugfile = ''; my $dump = 0; my $help = 0; my $time = 0; my $ini = ''; GetOptions ('version' => \$version, 'daemon' => \$daemon, 'debug:i' => \$debug, 'debugfile=s' => \$debugfile, 'dump' => \$dump, 'help' => \$help, 'ini=s' => \$ini, 'time' => \$time); if ($version == 1) { showversion(); exit(0); } if ($help == 1) { my $USAGE = qq/Usage: snmptt [] Options: --daemon Run as a daemon --debug=n Set debug level (1 or 2) --debugfile=filename Set debug output file --dump Dump (display) defined traps --help Display this message --ini=filename Set configuration file to load --version Display author and version information --time Use to see how long it takes to load and process trap file (eg: time snmptt --time) /; showversion(); print $USAGE; exit(0); } my $DEBUGGING; my $debugcmdline; my $daemoncmdline; my $DEBUGGING_FILE; my $debugfilecmdline; if ($debug >= 1) { $DEBUGGING = $debug; $debugcmdline = 1; } else { $DEBUGGING = 0; $debugcmdline = 0; } if ($daemon == 1) { # $daemon = 1; $daemoncmdline = 1; } else { $daemoncmdline = 0; } if ($dump ==1) { $DEBUGGING = 2; $debugcmdline = 1; findsnmpttini(); loadsnmpttini(); loadsnmpttconf(); # Load SNMPTT.CONF file print ("\n\n"); exit(0); } if ($time ==1) { findsnmpttini(); loadsnmpttini(); loadsnmpttconf(); # Load SNMPTT.CONF file exit(0); } if ($debugfile ne '') { $DEBUGGING_FILE = $debugfile; # commandline overpowers snmptt script $debugfilecmdline = 1; } else { $debugfilecmdline = 0; } # Global config file variables my $snmptt_system_name; #my $daemon; my $multiple_event; my $dns_enable; my $strip_domain; my @strip_domain_list; my $net_snmp_perl_enable; my $net_snmp_perl_cache_enable; my $net_snmp_perl_best_guess; my $translate_log_trap_oid; my $translate_value_oids; my $resolve_value_ip_addresses; my $translate_enterprise_oid_format; my $translate_trap_oid_format; my $translate_varname_oid_format; my $translate_integers; my $wildcard_expansion_separator; my $mibs_environment; my $allow_unsafe_regex; my $remove_backslash_from_quotes; my $dynamic_nodes; my $description_mode; my $description_clean; my $threads_enable; my $threads_max; my $ipv6_enable; my $date_format; my $time_format; my $date_time_format; my $date_time_format_sql; my $stat_time_format_sql; # DaemonMode my $daemon_fork; my $daemon_uid; my $pid_file; my $spool_directory; my $sleep; my $use_trap_time; my $keep_unlogged_traps; my $duplicate_trap_window; # Logging my $stdout_enable; my $log_enable; my $log_format; my $log_file; my $log_system_enable; my $log_system_file; my $unknown_trap_log_enable; my $unknown_trap_log_file; my $unknown_trap_nodes_match_mode; my $statistics_interval; my $syslog_enable; my $syslog_format; my $syslog_module; my $syslog_remote_dest; my $syslog_remote_port; my $syslog_remote_proto; my $syslog_app; my $syslog_rfc_format; my $syslog_facility; my @syslog_level_alert; my @syslog_level_crit; my @syslog_level_err; my @syslog_level_warning; my @syslog_level_notice; my @syslog_level_info; my @syslog_level_debug; my $syslog_level; my $syslog_system_enable; my $syslog_system_app; my $syslog_system_facility; my $syslog_system_level; my $eventlog_enable; my @eventlog_type_information; my @eventlog_type_warning; my @eventlog_type_error; my $eventlog_type; my $eventlog_system_enable; # Exec my $exec_enable; my $pre_exec_enable; my @unknown_trap_preexec; my $unknown_trap_exec; my $unknown_trap_exec_format; my $exec_escape; # SQL my $db_translate_enterprise; my $db_unknown_trap_format; my $mysql_dbi_enable; my $mysql_dbi_host; my $mysql_dbi_port; my $mysql_dbi_database; my $mysql_dbi_table; my $mysql_dbi_table_unknown; my $mysql_dbi_table_statistics; my $mysql_dbi_username; my $mysql_dbi_password; my $mysql_ping_on_insert; my $mysql_ping_interval; my $postgresql_dbi_enable; my $postgresql_dbi_module; my $postgresql_dbi_hostport_enable; my $postgresql_dbi_host; my $postgresql_dbi_port; my $postgresql_dbi_database; my $postgresql_dbi_table; my $postgresql_dbi_table_unknown; my $postgresql_dbi_table_statistics; my $postgresql_dbi_username; my $postgresql_dbi_password; my $postgresql_ping_on_insert; my $postgresql_ping_interval; my $dbd_odbc_enable; my $dbd_odbc_dsn; my $dbd_odbc_table; my $dbd_odbc_table_unknown; my $dbd_odbc_table_statistics; my $dbd_odbc_username; my $dbd_odbc_password; my $dbd_odbc_ping_on_insert; my $dbd_odbc_ping_interval; my $sql_win32_odbc_enable; my $sql_win32_odbc_dsn; my $sql_win32_odbc_table; my $sql_win32_odbc_table_unknown; my $sql_win32_odbc_table_statistics; my $sql_win32_odbc_username; my $sql_win32_odbc_password; my @sql_custom_columns; my @sql_custom_columns_unknown; # TrapFiles my @snmptt_conf_files; ############################################################################## # # Load config file # findsnmpttini(); loadsnmpttini(); ############################################################################## use Text::ParseWords; use POSIX qw(strftime); use Sys::Hostname; use File::Basename; use Text::Balanced qw (extract_bracketed); my $debug_file_used = 0; my $g_start_time = time(); my $debug_file_open_error = 1; my $fh_DEBUGFILE; if ($DEBUGGING >= 1) { # Open the debug file if ($DEBUGGING_FILE ne '') { if (open $fh_DEBUGFILE, ">>", $DEBUGGING_FILE) { select $fh_DEBUGFILE; # change default output to debug file $debug_file_used = 1; $debug_file_open_error = 0; } else { warn "could not open debug output file ($!)"; } } # print out time print "********** SNMPTT $snmptt_version started: ",scalar(localtime($g_start_time))," **********\n\n"; if ($sleep < 1) { print "Note: snmptt.ini sleep setting is less than 1 so \'Sleeping for x seconds\' will be supressed in the debug log.\n\n"; } } if ($threads_enable == 1) { eval 'require threads;'; if ($@) { warn $@; print "\nThreads have been enabled but the threads module is not available. To\n"; print "enable threads you need the threads module (part of ithreads,not Threads).\n"; print "This is only available in Perl 5.6.0 and higher.\n"; die "died"; } require threads; eval 'require Thread::Semaphore;'; if ($@) { warn $@; print "\nThreads have been enabled but the Thread module is not available.\n"; die "died"; } require Thread::Semaphore; } if ($syslog_enable == 1 || $syslog_system_enable == 1) { if ($syslog_module == 0) { eval 'require Sys::Syslog;'; if ($@) { warn $@; print "\nCould not load Perl module Sys::Syslog! If syslog_system_enable or\n"; print "syslog_enable is enabled then the Sys::Syslog module is required. Please\n"; print "see snmptt.html for system requirements.\n\n"; die "died"; } require Sys::Syslog; } else { eval 'require Log::Syslog::Fast;'; if ($@) { warn $@; print "\nCould not load Perl module Log::Syslog::Fast! If syslog_system_enable or\n"; print "syslog_enable is enabled then the Log::Syslog::Fast module is required. Please\n"; print "see snmptt.html for system requirements.\n\n"; die "died"; } require Log::Syslog::Fast; #Log::Syslog::Fast->import( qw(:all) ); eval 'require Log::Syslog::Constants;'; if ($@) { warn $@; print "\nCould not load Perl module Log::Syslog::Constants! If syslog_system_enable or\n"; print "syslog_enable is enabled then the LLog::Syslog::Constants module is required. Please\n"; print "see snmptt.html for system requirements.\n\n"; die "died"; } require Log::Syslog::Constants; #Log::Syslog::Constants->import( qw(:functions) ); } } # Win32 constants not available when using 'require' (!) my $eventlog_error = 1; # EVENTLOG_ERROR_TYPE my $eventlog_warning = 2; # EVENTLOG_WARNING_TYPE my $eventlog_information = 4; # EVENTLOG_INFORMATION_TYPE if ($eventlog_system_enable == 1 || $eventlog_enable == 1) { eval 'require Win32::EventLog;'; if ($@) { warn $@; print "\nCould not load Perl module Win32::EventLog! If eventlog_system_enable or\n"; print "eventlog_enable is enabled, then the Win32::EventLog module is required. \n"; print "Please see snmptt.html for system requirements.\n\n"; die "died"; } require Win32::EventLog; } if ($syslog_system_enable == 1 && $daemon == 1) { syslog_system("SNMPTT $snmptt_version started"); if ($DEBUGGING >= 1 && $debug_file_open_error == 1) { syslog_system("Could not open debug output file!"); } } if ($log_system_enable == 1 && $daemon == 1) { log_system("SNMPTT $snmptt_version started"); if ($DEBUGGING >= 1 && $debug_file_open_error == 1) { log_system("Could not open debug output file!"); } } if ($eventlog_system_enable == 1 && $daemon == 1) { eventlog_system("SNMPTT $snmptt_version started",0,$eventlog_information); if ($DEBUGGING >= 1 && $debug_file_open_error == 1) { eventlog_system("Could not open debug output file!",14,$eventlog_warning); } } if ($net_snmp_perl_enable == 1) { eval 'require SNMP;'; if ($@) { warn $@; print "\nCould not load the Perl module SNMP! If net_snmp_perl_enable is\n"; print "enabled then the SNMP module is required. Please see snmptt.html\n"; print "for system requirements. Note: SNMPTT uses the Net-SNMP package's\n"; print "SNMP module, NOT the CPAN Net::SNMP module!\n\n"; die "died"; } require SNMP; if (defined ($mibs_environment)) { $ENV{'MIBS'} = $mibs_environment; } my $noperlwarning; if ($description_mode == 2) { no warnings; # Variable is only used once $SNMP::save_descriptions = 1; } SNMP::initMib(); $SNMP::best_guess = $net_snmp_perl_best_guess; $noperlwarning = $SNMP::best_guess; # Just to get rid of the Perl warning that var used only once $SNMP::use_long_names = 0; # Set to 0, otherwise getType may fail when using a long name on 4.2.x $noperlwarning = $SNMP::use_long_names; # Just to get rid of the Perl warning that var used only once if ($DEBUGGING >= 1) { print "********** Net-SNMP version $SNMP::VERSION Perl module enabled **********\n\n"; if (defined ($mibs_environment)) { print "********** MIBS: $mibs_environment **********\n\n"; } } } if ($dns_enable == 1) { #eval 'require IO::Socket::IP;'; eval 'require Socket;'; if ($@) { warn $@; print "\nCould not load the Net-SNMP Perl module Socket! If dns_enable\n"; print "is enabled then the Socket module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } #require IO::Socket::IP; require Socket; Socket->import( qw(getaddrinfo getnameinfo SOCK_RAW AI_NUMERICHOST)); if ($DEBUGGING >= 1) { print "********** DNS enabled **********\n\n"; } } if ($ipv6_enable == 1) { eval 'require Net::IP;'; if ($@) { warn $@; print "\nCould not load the Perl module Net::IP! If ipv6_enable\n"; print "is enabled then the Net::IP module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require Net::IP; if ($DEBUGGING >= 1) { print "********** ipv6_enable enabled **********\n\n"; } } if ($mysql_dbi_enable == 1) { eval 'require DBI;'; if ($@) { warn $@; print "\nCould not load the Perl module DBI! If mysql_dbi_enable\n"; print "is enabled then the DBI module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require DBI; eval 'require DBD::mysql;'; if ($@) { warn $@; print "\nCould not load the Perl module DBD::mysql! If mysql_dbi_enable\n"; print "is enabled then the DBD::mysql module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require DBD::mysql; } if ($postgresql_dbi_enable == 1) { eval 'require DBI;'; if ($@) { warn $@; print "\nCould not load the Perl module DBI! If postgresql_dbi_enable\n"; print "is enabled then the DBI module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require DBI; if ($postgresql_dbi_module == 0) { eval 'require DBD::PgPP;'; if ($@) { warn $@; print "\nCould not load the Perl module DBD::PgPP! If postgresql_dbi_module\n"; print "is set to 0 then the DBD::PgPP module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require DBD::PgPP; } else { eval 'require DBD::Pg;'; if ($@) { warn $@; print "\nCould not load the Perl module DBD::Pg! If postgresql_dbi_module\n"; print "is set to 1 then the DBD::Pg module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require DBD::Pg; } } if ($dbd_odbc_enable == 1) { eval 'require DBI;'; if ($@) { warn $@; print "\nCould not load the Perl module DBI! If dbd_odbc_enable\n"; print "is enabled then the DBI module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require DBI; eval 'require DBD::ODBC;'; if ($@) { warn $@; print "\nCould not load the Perl module DBD::ODBC! If dbd_odbc_enable\n"; print "is enabled then the DBD::ODBC module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require DBD::ODBC; } if ($sql_win32_odbc_enable == 1) { eval 'require Win32::ODBC;'; if ($@) { warn $@; print "\nCould not load the Perl module Win32::ODBC! If sql_win32_odbc_enable\n"; print "is enabled then the Win32::ODBC module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require Win32::ODBC; } if ($duplicate_trap_window > 0) { eval 'require Digest::MD5;'; if ($@) { warn $@; print "\nCould not load the Perl module Digest::MD5! If duplicate_trap_window\n"; print "is set then the Digest::MD5 module is required. Please see snmptt.html\n"; print "for system requirements.\n\n"; die "died"; } require Digest::MD5; } ############################################################################## #### MAIN SECTION START # Global variables my %event; # Holds EVENT entries from all .conf files my $receivedtrap_entry; # Trap received - stored by readtrap() my $input; # For reading in trap from spool folder or STDIN my @event2; # Copy of the matched event my @var; # Variables of trap received by SNMPTRAPD my @entvar; # Enterprise variable values of trap received by SNMPTRAPD my @entvarname; # Enterprise variable names of trap received by SNMPTRAPD my @preexec_var; # PREEXEC results my @unknown_trap_preexec_result; # Unknown trap PREEXEC results my $receivedtrap; # Received trap my $receivedtrap_trans; # Translated version of received trap my $enterprise_trans; # Translated enterprise of received trap my $agent_dns_name; # DNS name of trap received my $processed; # Whether or not the trap was processed (found) to determine # if it should search using wildcards and log to unknown my $skipped; # Whether or not the trap was skipped because of NODE/MATCH my $trap_attempted_to_log; # To keep track of whether or not we attempted to log the trap my $trap_date_time; # Date and time of the trap. Used for log files. my $trap_date_time_sql; # Date and time of the trap. Used for SQL. my $trap_successfully_logged; # To keep track of whether or not we successfully logged the trap # so we know if we should delete the trap file my $db_enterprise; my $trap_date; # Date of trap my $trap_time; # Time of trap my $trap_date_time_epoch; # Date / time of trap my $configfile; # .ini file to use my $thread_exec_semaphore; # Semaphore for EXEC # Global variables for statistics my $g_total_traps_received = 0; my $g_total_traps_translated = 0; my $g_total_traps_ignored = 0; my $g_total_traps_unknown = 0; my $g_total_traps_skipped = 0; my $g_last_statistics_logged = $g_start_time; # Global variables for SQL my $dbh_mysql; my $dbh_postgresql; my $dbh_odbc; my $dbh_win32_odbc; # Global variables for SQL ping my $g_last_mysql_ping = $g_start_time; my $g_last_postgresql_ping = $g_start_time; my $g_last_dbd_odbc_ping = $g_start_time; # Global variables for daemon mode my $timetoreload; my $timetodie; my $timetologstatistics; my %duplicate_traps; my %my_translateObj_cache; my %my_mapEnum_cache; if ($daemon == 1) { # Check for old pid file. my $pid_file_set = 0; if ($pid_file eq '') { $pid_file = '/var/run/snmptt.pid'; } else { $pid_file_set = 1; } if ($DEBUGGING >= 1) { print STDOUT "PID file: $pid_file\n"; } if (-e $pid_file) { my $fh_OLDPID; open($fh_OLDPID, "<", $pid_file); my $old_pid = <$fh_OLDPID>; chomp $old_pid; close $fh_OLDPID; warn("There seems to be another SNMPTT process (pid $old_pid) running.\n"); warn("You may want to kill it and delete the .pid file ($pid_file). Aborting...\n"); if ($syslog_system_enable == 1) { syslog_system("There seems to be another SNMPTT process (pid $old_pid) running."); syslog_system("You may want to kill it and delete the .pid file ($pid_file). Aborting..."); } if ($log_system_enable == 1) { log_system("There seems to be another SNMPTT process (pid $old_pid) running."); log_system("You may want to kill it and delete the .pid file ($pid_file). Aborting..."); } die "Exiting"; } # Check to make sure we can create the .pid file if it was set by the user. # If the user didn't set it, then we don't really care. if ($pid_file_set) { if (! (-w dirname($pid_file))) { warn("pid file \'$pid_file\' is not writable. Aborting..."); if ($syslog_system_enable == 1) { syslog_system("pid file \'$pid_file\' is not writable. Aborting..."); } if ($log_system_enable == 1) { log_system("pid file \'$pid_file\' is not writable. Aborting..."); } die "Exiting"; } } $SIG{HUP} = \&signal_handler_reload; $SIG{TERM} = \&signal_handler_die; $SIG{USR1} = \&signal_log_statistics; $timetoreload = 0; $timetodie = 0; $timetologstatistics = 0; loadsnmpttconf(); # Load SNMPTT.CONF file # Only fork to the background if not Win32 if (($^O ne "MSWin32") && ($daemon_fork==1)) { use POSIX qw(setsid); use POSIX qw(signal_h); use POSIX ":sys_wait_h"; use Cwd; my $pid; my $pid2; my $working_dir = cwd; if ($DEBUGGING >= 1) { print "cwd: $working_dir\n"; } chdir '/' or die "Can't chdir to /: $!"; open STDIN, '<', '/dev/null' or die "Can't read /dev/null: $!"; open STDOUT, '>>', '/dev/null' or die "Can't write to /dev/null: $!"; open STDERR, '>>', '/dev/null' or die "Can't write to /dev/null: $!"; # We fork so we can return back to the shell or whatever started snmptt defined($pid = fork) or die "Can't fork: $!"; # fork returns: child pid to the parent, 0 to the child, undef if it failed. # We write PID using the uid of the user that started snmptt. if ($pid) # This is run in the parent process { if ($daemon_uid eq '') { my $fh_PID; if (open($fh_PID, ">", $pid_file) ) { print($fh_PID "$pid\n"); close($fh_PID); } else { $pid_file = "$working_dir/snmptt.pid"; if (open($fh_PID, ">", $pid_file) ) { print($fh_PID "$pid\n"); close($fh_PID); } } } exit; } POSIX:setsid or die "Can't start a new session: $!"; #umask 0; umask 002; # We fork again so there are two processes. The first which is run using the uid # of the user that started snmptt, and the second which is run as the user as # defined by daemon_uid in snmptt.ini. We do this so we can sit and wait for the # child to finish so we can clean up the snmptt.pid file. # We only need to do this if daemon_uid is set.. if ($daemon_uid ne '') { defined($pid2 = fork) or die "Can't fork: $!"; if ($pid2) # This is run in the parent process { $SIG{TERM} = \&signal_handler_die; # new signal for parent my $fh_PID; if (open($fh_PID, ">", $pid_file) ) { print($fh_PID "$pid2\n"); close($fh_PID); } else { $pid_file = "$working_dir/snmptt.pid"; if (open($fh_PID, ">", $pid_file) ) { print($fh_PID "$pid2\n"); close($fh_PID); } } while (1) { if ($timetodie == 1 || waitpid($pid2, WNOHANG) != 0) { kill SIGTERM, $pid2; # Clean up snmptt.pid file unlink($pid_file); exit; } sleep 3; } } POSIX:setsid or die "Can't start a new session: $!"; #umask 0; umask 002; } } # Change user if not Windows, and daemon_uid ini parameter not blank if ($^O ne "MSWin32" && $daemon_uid ne '') { my $daemon_uid_name = ''; my $daemon_gid_name = ''; if ($DEBUGGING >= 1) { syslog_system("Configured daemon_uid: $daemon_uid\n"); } # Convert textual daemon_uid to numerical if ($daemon_uid =~ /\D/) { # no numbers found, so assume it's a textual name $daemon_uid_name = $daemon_uid; $daemon_uid = getpwnam($daemon_uid_name); if (!defined($daemon_uid)) { if ($syslog_system_enable == 1) { syslog_system("Could not convert user id \'$daemon_uid_name\' to a numeric UID\n"); syslog_system("SNMPTT $snmptt_version shutdown"); } if ($log_system_enable == 1) { log_system("Could not convert user id \'$daemon_uid_name\' to a numeric UID\n"); log_system("SNMPTT $snmptt_version shutdown"); } if ($DEBUGGING >= 1) { print "Could not convert user id \'$daemon_uid_name\' to a numeric UID\n"; print "SNMPTT $snmptt_version shutdown"; } warn("Could not convert user id \'$daemon_uid_name\' to a numeric UID\n"); exit 1; } } # Build textual user group list # Get user primary group (my $name, my $passwd, my $uid, my $gid, my $quota, my $comment, my $gcos, my $dir, my $shell, my $expire) = getpwuid($daemon_uid); my $daemon_gid = $gid; # numerical $daemon_gid_name = getgrgid($daemon_gid); # textual if ($DEBUGGING >= 1) { syslog_system("daemon_uid primary group: $daemon_gid_name ($daemon_gid)\n"); } $daemon_gid_name .= " "; # Get OS groups $daemon_gid .= " "; while( (my $name, my $passwd, my $gid, my $members_list) = getgrent() ) { my @members = split(" ", $members_list); foreach my $member (@members) { if ($member eq $daemon_uid_name) { if ($DEBUGGING >= 1) { syslog_system("daemon_uid $daemon_uid_name found in group $name\n"); } $daemon_gid_name .= "$name "; } } } chop($daemon_gid_name); $daemon_gid = $daemon_gid_name; my $daemon_gid_list = ""; my @daemon_gids = split(' ', $daemon_gid); if ($DEBUGGING >= 1) { syslog_system("daemon_uid group list: @daemon_gids\n"); } # Convert textual daemon_gid's to numerical foreach my $daemon_gid (@daemon_gids) { if ($daemon_gid =~ /\D/) { # no numbers found, so assume it's a textual name my $daemon_gid_name_temp = $daemon_gid; $daemon_gid = getgrnam($daemon_gid_name_temp); if (!defined($daemon_gid)) { if ($syslog_system_enable == 1) { syslog_system("Could not convert group id \'$daemon_gid_name_temp\' to a numeric GID\n"); syslog_system("SNMPTT $snmptt_version shutdown"); } if ($log_system_enable == 1) { log_system("Could not convert group id \'$daemon_gid_name_temp\' to a numeric GID\n"); log_system("SNMPTT $snmptt_version shutdown"); } if ($DEBUGGING >= 1) { print "Could not convert group id \'$daemon_gid_name_temp\' to a numeric GID\n"; print "SNMPTT $snmptt_version shutdown"; } warn("Could not convert group id \'$daemon_gid_name_temp\' to a numeric GID\n"); exit 1; } } $daemon_gid_list .= "$daemon_gid "; } chop($daemon_gid_list); $daemon_gid = $daemon_gid_list; if ($DEBUGGING >= 1) { syslog_system("Final daemon_gid group list: $daemon_gid_name ($daemon_gid)\n"); } # Change current uid to daemon_uid and gid to daemon_gid if (defined($daemon_uid)) { if ($daemon_uid_name ne '') { if ($syslog_system_enable == 1) { syslog_system("Changing to UID: $daemon_uid_name \($daemon_uid\), GID: $daemon_gid_name \($daemon_gid\)"); } if ($log_system_enable == 1) { log_system("Changing to UID: $daemon_uid_name \($daemon_uid\), GID: $daemon_gid_name \($daemon_gid\)"); } if ($DEBUGGING >= 1) { print "Changing to UID: $daemon_uid_name \($daemon_uid\), GID: $daemon_gid_name \($daemon_gid\)\n"; } } else { if ($syslog_system_enable == 1) { syslog_system("Changing to UID: $daemon_uid, GID: $daemon_gid"); } if ($log_system_enable == 1) { log_system("Changing to UID: $daemon_uid, GID: $daemon_gid"); } if ($DEBUGGING >= 1) { print "Changing to UID: $daemon_uid, GID: $daemon_gid\n"; } } if ($DEBUGGING >= 1) { print $fh_DEBUGFILE "Closing debug file $DEBUGGING_FILE\n"; # Close debug file (if it is open) before changing users and re-open after close($fh_DEBUGFILE); } # Change owner of debug file so $daemon_uid can write to it. chown $daemon_uid, -1, $DEBUGGING_FILE; # Permanently change to new uid/gid by setting both effetive and real IDs. $) = $daemon_gid; $( = $); # Change real ID before effective ID otherwise it breaks on FreeBSD. Linux was not affected. Bug #47. $< = $daemon_uid; $> = $<; if ($< != $daemon_uid || $( != $daemon_gid) { if ($syslog_system_enable == 1) { syslog_system("Could not change to UID: $daemon_uid, GID: $daemon_gid"); syslog_system("SNMPTT $snmptt_version shutdown"); } if ($log_system_enable == 1) { log_system("Could not change to UID: $daemon_uid, GID: $daemon_gid"); log_system("SNMPTT $snmptt_version shutdown"); } # This won't make it into the debug log.. if ($DEBUGGING >= 1) { print "Could not change to UID: $daemon_uid, GID: $daemon_gid\n"; print "SNMPTT $snmptt_version shutdown"; } warn "Could not change to UID: $daemon_uid, GID: $daemon_gid"; exit 1; } $debug_file_open_error = 1; # Re-open debug file (if needed) as the new user if ($DEBUGGING >= 1) { reopen_debug_file(); } } } # Open connections to MySQL, PostresQL, ODBC etc # (Do this after switch user IDs when daemon_uid is defined) create_db_connections(); if ($threads_enable == 1) { $thread_exec_semaphore = (); $thread_exec_semaphore = Thread::Semaphore->new($threads_max); } while (!$timetodie) { # If debugging, flush the buffers so we can tail snmptt.debug file. if ($DEBUGGING >= 1) { $| = 1; $| = 0; } if ($duplicate_trap_window) { # Purge traps older than duplicate_trap_window in %duplicate_traps my $duplicate_traps_current_time = time(); foreach my $key (sort keys %duplicate_traps) { #print "Previous trap digest: $key: " . localtime($duplicate_traps{$key}) . "\n"; if ($duplicate_traps{$key} < $duplicate_traps_current_time - $duplicate_trap_window) { # Purge the record delete $duplicate_traps{$key}; #print " Deleted...\n"; } } #print "\n"; } my $fh_DIR; if ($spool_directory eq '') { if ($DEBUGGING >= 1) { print "No spool dir defined\n"; } warn "No spool dir defined\n"; if ($syslog_system_enable == 1) { syslog_system("No spool dir defined"); } if ($log_system_enable == 1) { log_system("No spool dir defined"); } if ($eventlog_system_enable == 1) { eventlog_system("No Spool dir defined",3,$eventlog_error); } } elsif (! (chdir($spool_directory))) { if ($DEBUGGING >= 1) { print "Unable to enter spool dir $spool_directory:$!\n"; } warn "Unable to enter spool dir $spool_directory:$!\n"; if ($syslog_system_enable == 1) { syslog_system("Unable to enter spool dir $spool_directory"); } if ($log_system_enable == 1) { log_system("Unable to enter spool dir $spool_directory"); } if ($eventlog_system_enable == 1) { eventlog_system("Unable to enter spool dir $spool_directory",3,$eventlog_error); } } elsif (! (opendir($fh_DIR, "."))) { if ($DEBUGGING >= 1) { print "Unable to open $spool_directory:$!\n"; } warn "Unable to open spool dir $spool_directory:$!\n"; if ($syslog_system_enable == 1) { syslog_system("Unable to open spool dir $spool_directory"); } if ($log_system_enable == 1) { log_system("Unable to open spool dir $spool_directory"); } if ($eventlog_system_enable == 1) { eventlog_system("Unable to open spool dir $spool_directory",4,$eventlog_error); } } elsif (! (my @filenames = readdir($fh_DIR))) { if ($DEBUGGING >= 1) { print "Unable to read spool dir $spool_directory:$!\n"; } warn "Unable to read spool dir $spool_directory:$!\n"; if ($syslog_system_enable == 1) { syslog_system("Unable to read spool dir $spool_directory"); } if ($log_system_enable == 1) { log_system("Unable to read spool dir $spool_directory"); } if ($eventlog_system_enable == 1) { eventlog_system("Unable to read spool dir $spool_directory",5,$eventlog_error); } } else { closedir($fh_DIR); @filenames = sort (@filenames); # Sort list of filenames to ensure they # are processed in the order they were # received foreach my $file (@filenames) { next if ($file eq "."); next if ($file eq ".."); if (lc($file) eq "!reload") { $timetoreload = 1; unless (unlink($file)) { if ($DEBUGGING >= 1) { print "Unable to delete !reload file from spool dir:$!\n"; } warn "Unable to delete !reload file from spool dir:$!\n"; if ($syslog_system_enable == 1) { syslog_system("Unable to delete !reload file from spool dir"); } if ($log_system_enable == 1) { log_system("Unable to delete !reload file from spool dir"); } if ($eventlog_system_enable == 1) { eventlog_system("Unable to delete !reload file from spool dir",20,$eventlog_error); } } next; } if (lc($file) eq "!statistics") { $timetologstatistics = 1; unless (unlink($file)) { if ($DEBUGGING >= 1) { print "Unable to delete !statistics file from spool dir:$!\n"; } warn "Unable to delete !statistics file from spool dir:$!\n"; if ($syslog_system_enable == 1) { syslog_system("Unable to delete !statistics file from spool dir"); } if ($log_system_enable == 1) { log_system("Unable to delete !statistics file from spool dir"); } if ($eventlog_system_enable == 1) { eventlog_system("Unable to delete !statistics file from spool dir",21,$eventlog_error); } } next; } next if (! ($file =~ /\#.*/)); # Must start with # (for file locking change) if ($DEBUGGING >= 1) { print "Processing file: $file\n"; } my $filesuccess = 1; my $fh_FILE; unless (open $fh_FILE, '<', $spool_directory.$file) { if ($DEBUGGING >= 1) { print "Could not open trap file $spool_directory$file: ($!)\n"; } warn "Could not open trap file $spool_directory$file: ($!)\n"; if ($syslog_system_enable == 1) { syslog_system("Could not open trap file $spool_directory$file"); } if ($log_system_enable == 1) { log_system("Could not open trap file $spool_directory$file"); } if ($eventlog_system_enable == 1) { eventlog_system("Could not open trap file $spool_directory$file",6,$eventlog_error); } $filesuccess = 0; } $input = $fh_FILE; my $readtrap_result = readtrap(); # Read trap from STDIN or file if ($readtrap_result == 0) { if ($DEBUGGING >= 1) { print " Error processing trap file $file. Skipping...\n"; } next; } my $trap_is_a_duplicate = 0; if ($readtrap_result == -1) { $trap_is_a_duplicate = 1; if ($DEBUGGING >= 1) { print " Duplicate trap detected in trap file $file. Skipping...\n"; } } # Search for trap only if it's not a duplicate. if (! ($trap_is_a_duplicate)) { searchfortrap(); # Search for trap snmptt.conf (array) } close $fh_FILE; if ($filesuccess == 1) { if ($keep_unlogged_traps == 0 || $trap_successfully_logged == 1 || $trap_is_a_duplicate) { unless (unlink($file)) { if ($DEBUGGING >= 1) { print "Unable to delete trap file $file from spool dir:$!\n"; } warn "Unable to delete trap file $file from spool dir:$!\n"; if ($syslog_system_enable == 1) { syslog_system("Unable to delete trap file $file from spool dir"); } if ($log_system_enable == 1) { log_system("Unable to delete trap file $file from spool dir"); } if ($eventlog_system_enable == 1) { eventlog_system("Unable to delete trap file $file from spool dir",7,$eventlog_error); } } } } } } if ($statistics_interval > 0) { if (time() >= ($g_last_statistics_logged + $statistics_interval)) { log_statistics(); } } if ($mysql_dbi_enable == 1 && $mysql_ping_interval > 0) { if (time() >= ($g_last_mysql_ping + $mysql_ping_interval)) { mysql_ping(); } } if ($postgresql_dbi_enable == 1 && $postgresql_ping_interval > 0) { if (time() >= ($g_last_postgresql_ping + $postgresql_ping_interval)) { postgresql_ping(); } } if ($dbd_odbc_enable == 1 && $dbd_odbc_ping_interval > 0) { if (time() >= ($g_last_dbd_odbc_ping + $dbd_odbc_ping_interval)) { dbd_odbc_ping(); } } if ($timetologstatistics == 1) { log_statistics(); $timetologstatistics = 0; } if ($DEBUGGING >= 1) { if ($sleep >= 1) { print "Sleeping for $sleep seconds\n\n"; } } if($sleep < 1) { select(undef, undef, undef, $sleep); } else { sleep $sleep; } if ($timetoreload == 1) { if ($DEBUGGING >= 1) { print "Reloading configuration file\(s\)\n\n"; } if ($syslog_system_enable == 1) { syslog_system("Reloading configuration file\(s\)"); } if ($log_system_enable == 1) { log_system("Reloading configuration file\(s\)"); } if ($eventlog_system_enable == 1) { eventlog_system("Reloading configuration file\(s\)",8,$eventlog_information); } loadsnmpttini(); # Load ini file loadsnmpttconf(); # Load SNMPTT.CONF file $timetoreload = 0; } } # If $daemon_uid was not set, clean up pid file here. Otherwise it's cleaned up # when the child process is finished above. if ($^O ne "MSWin32" && $daemon_uid eq '') { # Clean up snmptt.pid file unlink($pid_file); } if ($DEBUGGING >= 1) { print "SNMPTT $snmptt_version shutdown: ",scalar(localtime),"\n\n"; print "Total traps received: $g_total_traps_received\n"; print "Total traps translated: $g_total_traps_translated\n"; print "Total traps ignored: $g_total_traps_ignored\n"; if ($unknown_trap_nodes_match_mode != 0) { print "Total traps skipped: $g_total_traps_skipped\n"; } print "Total unknown traps: $g_total_traps_unknown\n\n"; } if ($syslog_system_enable == 1 && $daemon == 1) { syslog_system("SNMPTT $snmptt_version shutdown"); if ($unknown_trap_nodes_match_mode != 0) { syslog_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total traps skipped=$g_total_traps_skipped,Total unknown traps=$g_total_traps_unknown"); } else { syslog_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total unknown traps=$g_total_traps_unknown"); } } if ($log_system_enable == 1 && $daemon == 1) { log_system("SNMPTT $snmptt_version shutdown"); if ($unknown_trap_nodes_match_mode != 0) { log_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total traps skipped=$g_total_traps_skipped,Total unknown traps=$g_total_traps_unknown"); } else { log_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total unknown traps=$g_total_traps_unknown"); } } if ($eventlog_system_enable == 1 && $daemon == 1) { my $message; if ($unknown_trap_nodes_match_mode != 0) { $message = "SNMPTT $snmptt_version shutdown\n\n" . "Total traps received: $g_total_traps_received\n" . "Total traps translated: $g_total_traps_translated\n" . "Total traps ignored: $g_total_traps_ignored\n" . "Total traps skipped: $g_total_traps_skipped\n" . "Total unknown traps: $g_total_traps_unknown\n"; } else { $message = "SNMPTT $snmptt_version shutdown\n\n" . "Total traps received: $g_total_traps_received\n" . "Total traps translated: $g_total_traps_translated\n" . "Total traps ignored: $g_total_traps_ignored\n" . "Total unknown traps: $g_total_traps_unknown\n"; } eventlog_system($message,1,$eventlog_information); } } else { # NOT daemon mode... # Open connections to MySQL, PostresQL, ODBC etc create_db_connections(); $input = 'STDIN'; readtrap(); # Read trap from STDIN or file loadsnmpttconf(); # Load SNMPTT.CONF file searchfortrap(); # Search for trap snmptt.conf (array) } close_db_connections(); exit(); #### MAIN SECTION END sub processtrap { # Variables of trap received by SNMPTRAPD: # # $var[0] hostname # $var[1] ip address # $var[2] uptime # $var[3] trapname / OID # $var[4] ip address from trap agent # $var[5] trap community string # $var[6] enterprise # $var[7] securityEngineID (snmptthandler-embedded required) # $var[8] securityName (snmptthandler-embedded required) # $var[9] contextEngineID (snmptthandler-embedded required) # $var[10] contextName (snmptthandler-embedded required) # # $entvarname[0] passed variable name 1 # $entvarname[1] passed variable name 2 # # $entvar[0] passed variable 1 # $entvar[1] passed variable 2 # . # . # etc.. # # $event hash for each trapped defined in snmptt.conf with following values: # # 0: name of trap # 1: category # 2: severity # 3: FORMAT string # 4: EXEC string # 5: NODES string # 6: REGEX array # 7: MATCH array # 8: DESC array # 9: PREEXEC string # # Example: To access FORMAT string for received trap: # $event{"$var[3]"}[3] # # or # # $event{"$receivedtrap_entry"}[3] ############################################################################### # if no nodes list, then $hostmatch = 1 so trap is logged etc, otherwise it is not my $l = 1; # Start with first event entry my $multiple_event_passes = 0; # This variable increases each time we match an EVENT. # Below, if =1, then abort because we already found a match for # this trap. This would only happen if you have a trap # defined multiple times in the config file to allow # different machines to have different actions based on # the node name. while (defined($event{$receivedtrap_entry}[$l+1])) { if (($multiple_event == 0) && ($multiple_event_passes > 0)) { if ($DEBUGGING >= 1) { print " multiple_event = $multiple_event, multiple_event_passes = " . "$multiple_event_passes so don't process any more entries\n"; } last; # Don't look any further - stop } if ($DEBUGGING >= 1) { print "Working with EVENT entry: $receivedtrap_entry => $event{$receivedtrap_entry}[0+$l],$event{$receivedtrap_entry}[1+$l],$event{$receivedtrap_entry}[2+$l],$event{$receivedtrap_entry}[5+$l]\n"; } my $hostmatch = 0; my $nodesmatch = 0; # Match from NODES my $match_found = 0; # Match from MATCH my $preexec_already_run = 0; # $event is the hash of events defined in the config file # $event2 is a copy of the matching event # Flush out @event2 @event2 = (); # Flush out @nodes #@nodes = (); my @nodes2 = (); # $event2[0]=$event{"$receivedtrap_entry"}[0]; # 0: name of trap # $event2[1]=$event{"$receivedtrap_entry"}[1]; # 1: category # $event2[2]=$event{"$receivedtrap_entry"}[2]; # 2: severity # $event2[3]=$event{"$receivedtrap_entry"}[3]; # 3: FORMAT string # $event2[4]=$event{"$receivedtrap_entry"}[4]; # 4: EXEC string # $event2[5]=$event{"$receivedtrap_entry"}[5]; # 5: NODES string $event2[0]=$event{"$receivedtrap_entry"}[0+$l]; # 0: name of trap $event2[1]=$event{"$receivedtrap_entry"}[1+$l]; # 1: category $event2[2]=$event{"$receivedtrap_entry"}[2+$l]; # 2: severity $event2[3]=$event{"$receivedtrap_entry"}[3+$l]; # 3: FORMAT string $event2[4]=$event{"$receivedtrap_entry"}[4+$l]; # 4: EXEC string $event2[5]=$event{"$receivedtrap_entry"}[5+$l]; # 5: NODES string $event2[6]=$event{"$receivedtrap_entry"}[6+$l]; # 6: REGEX array $event2[7]=$event{"$receivedtrap_entry"}[7+$l]; # 7: MATCH array $event2[8]=$event{"$receivedtrap_entry"}[8+$l]; # 8: DESC array $event2[9]=$event{"$receivedtrap_entry"}[9+$l]; # 9: PREEXEC array $l+=11; # Also update in 'Printing out all the events in hash table' if ($event2[5] eq '') { $nodesmatch = 1; #$hostmatch = 1; # No nodes defined, so default to all nodes #$multiple_event_passes++; #$processed = 1; if ($DEBUGGING >= 1) { print " No nodes defined for this entry so all nodes will match\n"; } } else { if ($DEBUGGING >= 1) { print " Nodes defined for this entry...\n"; } if ($dynamic_nodes == 0) { @nodes2 = split /\s/, $event2[5]; } else { @nodes2 = process_nodes($event2[5]); } if ($DEBUGGING >= 1) { print " NODES entries: @nodes2\n"; } my $nodes_mode = 'pos'; # 0 = POSitive match # nodes2 contains all the node entries after being 'extracted' from multiple # NODES lines, NODES files etc. foreach my $a (@nodes2) { # Check for MODE statement. Default is POSitive. if ($a =~ /^mode=(.*)/i) { if ($1 =~ /^neg/i) { $nodes_mode = "neg"; } next; } # IPv6_TODO_DONE elsif ($ipv6_enable == 1 && $a =~ /(.*?:.*)/) # NODES entry is an IPv6 address { if ( checkipv6($var[4], $a) == 1) { $nodesmatch = 1; } } elsif ($a =~ /^(\d+\.\d+\.\d+\.\d+)/) # NODES entry is an IPv4 address / cidr / range { #if ( $a eq $var[1]) # compare against agent ip if ( checkip($var[4], $a) == 1) { $nodesmatch = 1; } } # IPv6_TODO_DONE elsif ($dns_enable == 1) # Resolve NODES entry to IP address, and compare { my $temp = gethostbyname($a); if (defined($temp)) { #$temp = Socket::inet_ntoa(scalar($temp)); $temp = name_to_ip(scalar($temp)); if ($DEBUGGING >= 1) { print " NODES entry ($a) resolved to: $temp\n"; } if ( checkip($var[4], $temp) == 1) { $nodesmatch = 1; } } else { print " NODES entry ($a) could NOT be resolved.\n"; } } elsif (lc $a eq lc $agent_dns_name) # NODES entry is a host name. Do lowercase compare { $nodesmatch = 1; } } # If NODES MODE=NEG, then reverse the result. if ($nodes_mode eq 'neg') { if ($nodesmatch == 0) { $nodesmatch = 1; } else { $nodesmatch = 0; } } if ($DEBUGGING >= 1) { if ($nodes_mode eq 'pos') { if ($nodesmatch == 1) { print " NODES has a positive match (node mode \'pos\' and node found in list)\n"; } else { print " NODES has a negative match (node mode \'pos\' and node NOT found in list)\n"; } } else { if ($nodesmatch == 1) { print " NODES has a positive match (node mode \'neg\' and node NOT found in list)\n"; } else { print " NODES has a negative match (node mode \'neg\' and node found in list)\n"; } } } } # IPV6_TODO_DONE # Check for MATCH entries if ($nodesmatch == 1 && defined($event2[7][0])) # Only if NODES has matched and { # we have some MATCH statements my @match = (); my $match_type; for (my $i=0; defined($event2[7][$i]); $i++) { my $match_temp = $event2[7][$i]; $match_temp =~ s/\s//g; # Remove any white space from $match if ($match_temp =~ /^mode=(.*)/i) { $match_type = lc($1); last; } } if ($match_type ne 'and' && $match_type ne 'or') { $match_type = 'or'; } if ($DEBUGGING >= 2) { print " MATCH statements found\n"; print " MATCH mode: " . uc($match_type) . "\n"; } for (my $i=0; defined($event2[7][$i]); $i++) { my $match_temp = $event2[7][$i]; # If it's a regex (has ()) then remove white space before and after ()s. # Otherwise, remove all white space #print "!$match_temp!\n"; if ($match_temp =~ /\(|\)/) { $match_temp =~ s/\s*(\(.*\))\s*/$1/g; # Remove any white space from before and after ()'s $match_temp =~ s/\)\s*(i)\s*/\)$1/g; # Remove any white space from before and after i modifier # if there is one $match_temp =~ s/\s*(\!.*)/$1/g; # Remvoe any white space in front of the ! if there is one } else { $match_temp =~ s/\s//g; # Remove any white space from $match } #print "!$match_temp!\n"; # If MATCH statement contains any $p variables (PREEXEC results), then we need to execute PREEXEC first. # It's usually run after NODES and MATCH. if ($match_temp =~ /\$p/) { if ($preexec_already_run == 0) { if ($DEBUGGING >= 1) { print "\nMATCH statement contains \$p value(s). Executing PREEXEC line(s)...\n"; } preexec_run(); $preexec_already_run = 1; } } # DEFAULT= line if ($match_temp =~ /^mode=(.*)/i) { next; } if (match($match_temp) == 1) { $match_found = 1; if ($match_type eq "or") { last; } } else { $match_found = 0; if (lc($match_type) eq "and") { last; } } } if ($DEBUGGING >= 1) { if ($match_found == 1) { print " MATCH statement(s) final result = true...\n\n"; } else { print " MATCH statement(s) final result = false...\n\n"; } } } elsif ($nodesmatch == 1) { if ($DEBUGGING >= 1) { print " No MATCH entries defined for this entry\n"; } $match_found = 1; # Default to match_found if no MATCH entries } if ($nodesmatch == 1 && $match_found == 1) { $multiple_event_passes++; $hostmatch = 1; $processed = 1; } elsif ($unknown_trap_nodes_match_mode != 0) { $skipped = 1; } my $message_short; my $message; if ($hostmatch == 1 && $event2[1] ne "IGNORE") { $message_short = ""; # Trap received exists in our list of trap definitions # Statistics $g_total_traps_translated++; if ($DEBUGGING >= 1) { print "\nTrap defined, processing...\n\n"; } # # Variable substitution for PREEXEC string # if ($preexec_already_run == 0) { preexec_run(); $preexec_already_run = 1; } if ($DEBUGGING >= 1) { print "\n\nFORMAT line:\n"; } # # Variable substitution for FORMAT string # if ($event2[3] ne '') # if FORMAT string has been defined { # Variable substitution for FORMAT string $_ = $event2[3]; # FORMAT string # Translate if enabled, and if we can $receivedtrap_trans = translate_log_trap_oid_sub($receivedtrap); $enterprise_trans = translate_enterprise_oid_format_sub($var[6]); substitute(); $message_short = $_; # Default = $O $s "$c" $A - $Fz if ($log_format eq "") { $message = "$receivedtrap_trans $event2[2] \"$event2[1]\" $agent_dns_name - $message_short\n"; } else { $_ = $log_format; substitute($message_short); $message = $_ . "\n"; } if ($stdout_enable == 1) { #select STDOUT; print STDOUT "$message"; #if ($debug_file_used == 1) #{ # select $fh_DEBUGFILE; #} } if ($DEBUGGING >= 1) { print "$message_short\n"; print "\n$message"; } if ($log_enable == 1) { $trap_attempted_to_log++; my $fh_LOGFILE; if (open $fh_LOGFILE, ">>", $log_file) { print $fh_LOGFILE $trap_date_time." $message"; close $fh_LOGFILE; $trap_successfully_logged++; } else { warn "Can not open log file $log_file: $!"; if ($syslog_system_enable == 1) { syslog_system("Can not open log file $log_file"); } if ($log_system_enable == 1) { log_system("Can not open log file $log_file"); } if ($eventlog_system_enable == 1) { eventlog_system("Can not open log file $log_file",14,$eventlog_error); } } } if ($syslog_enable == 1) { # Default = $O $s "$c" $A - $Fz my $syslogmessage; if ($syslog_format eq "") { $syslogmessage = "$receivedtrap_trans $event2[2] \"$event2[1]\" $agent_dns_name - $message_short\n"; } else { $_ = $syslog_format; substitute($message_short); $syslogmessage = $_ . "\n"; } $trap_attempted_to_log++; my $syslog_level_temp = 99; # $event2[2] = severity of trap definition snmptt.conf # Sys::Syslog accepts severities with or without LOG_ and in upper and lower case. These are valid: info, iNFo, LOG_INFO, Log_Info. # Log::Syslog::Fast requires constants or the use of get_facility() from Log::Syslog::Constants so that we can use 'iNFo' etc. foreach my $temp (@syslog_level_debug) { if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'debug' } } foreach my $temp (@syslog_level_info) { if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'info' } } foreach my $temp (@syslog_level_notice) { if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'notice' } } foreach my $temp (@syslog_level_warning) { if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'warning' } } foreach my $temp (@syslog_level_err) { if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'err' } } foreach my $temp (@syslog_level_crit) { if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'crit' } } foreach my $temp (@syslog_level_alert) { if (lc($event2[2]) eq lc($temp)) { $syslog_level_temp = 'alert' } } if ($syslog_level_temp == 99) { $syslog_level_temp = $syslog_level; } if ($syslog_module == 0) { # Sys::Syslog eval { if (Sys::Syslog::openlog('snmptt', 'pid',$syslog_facility) ) { Sys::Syslog::syslog ($syslog_level_temp, "%s", $syslogmessage); Sys::Syslog::closelog(); $trap_successfully_logged++; } }; warn "Cannot write to syslog: $@\n" if $@; } else { # Log::Syslog::Fast my $protocol = ($syslog_remote_proto eq "TCP" ? Log::Syslog::Fast->LOG_TCP : Log::Syslog::Fast->LOG_UDP); eval { if (my $logger = Log::Syslog::Fast->new($protocol, $syslog_remote_dest, $syslog_remote_port, Log::Syslog::Constants::get_facility($syslog_facility), Log::Syslog::Constants::get_severity($syslog_level_temp), $agent_dns_name, $syslog_app)) { if ($syslog_rfc_format == 1) { $logger->set_format(Log::Syslog::Fast->LOG_RFC3164_LOCAL); } elsif ($syslog_rfc_format == 2) { $logger->set_format(Log::Syslog::Fast->LOG_RFC5424); } else { $logger->set_format(Log::Syslog::Fast->LOG_RFC3164); } $logger->send($syslogmessage); $trap_successfully_logged++; } }; if ($@) { warn "Cannot write to syslog: $@\n"; print "Cannot write to syslog. Message to be logged: $syslogmessage\n" if ($DEBUGGING >= 1); } } } if ($eventlog_enable == 1) { $trap_attempted_to_log++; my $eventlog_type_temp = 99; #my $temp; foreach my $temp (@eventlog_type_information) { if (lc($event2[2]) eq lc($temp)) { $eventlog_type_temp = $eventlog_information } } foreach my $temp (@eventlog_type_warning) { if (lc($event2[2]) eq lc($temp)) { $eventlog_type_temp = $eventlog_warning } } foreach my $temp (@eventlog_type_error) { if (lc($event2[2]) eq lc($temp)) { $eventlog_type_temp = $eventlog_error } } if ($eventlog_type_temp == 99) # Use the default eventlog type { if ($eventlog_type =~ /INFORMATION/s) { if (eventlog_system($message,2,$eventlog_information) == 0) { $trap_successfully_logged++; } } elsif ($eventlog_type =~ /ERROR/s) { if (eventlog_system($message,2,$eventlog_error) == 0) { $trap_successfully_logged++; } } else { if (eventlog_system($message,2,$eventlog_warning) == 0) { $trap_successfully_logged++; } } } else { if (eventlog_system($message,2,$eventlog_type_temp) == 0) { $trap_successfully_logged++; } } } if ($net_snmp_perl_enable == 1 && $db_translate_enterprise == 1) { $db_enterprise = $enterprise_trans; } else { $db_enterprise = $var[6]; } if ($mysql_dbi_enable == 1) { $trap_attempted_to_log++; # Backslash any quotes my $message_short2 = $message_short; $message_short2 =~ s(\')(\\\')g; #' $message_short2 =~ s(\")(\\\")g; #" my $community = $var[5]; $community =~ s(\')(\\\')g; #' $community =~ s(\")(\\\")g; #" my @t_sql_custom_columns = (); if (@sql_custom_columns) { @t_sql_custom_columns = @sql_custom_columns; for (my $i = 1; $i <= $#t_sql_custom_columns; $i+=2) { $_ = $t_sql_custom_columns[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns[$i] = $_; } } if (mysql_insert($mysql_dbi_table, "eventname", $event2[0], # $N "eventid", $receivedtrap_entry, # $i "trapoid", $receivedtrap_trans, # $O "enterprise", $db_enterprise, # $E or $e depending on $db_translate_enterprise "community", $community, # $C "hostname", $agent_dns_name, # $A "agentip", $var[4], # $aA "category", $event2[1], # $c "severity", $event2[2], # $s "uptime", $var[2], # $T "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns) == 1 ) { # $trap_successfully_logged++; } } if ($postgresql_dbi_enable == 1) { $trap_attempted_to_log++; # Backslash any quotes my $message_short2 = $message_short; $message_short2 =~ s(\')(\\\')g; #' $message_short2 =~ s(\")(\\\")g; #" my $community = $var[5]; $community =~ s(\')(\\\')g; #' $community =~ s(\")(\\\")g; #" my @t_sql_custom_columns = (); if (@sql_custom_columns) { @t_sql_custom_columns = @sql_custom_columns; for (my $i = 1; $i <= $#t_sql_custom_columns; $i+=2) { $_ = $t_sql_custom_columns[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns[$i] = $_; } } if (postgresql_insert($postgresql_dbi_table, "eventname", $event2[0], # $N "eventid", $receivedtrap_entry, # $i "trapoid", $receivedtrap_trans, # $O "enterprise", $db_enterprise, # $E or $e depending on $db_translate_enterprise "community", $community, # $C "hostname", $agent_dns_name, # $A "agentip", $var[4], # $aA "category", $event2[1], # $c "severity", $event2[2], # $s "uptime", $var[2], # $T "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns) == 1 ) { # $trap_successfully_logged++; } } if ($dbd_odbc_enable == 1) { $trap_attempted_to_log++; # Double any single quotes my $message_short2 = $message_short; $message_short2 =~ s(\')('')g; #' my $community = $var[5]; $community =~ s(\')('')g; #' my @t_sql_custom_columns = (); if (@sql_custom_columns) { @t_sql_custom_columns = @sql_custom_columns; for (my $i = 1; $i <= $#t_sql_custom_columns; $i+=2) { $_ = $t_sql_custom_columns[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns[$i] = $_; } } if (odbc_insert($dbd_odbc_table, "eventname", $event2[0], # $N "eventid", $receivedtrap_entry, # $i "trapoid", $receivedtrap_trans, # $O "enterprise", $db_enterprise, # $E or $e depending on $db_translate_enterprise "community", $community, # $C "hostname", $agent_dns_name, # $A "agentip", $var[4], # $aA "category", $event2[1], # $c "severity", $event2[2], # $s "uptime", $var[2], # $T "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns) == 1 ) { # $trap_successfully_logged++; } } if ($sql_win32_odbc_enable) { $trap_attempted_to_log++; # Double any single quotes my $message_short2 = $message_short; $message_short2 =~ s(\')('')g; #' my $community = $var[5]; $community =~ s(\')('')g; #' my @t_sql_custom_columns = (); if (@sql_custom_columns) { @t_sql_custom_columns = @sql_custom_columns; for (my $i = 1; $i <= $#t_sql_custom_columns; $i+=2) { $_ = $t_sql_custom_columns[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns[$i] = $_; } } if (sql_win32_odbc_insert($sql_win32_odbc_table, "eventname", $event2[0], # $N "eventid", $receivedtrap_entry, # $i "trapoid", $receivedtrap_trans, # $O "enterprise", $db_enterprise, # $E or $e depending on $db_translate_enterprise "community", $community, # $C "hostname", $agent_dns_name, # $A "agentip", $var[4], # $aA "category", $event2[1], # $c "severity", $event2[2], # $s "uptime", $var[2], # $T "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns) == 1 ) { # $trap_successfully_logged++; } } } # end block for if ($event2[3] ne '') if FORMAT string has been defined else { if ($DEBUGGING >= 1) { print " FORMAT line not defined\n"; } } # # Variable substitution for EXEC string # if ($DEBUGGING >= 1) { print "\n\nEXEC line(s):\n"; } if (defined ($event2[4][0])) # if EXEC string has been defined { if ($event2[2] ne "LOGONLY") { for (my $i=0; defined($event2[4][$i]); $i++) { $_ = $event2[4][$i]; substitute($message_short); my $command = $_; if ($exec_enable == 1) { if ($DEBUGGING >= 1) { print "EXEC command:$command\n"; } # Execute command if ($threads_enable == 1 && $daemon == 1) { if ($DEBUGGING >= 1) { print "EXEC command - creating thread...\n"; } my $exec_thread = threads->new(\&exec_thread_sub, $command); $exec_thread->detach; # Detach and let it clean up after itself } else { if ($exec_escape == 1) { # Escape wildcard characters $command =~ s/\*/\\\*/g; $command =~ s/\?/\\\?/g; } system $command; } } } } elsif ($DEBUGGING >= 1) { print " Trap severity defined as LOGONLY. Skipping EXEC processing.\n\n"; } } # end of block if ($event2[4] ne '') else { if ($DEBUGGING >= 1) { print " EXEC line not defined\n"; } } } else { if ($event2[1] eq "IGNORE") { # Statistics $g_total_traps_ignored++; } elsif ($unknown_trap_nodes_match_mode != 0) { $g_total_traps_skipped++; } if ($DEBUGGING >= 1) { if ($event2[1] eq "IGNORE") { print "\nTrap set to IGNORE...\n\n"; } else { print "\nTrap defined, but NODES or MATCH check failed - skipping this EVENT entry..\n\n"; } } } } #while defined.. } sub substitute { my $format_line = shift; # Used for $Fz variable substitution for EXEC line. # Perform substitution on $_ variable # Wildcards - $*, $+*, $-* # Expand to $1 $2 $3 etc my $temp_wildcard1 = (); my $temp_wildcard2 = (); my $temp_wildcard3 = (); for(my $i=1;$i <= $#entvar+1; $i++) { $temp_wildcard1 = $temp_wildcard1 . "\$$i" . $wildcard_expansion_separator; $temp_wildcard2 = $temp_wildcard2 . "\$\+$i" . $wildcard_expansion_separator; $temp_wildcard3 = $temp_wildcard3 . "\$-$i" . $wildcard_expansion_separator; } # Trim off last wildcard separator # TODO: Bug? Should it be checking $temp_wildcard1, $temp_wildcard2 or $temp_wildcard3 is defined? if (defined ( $temp_wildcard1) ) { my $end_trim = length($wildcard_expansion_separator); $temp_wildcard1 = substr($temp_wildcard1,0,-$end_trim); $temp_wildcard2 = substr($temp_wildcard2,0,-$end_trim); $temp_wildcard3 = substr($temp_wildcard3,0,-$end_trim); } #s(\$\*)($temp_wildcard1)g; substitute2 ("\$\*", $temp_wildcard1); s(\$\+\*)($temp_wildcard2)g; substitute2 ("\$\+\*", $temp_wildcard2); s(\$-\*)($temp_wildcard3)g; substitute2 ("\$-\*", $temp_wildcard3); # $v - Names of variable-bindings # Count down backwards to make sure 10 is not mistaken for $1 for(my $i=$#entvarname+1;$i > 0; $i--) { if ($net_snmp_perl_enable == 1) { my $temp = my_translateObj($entvarname[$i-1],$translate_varname_oid_format); if (!defined ($temp) ) { $temp = $entvarname[$i-1]; } #s(\$v$i)($temp)g; substitute2 ("\$v$i", $temp); } else { #s(\$v$i)($entvarname[$i-1])g; substitute2 ("\$v$i", $entvarname[$i-1]); } } # $n - Variable-bindings # Count down backwards to make sure 10 is not mistaken for $1 for(my $i=$#entvar+1;$i > 0; $i--) { my $val = $entvar[$i-1]; if ($DEBUGGING >= 2) { print "\nVariable $entvarname[$i-1] with value $entvar[$i-1]\n"; } # This will translate (if enabled) any OIDs found in the VALUE (not the variable name) to the textual # name if possible. For example, if the value was 'A UPS Alarm (.1.3.6.1.4.1.534.1.7.12) has cleared.', # it could be translated to: 'A UPS Alarm (xupsBuildingAlarm) has cleared.' if ($translate_value_oids > 0 && $net_snmp_perl_enable == 1) { $val = translate_value_oids_sub($val); } # IPv6_TODO_DONE #if ($dns_enable == 1 && $resolve_value_ip_addresses == 1 && $net_snmp_perl_enable == 1) # Shouldn't need to check net_snmp_perl_enable - 2021/03/16 - Alex if ($dns_enable == 1 && $resolve_value_ip_addresses == 1) { $val = resolve_value_ip_addresses_sub($val); if ($ipv6_enable == 1) { $val = resolve_value_ipv6_addresses_sub($val); } } if ($translate_integers == 1 && $net_snmp_perl_enable == 1) { if (! ($entvar[$i-1] =~ /\D+/)) # Check to see if value is numerical { if ($DEBUGGING >= 2) { print " Value is numerical\n"; } my $temp_OID_type = uc (my_getType($entvarname[$i-1])); if ($temp_OID_type eq "INTEGER32" || $temp_OID_type eq "INTEGER") # Make sure it's a SNMP INTEGER type { if ($DEBUGGING >= 2) { print " Value is defined as an INTEGER in the mib - will attempt to translate\n"; } my $val2 = my_mapEnum($entvarname[$i-1], $entvar[$i-1]); if ($val2 ne '') # Make sure we got something { $val = $val2; if ($DEBUGGING >= 2) { print " Translated to: $val2\n"; } } else { $val = $entvar[$i-1]; if ($DEBUGGING >= 2) { print " Could not translate\n"; } } } else { if ($DEBUGGING >= 2) { print " Value is NOT defined as an INTEGER or Integer32 in the mib\n"; } } } } if ($net_snmp_perl_enable == 1) { #s(\$$i)($val)g; # $n substitute2 ("\$$i", $val); # Translate for $+n and $-n my $temp = my_translateObj($entvarname[$i-1],$translate_varname_oid_format); if (!defined ($temp) ) { $temp = $entvarname[$i-1]; } my $temp2 = my_getType($entvarname[$i-1]); if (!defined ($temp2)) { $temp2 = "unknown"; } #s(\$\+$i)($temp:$val)g; # $+n substitute2 ("\$\+$i", "$temp:$val"); #s(\$-$i)($temp ($temp2):$val)g; # $-n substitute2 ("\$-$i", "$temp ($temp2):$val"); } else { #s(\$$i)($val)g; # $n substitute2 ("\$$i", $val); #s(\$\+$i)($entvarname[$i-1]:$val)g; # $+n substitute2 ("\$\+$i", "$entvarname[$i-1]:$val"); #s(\$-$i)($entvarname[$i-1] (unknown):$val)g; # $-n substitute2 ("\$-$i", "$entvarname[$i-1] (unknown):$val"); } } # $Be - securityEngineID (snmpEngineID) substitute2 ("\$Be", $var[7]); # $Bu - securityName (snmpCommunitySecurityName) substitute2 ("\$Bu", $var[8]); # $BE - contextEngineID (snmpCommunityContextEngineID) substitute2 ("\$BE", $var[9]); # $Bn - contextName (snmpCommunityContextName) substitute2 ("\$Bn", $var[10]); # $c - Category #s(\$c)($event2[1])g; substitute2 ("\$c", $event2[1]); # $C - Trap community string #s(\$C)($var[5])g; substitute2 ("\$C", $var[5]); # $E - Enterprise trap (symbolic) #s(\$E)($enterprise_trans)g; substitute2 ("\$E", $enterprise_trans); # $e - Enterprise trap (numerical) #s(\$e)($var[6])g; substitute2 ("\$e", $var[6]); # $j - Enterprise number $var[6] =~ /.*\.(\d+)/; my $enterpriseInteger = $1; substitute2 ("\$j", $enterpriseInteger); # $N - Event name #s(\$N)($event2[0])g; substitute2 ("\$N", $event2[0]); # $i - Event OID #s(\$i)($receivedtrap_entry)g; substitute2 ("\$i", $receivedtrap_entry); # $# - Number of variable-bindings in the trap my $entvar_temp = $#entvar+1; #s(\$#)($entvar_temp)g; substitute2 ("\$#", $entvar_temp); # $O - Received trap (symbolic) if ($net_snmp_perl_enable == 1) { if ($DEBUGGING >= 2) { print "\nOID of received trap: $receivedtrap. Will attempt to translate to text\n"; } my $temp = my_translateObj("$receivedtrap",$translate_trap_oid_format); if (defined ($temp) ) { if ($DEBUGGING >= 2) { print " Translated to $temp\n"; } #s(\$O)($temp)g; substitute2 ("\$O", $temp); } else { # Could not translate default to numeric if ($DEBUGGING >= 2) { print " Could not translate - defaulting to numeric\n"; } #s(\$O)($temp)g; substitute2 ("\$O", $temp); } } else { #s(\$O)($receivedtrap)g; substitute2 ("\$O", $receivedtrap); } # $o - Received trap (numerical) #s(\$o)($receivedtrap)g; substitute2 ("\$o", $receivedtrap); # $s - Severity #s(\$s)($event2[2])g; substitute2 ("\$s", $event2[2]); # $AR, $ar - IP address #s(\$aR)($var[1])g; substitute2 ("\$aR", $var[1]); #s(\$ar)($var[1])g; substitute2 ("\$ar", $var[1]); # $R, $r - Trap hostname #s(\$R)($var[0])g; substitute2 ("\$R", $var[0]); # $H - Host name of the system running SNMPTT substitute2 ("\$H", $snmptt_system_name); #s(\$r)($var[0])g; substitute2 ("\$r", $var[0]); # IPv6_TODO_DONE # $A, $a - Trap agent IP address #s(\$aA)($var[4])g; # IP address substitute2 ("\$aA", $var[4]); # IPv6_TODO_DONE #s(\$A)($agent_dns_name)g; # hostname substitute2 ("\$A", $agent_dns_name); # $T - Uptime #s(\$T)($var[2])g; substitute2 ("\$T", $var[2]); # $x - Current date #s(\$x)($trap_date)g; substitute2 ("\$x", $trap_date); # $X - Current time #s(\$X)($trap_time)g; substitute2 ("\$X", $trap_time); # $@ - Current time since epoch (Jan 1, 1904 for Mac, Jan 1, 1900 for most others) #s(\$@)($trap_date_time_epoch)g; substitute2 ("\$@", $trap_date_time_epoch); # iso(1) org(3) dod(6) internet(1) snmpV2(6) snmpModules(3) snmpMIB(1) snmpMIBObjects(1) 5 # SNMPv2 Generic? 1=coldStart....5=authenticationFailure, 6=egpNeighborLoss so 0=enterprise? if ($receivedtrap =~ /^.1.3.6.1.6.3.1.1.5/) { # It's a generic trap # $S - Specific trap number - 0 #s(\$S)(0)g; substitute2 ("\$S", "0"); # $G - Generic trap number - last # my $temp = $_; # Save old $_ $_ = $receivedtrap; /(\d+)$/; my $temp2 = $1; $_ = $temp; # Restore old $_ #s(\$G)($temp2)g; substitute2 ("\$G", $temp2); } else { # It's an enterprise trap # $S - Specific trap number - last # my $temp = $_; # Save old $_ $_ = $receivedtrap; /(\d+)$/; my $temp2 = $1; $_ = $temp; # Restore old $_ #s(\$S)($temp2)g; substitute2 ("\$S", $temp2); # $G - Generic trap number - 0 s(\$G)(0)g; substitute2 ("\$G", "0"); } # $D - Description from MIB file (NOT from snmptt.conf) if ($net_snmp_perl_enable == 1 && $description_mode == 2) { my $description_temp; { no warnings; # Variable is only used once $description_temp = $SNMP::MIB{$event2[0]}->{description}; } if ($description_clean == 1) { $description_temp =~ s/\n\s+/\n/g; } s(\$D)($description_temp)g; substitute2 ("\$D", $description_temp); } elsif ($description_mode == 1) { my $description_temp; for (my $i=0; defined($event2[8][$i]); $i++) { $description_temp = $description_temp . $event2[8][$i] . "\n"; } if ($description_clean == 1) { $description_temp =~ s/\n\s+/\n/g; } substitute2 ("\$D", $description_temp); } else { #s(\$D)()g; substitute2 ("\$D", ""); } # Formatting # alarm (bell) (BEL) substitute2 ("\$Fa", "\a"); # form feed (FF) substitute2 ("\$Ff", "\f"); # newline (LF, NL) substitute2 ("\$Fn", "\n"); # return (CR) substitute2 ("\$Fr", "\r"); # tab (HT, TAB) substitute2 ("\$Ft", "\t"); # $pn - PREEXEC variables # preexec_var #&substitute2 ("\$p", "$preexec_var"); # Count down backwards to make sure 10 is not mistaken for $1 for(my $i=$#preexec_var+1;$i > 0; $i--) { my $val = $preexec_var[$i-1]; if ($DEBUGGING >= 2) { print "\nPREEXEC variable $i: $val\n"; } substitute2 ("\$p$i", $val); } # $pun - Unknown trap PREEXEC unknown_trap_preexec # Count down backwards to make sure 10 is not mistaken for $1 for(my $i=$#unknown_trap_preexec_result+1;$i > 0; $i--) { my $val = $unknown_trap_preexec_result[$i-1]; if ($DEBUGGING >= 2) { print "\nUnknown trap PREEXEC variable $i: $val\n"; } substitute2 ("\$pu$i", $val); } # FORMAT line (only possible when doing EXEC line) substitute2 ("\$Fz", "$format_line"); # $$ - Print a $ s(\$\$)(\$)g; # Do user defined REGEX on $message if ($DEBUGGING >= 2 && defined($event2[6][0])) { print "\n$_\n"; } for (my $i=0; defined($event2[6][$i]); $i++) { my $regex_temp = $event2[6][$i]; #print "!!!!!!$regex_temp\n"; # Pull out modifiers $regex_temp =~ /^(.*\))(.*)$/; $regex_temp = $1; my $modifiers = $2; # Split using )( my @regex_temp2; ($regex_temp2[0], $regex_temp2[1]) = extract_bracketed($regex_temp,'()'); #Remove starting and ending () from each part of the split REGEX $regex_temp2[0] =~ s/^\((.*)\)$/$1/; $regex_temp2[1] =~ s/^\((.*)\)$/$1/; #print "0:$regex_temp2[0]\n"; #print "1:$regex_temp2[1]\n"; #print "Modifiers: $modifiers\n"; if ($regex_temp2[0] ne '') { # allowed modifiers: any combination of: i, g, e, ee+ not permitted my $modifiers2 = 0; if ($modifiers =~ /i/) { $modifiers2 = $modifiers2 | 1; } if ($modifiers =~ /g/) { $modifiers2 = $modifiers2 | 2; } if ($modifiers =~ /e/) { if ($allow_unsafe_regex == 1) { $modifiers2 = $modifiers2 | 4; # Append a package declaration to the beginning to prevent global variables from being # visible. Also remove any attempts to access variables from the main:: package. $regex_temp2[1] = "package regexisoloate;$regex_temp2[1]"; $regex_temp2[1] =~ s/\$main::/\$/gi; # Remove attempts to access the main package } else { if ($DEBUGGING >= 2) { print " REGEX detected with e modifier, but allow_unsafe_regex is disabled. Removing e modifier\n"; } } } #print "modifiers2 = $modifiers2\n"; # 0 none # 1 i # 2 g # 3 ig # 4 e # 5 ie # 6 ge # 7 ige if ($DEBUGGING >= 2) { print " Doing REGEX: s($regex_temp2[0])($regex_temp2[1])$modifiers\n"; print " FORMAT line before: $_\n"; } # If unsafe is off, then don't use the e modifier. This means that variable interopolation # will not work on the right side. If the user puts a $1 on the right, it will print out $1. # # If unsafe is on, then we use the /ee modifier. # # With a normal regular expression in a program, the e modifier is not requried for variable # interopolation to work. If you want to have code executed, then you need at least one e # modifier in a regular program. # # Because we are passing the right side to a perl program instead of hard-coding the right side # *inside* of the program, we need to handle it differently. These are the rules: # # -for interopolation and / or code execution to work, we need to use the /ee modifier inside the # program (snmptt) # -if we only want interop and no code execution (same behaviour as a program with no e modifier), # then we: # -escape all backslashes # -escape all double quotes # -escape all single quotes # -put quotes around the whole thing # - One would assume that if we didn't put quotes around everything, and used one /e instead of two, then # it would work, but it does not.. # # -if we want both interop and code to execute (same behaviour as a program with one e modifier), # then we: # -do nothing extra (no escaping or putting quotes around it) # # With unsafe off, or unsafe no with NO e modifier, whatever the user puts in the right side of the # REGEX will display exactly, with the only exception of the $n variable interop working with unsafe on. # # With unsafe on and one e modifier, the right side is evaluated so any text needs to be in quotes, and # printable quotes need to be escaped etc just like a normal program. # # Example1: # # "5 + 5 is ".(5+5)."and \"this is quoted\"" # will output: # 5+5 is 10 and "this is quoted" # # If unsafe was off, it would output: # # "5 + 5 is ".(5+5)."and \"this is quoted\"" # # Example2: # # "5 + 5 is ".sprintf("%d",(5+5))."and \"this is quoted\"" # will output: # 5+5 is 10 and "this is quoted" # # If unsafe was off, it would output: # # "5 + 5 is ".sprintf("%d",(5+5))."and \"this is quoted\"" if ($allow_unsafe_regex == 0) { # unsafe_regex is OFF if ($modifiers2 == 0 ) { s($regex_temp2[0])($regex_temp2[1]); } elsif ($modifiers2 == 1) { s($regex_temp2[0])($regex_temp2[1])i; } elsif ($modifiers2 == 2) { s($regex_temp2[0])($regex_temp2[1])g; } elsif ($modifiers2 == 3) { s($regex_temp2[0])($regex_temp2[1])ig; } } else { # unsafe_regex is ON # If unsafe is on, use two e's. If user does not specify an e, # escape back slashes and quotes, and put quotes around the whole string # to prevent functions from being executed if (! ($modifiers =~ /e/)) { # If it contains a \, then escape it $regex_temp2[1] =~ s/\\/\\\\/g; # Escape each quote $regex_temp2[1] =~ s/\"/\\\"/g; # double quotes $regex_temp2[1] =~ s/\'/\\\'/g; # single quotes # put quotes around the whole thing $regex_temp2[1] = "\"$regex_temp2[1]\""; } #print "1:$regex_temp2[1]\n"; if ($modifiers2 == 0 ) { s($regex_temp2[0])($regex_temp2[1])ee; } elsif ($modifiers2 == 1) { s($regex_temp2[0])($regex_temp2[1])eei; } elsif ($modifiers2 == 2) { s($regex_temp2[0])($regex_temp2[1])eeg; } elsif ($modifiers2 == 3) { s($regex_temp2[0])($regex_temp2[1])eeig; } elsif ($modifiers2 == 4) { s($regex_temp2[0])($regex_temp2[1])ee; } elsif ($modifiers2 == 5) { s($regex_temp2[0])($regex_temp2[1])eei; } elsif ($modifiers2 == 6) { s($regex_temp2[0])($regex_temp2[1])eeg; } elsif ($modifiers2 == 7) { s($regex_temp2[0])($regex_temp2[1])eeig; } } if ($DEBUGGING >= 2) { print " FORMAT line after: $_\n"; } } } } # sub substitute2 (left, right) # # left: inside of double quotes with escapes. eg: "\$a" # right: replacement string eg: $name, "hello", "hi$namebye" # # Only substitute if there is an odd number of $ or \ symbols before the # substitute variable (eg: a in $a). If there is an even number (0,2,4 etc) # then do NOT substitute. If there is an odd number (1,3,5 etc) then # substitute. For example, if $a converted to x: # $a = x # $$a = $a # $$$a = $x # $$$$a = $$a # $$$$$a = $$x # # We need variable length look behind pattern matching to do this, but Perl # does not support it. As an alternative we can reverse the string, do a # substitute with look ahead, and reverse it back. # sub substitute2 { my $left = shift; my $right = shift; my $string_r = reverse $_; my $left_r = reverse $left; $left_r =~ s/\\/\\\\/g; # escape \ $left_r =~ s/\$/\\\$/g; # escape $ $left_r =~ s/\*/\\\*/g; # escape * $left_r =~ s/\+/\\\+/g; # escape + my $right_r = reverse $right; # The format is: # s/a\$((?=(\$\$)*(?!\$)))/b/g; # where 'a\$' is the reverse of what to look for and 'b' is what to replace # it with (reversed). $string_r =~ s/$left_r((?=(\$\$)*(?!\$)))/$right_r/g; $_ = reverse $string_r; } sub loadsnmpttconf { # # snmptt.conf loader. # This section loads the snmptt.conf which defines the traps and text strings # that will be used for variable substitution # Flush variables in case this is a re-load during run my @snmpttconf = (); # @snmptt_conf_files = (); undef %event; my $fh_SNMPTTCONF; foreach my $snmpttconffile (map { glob } @snmptt_conf_files) { if ($DEBUGGING >= 1) { print "\n\tLoading $snmpttconffile\n"; } if ($syslog_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { syslog_system("Loading $snmpttconffile"); } if ($log_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { log_system("Loading $snmpttconffile"); } if ($eventlog_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { eventlog_system("Loading $snmpttconffile",9,$eventlog_information); } unless (open $fh_SNMPTTCONF, '<', $snmpttconffile) { warn "Could not open configuration file: $snmpttconffile ($!)"; if ($DEBUGGING >= 1) { print "\n\tCould not open configuration file: $snmpttconffile\n"; } if ($syslog_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { syslog_system("Could not open configuration file: $snmpttconffile"); } if ($log_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { log_system("Could not open configuration file: $snmpttconffile"); } if ($eventlog_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { eventlog_system("Could not open configuration file: $snmpttconffile",10,$eventlog_error); } next; } my $tempcount = 0; while (<$fh_SNMPTTCONF>) { chomp; #remove at end of line s/\015//; # Remove any DOS carriage returns push(@snmpttconf, $_); #add to each line to @trapconf array $tempcount++; } if ($DEBUGGING >= 1) { print "\tFinished loading $tempcount lines from $snmpttconffile\n"; } if ($syslog_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { syslog_system("Finished loading $tempcount lines from $snmpttconffile"); } if ($log_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { log_system("Finished loading $tempcount lines from $snmpttconffile"); } if ($eventlog_system_enable == 1 && $daemon == 1 && $dump == 0 && $time == 0) { eventlog_system("Finished loading $tempcount lines from $snmpttconffile",11,$eventlog_information); } } if ($DEBUGGING >= 1) { print "\nFinished loading configuration files\n"; } # Process each line of snmptt.conf loaded into @snmpttconf array. # Add each EVENT to %event hash if ($DEBUGGING >= 1) { print "\nProcessing memory copy of configuration files\n"; } my $currentline=0; my $tempcount=0; my $tempcount2=0; while ($currentline <= $#snmpttconf) { my $line = $snmpttconf[$currentline]; if ($line =~ /^EVENT/) { #EVENT name .x.x.x.x "category" severity (note: .x.x. is FULL OID!) #0 1 2 3 4 # use shellwords to divide the two space separated values # without messing up quoted text and re-join using :: as the # delimiter # # hash elements: # 0: name of trap # 1: category # 2: severity # 3: FORMAT string # 4: EXEC string # 5: NODES string # 6: REGEX array # 7: MATCH array # 8: DESC array $tempcount++; # Temporary counter of the number of EVENTS found # Divide up line taking into consideration strings in quotes my $temp = join "::::", shellwords($line); #print "line is: $temp\n"; # Split line up into multiple elements of an array using the new delimiter my @temp = split ("::::",$temp); if (! ($temp[2] =~ /^(\.\d+)+$/) && ! ($temp[2] =~ /^(\.\d+)+\.\*$/) && ! ($temp[2] =~ /^(\.\*)$/) ) { # OID contains text, so it's not fully numerical if ($net_snmp_perl_enable == 1) { # Try to convert to numerical if ($DEBUGGING >= 2) { print "Non-numeric OID: $temp[2]. Will attempt to translate to numerical\n"; } my $temp2 = SNMP::translateObj("$temp[2]",0); if (defined ($temp2) ) { if ($DEBUGGING >= 2) { print " Translated to $temp2\n"; } $temp[2] = $temp2; } else { # Could not translate, so skip this entry if ($DEBUGGING >= 2) { print " Could not translate - skipping entry\n"; } $currentline++; # Increment to the next line next; } } else { # Net-SNMP Perl module not enabled, and OID is not numeric so skip this entry if ($DEBUGGING >= 2) { print "Non-numeric OID: $temp[2]. Net-SNMP Perl module not enabled - skipping entry\n"; } $currentline++; # Increment to the next line next; } } # Clear out the FORMAT, EXEC and NODES lines in case they are not found. Don't want old # data in them.. my $lineformat = ''; my $lineexec = ''; my $linenodes = ''; my $lineregex = ''; my @exec = (); my @regex = (); my @match = (); my @desc = (); my @preexec = (); $currentline++; # Increment to the next line which should be a FORMAT, EXEC or NODES my $line2 = $snmpttconf[$currentline]; while ( ($currentline <= $#snmpttconf) && !($line2 =~ /^EVENT/) ) { # Keep going through the file until the next EVENT or the end of snmptt.conf # is reached # Check to see if next line is a FORMAT line (it should be!) if ($line2 =~ /^FORMAT/) { # It's a FORMAT line $lineformat = substr($line2,7); } elsif ($line2 =~ /^EXEC/) { # It's an EXEC line $lineexec = substr($line2,5); push (@exec,$lineexec); } elsif ($line2 =~ /^PREEXEC/) { # It's a PREEXEC line my $linematch = substr($line2,8); push (@preexec,$linematch); } elsif ($line2 =~ /^NODES/) { # It's a NODES line # Allow for multiple NODES lines - concatenate them $linenodes = $linenodes . substr($line2,6) . " "; } elsif ($line2 =~ /^REGEX/) { # It's a REGEX line $lineregex = substr($line2,6); # Remove spaces before and after $lineregex =~ /^\s*(.*?)\s*$/; $lineregex = $1; # Make sure it looks like ()() before accepting if ($lineregex =~ /\(.*?\)\(.*?\)/ ) { push (@regex,$lineregex); } } elsif ($line2 =~ /^MATCH/) { # It's a MATCH line my $linematch = substr($line2,6); push (@match,$linematch); } elsif ( ($line2 =~ /^SDESC/) && ($description_mode== 1) ) { # It's a DESC line $currentline++; # Increment to the next line $line2 = $snmpttconf[$currentline]; # Get next line while ( ($currentline <= $#snmpttconf) && !($line2 =~ /^EVENT/) && !($line2 =~ /^EDESC/)) { print "DESC line: $line2\n"; push (@desc,$line2); $currentline++; # Increment to the next line $line2 = $snmpttconf[$currentline]; # Get next line } } $currentline++; # Increment to the next line $line2 = $snmpttconf[$currentline]; # Get next line } if ($lineformat ne '' || $exec[0] ne '') { # At least the mandatory FORMAT line was defined, so we'll keep it # Stuff all elements into a one hash # $event{@temp[2]} = [@temp[1],@temp[3],@temp[4],$lineformat,$lineexec,$linenodes]; if (!defined(@{ $event{$temp[2]} }[0])) { @{ $event{$temp[2]} }[0] = 0; } #@regex = qw/one two/; my $countx = @{ $event{$temp[2]} }[0]; #print "countx $countx\n"; @{ $event{$temp[2]} }[1+$countx]= $temp[1]; @{ $event{$temp[2]} }[2+$countx]= $temp[3]; @{ $event{$temp[2]} }[3+$countx]= $temp[4]; @{ $event{$temp[2]} }[4+$countx]= $lineformat; #@{ $event{$temp[2]} }[5+$countx]= $lineexec; @{ $event{$temp[2]} }[5+$countx]= [@exec]; # If dynamic_nodes is disabled, process the nodes list to load any nodes # files first so they are not loaded each time a trap is processed if ($dynamic_nodes == 0) { my @nodes2 = process_nodes($linenodes); @{ $event{$temp[2]} }[6+$countx]= "@nodes2"; } else { @{ $event{$temp[2]} }[6+$countx]= $linenodes; } @{ $event{$temp[2]} }[7+$countx]= [@regex]; @{ $event{$temp[2]} }[8+$countx]= [@match]; @{ $event{$temp[2]} }[9+$countx]= [@desc]; @{ $event{$temp[2]} }[10+$countx]= [@preexec]; @{ $event{$temp[2]} }[0]= 11+ @{ $event{$temp[2]} }[0]; $tempcount2++; # Temporary counter of the number of EVENTS found with at # least a FORMAT line specified } # Change currentline back so the next increment will re-evalutate the last line which # would have been an EVENT (to get out of the WHILE loop above), or the end of the file $currentline--; } $currentline++; # Increment current line for next loop through snmpttconf array } if ($DEBUGGING >= 1) { print "$tempcount EVENTs found\n"; print "$tempcount2 EVENTs found that contain at least the mandatory FORMAT line\n"; if($tempcount2<$tempcount) { print " Warning: EVENT entries were found that did not contain the mandatory FORMAT line!\n"; print " These EVENTS will be ignored!\n"; } print "Finished processing memory copy of configuration files\n\n"; } if ($DEBUGGING >= 2) { print "==========================================================\n"; print "Printing out all the events in hash table:\n\n"; foreach my $key (sort keys %event) { my $l=0; while (defined($event{$key}[$l+1])) { # 0: name of trap # 1: category # 2: severity # 3: FORMAT string # 4: EXEC string # 5: NODES string # 6: REGEX array # 7: MATCH array # 8: DESC array # 9: PREEXEC string my $trap_Name = $event{$key}[1+$l]; my $trap_category = $event{$key}[2+$l]; my $trap_severity = $event{$key}[3+$l]; my $trap_FORMAT = $event{$key}[4+$l]; #my $trap_REGEX = $event{$key}[7+$l][0]; print "Event: $key => $trap_Name,$trap_category,$trap_severity,$trap_FORMAT\n"; # print "Event: $key => $event{$key}[1+$l],$event{$key}[2+$l],$event{$key}[3+$l],$event{$key}[4+$l],$event{$key}[7+$l][0]\n"; $l+=11; } } print "\nFinished printing out all events in hash table\n"; print "==========================================================\n"; # Print out duplicates if asking for a dump (snmptt --dump) if ($dump == 1) { print "Printing out all duplicate events in hash table:\n\n"; my %temp = (); foreach my $key (sort keys %event) { my $l=0; while (defined($event{$key}[$l+1])) { $temp{$key}++; $l+=11; } } foreach my $key (sort keys %temp) { if ($temp{$key} > 1) { print "Duplicate event: $key\n"; } } print "\nFinished printing out all duplicate events in hash table\n"; print "==========================================================\n"; } } close $fh_SNMPTTCONF; } sub readtrap { # Flush out @tempvar, @var and @entvar my @tempvar = (); @var = (); @entvar = (); @entvarname = (); @preexec_var = (); my @rawtrap = (); # Statistics $g_total_traps_received++; if ($DEBUGGING >= 2) { print "Reading trap. Current time: " . scalar(localtime()). "\n"; } if ( $daemon == 1) { chomp($trap_date_time_epoch = (<$input>)); # Pull time trap was spooled push(@rawtrap, $trap_date_time_epoch); if ($trap_date_time_epoch eq "") { if ($DEBUGGING >= 1) { print " Invalid trap file. Expected a serial time on the first line but got nothing\n"; return 0; } } $trap_date_time_epoch =~ s/[^0-9]//g; # Sanitize / Remove non numeric characters } else { $trap_date_time_epoch = time(); # Use current time as time trap was received } my @localtime_array; if ( $daemon == 1 && $use_trap_time == 1 ) # Daemon mode only { @localtime_array = localtime($trap_date_time_epoch); if ($date_time_format eq "") { $trap_date_time = localtime($trap_date_time_epoch); } else { $trap_date_time = strftime $date_time_format, @localtime_array; } if ($date_time_format_sql eq "") { $trap_date_time_sql = localtime($trap_date_time_epoch); } else { $trap_date_time_sql = strftime $date_time_format_sql, @localtime_array; } } else # Standalone mode { @localtime_array = localtime(); if ($date_time_format eq "") { $trap_date_time = localtime(); } else { $trap_date_time = strftime $date_time_format, @localtime_array; } if ($date_time_format_sql eq "") { $trap_date_time_sql = localtime(); } else { $trap_date_time_sql = strftime $date_time_format_sql, @localtime_array; } } $trap_date = strftime $date_format, @localtime_array; $trap_time = strftime $time_format, @localtime_array; # Pull in passed SNMP info from snmptrapd via STDIN and place in the array @tempvar # Net-SNMP examples: # UDP: [127.0.0.1]:38249->[127.0.0.1]:162 chomp($tempvar[0]=<$input>); # hostname push(@rawtrap, $tempvar[0]); if ($ipv6_enable == 1) { $tempvar[0] =~ s/[^a-zA-Z0-9.\-_:\[\]%]//g; # Sanitize / Remove most non alphanumeric characters } else { #$tempvar[0] =~ s/[^a-zA-Z0-9.\-_]//g; # Sanitize / Remove most non alphanumeric characters $tempvar[0] =~ s/[^a-zA-Z0-9.\-_:\[\]]//g; # Sanitize / Remove most non alphanumeric characters } if ($tempvar[0] eq "") { if ($DEBUGGING >= 1) { print " Invalid trap file. Expected a hostname on line 2 but got nothing\n"; return 0; } } # IPv6_TODO_DONE # With DNS resolution disabled in snmptrapd, some systems pass the hostname as: # UDP: [x.x.x.x]:161->[y.y.y.y] # If this is detected, use x.x.x.x as the hostname. if ($ipv6_enable == 1) { if ($tempvar[0] =~ /\[(\d+\.\d+\.\d+\.\d+)\].*?-\[(\d+\.\d+\.\d+\.\d+)\]/) { # > character is removed above #if ($tempvar[0] =~ /\[(\d+\.\d+\.\d+\.\d+)\].*?->\[(\d+\.\d+\.\d+\.\d+)\]/) { # IPv4 $tempvar[0] = $1; if ($DEBUGGING >= 2) { print " IPv4 detected for value 0\n"; } } elsif ($tempvar[0] =~ /\[(.*?)\]/) { # IPv6 # Expecting UDP/IPv6: [fe80::ec21:4113:ff2b:1234%ens160]:47685 if ($DEBUGGING >= 2) { print " IPv6 detected for value 0\n"; } $tempvar[0] = $1; # Remove zone index from IPv6 address if ($tempvar[0] =~ /(.*?)%.*/) { $tempvar[0] = $1; } } elsif ($tempvar[0] =~ /(.*?:.*)/) { # IPv6 # Expecting: fe80::ec21:4113:ff2b:1234%ens160 if ($DEBUGGING >= 2) { print " IPv6 detected for value 0\n"; } $tempvar[0] = $1; if ($tempvar[0] =~ /(.*?)%.*/) { $tempvar[0] = $1; } } } else { if ($tempvar[0] =~ /\[(\d+\.\d+\.\d+\.\d+)\].*?-\[(\d+\.\d+\.\d+\.\d+)\]/) { # > character is removed above #if ($tempvar[0] =~ /\[(\d+\.\d+\.\d+\.\d+)\].*?->\[(\d+\.\d+\.\d+\.\d+)\]/) { $tempvar[0] = $1; } } # Clean again as we should now how an IP address or host name $tempvar[0] =~ s/[^a-zA-Z0-9.\-_:]//g; # Sanitize / Remove most non alphanumeric characters # IPv6_TODO_DONE chomp($tempvar[1]=<$input>); # ip address push(@rawtrap, $tempvar[1]); if ($ipv6_enable == 1) { $tempvar[1] =~ s/[^a-zA-Z0-9.\-_:\[\]%]//g; # Sanitize / Remove most non alphanumeric characters } else { $tempvar[1] =~ s/[^a-zA-Z0-9.\-_:\[\]]//g; # Sanitize / Remove most non alphanumeric characters } # Example: UDP: [127.0.0.1]:38249->[127.0.0.1]:162 # Should only be IPs, but also allow for hostname characters, just in case. if ($tempvar[1] eq "") { if ($DEBUGGING >= 1) { print " Invalid trap file. Expected an IP address on line 3 but got nothing\n"; return 0; } } # IPv6_TODO_DONE # Some systems pass the IP address as udp:ipaddress:portnumber. This will pull # out just the IP address if ($ipv6_enable == 1) { if (($tempvar[1] =~ /(\d+\.\d+\.\d+\.\d+)/) && !($tempvar[1] =~ /(.*?:.*)/) ) { # IPv4 $tempvar[1] = $1; if ($DEBUGGING >= 2) { print " IPv4 detected for value 1\n"; } } elsif ($tempvar[1] =~ /\[(.*?)\]/) { # IPv6 # Expecting UDP/IPv6: [fe80::ec21:4113:ff2b:1234%ens160]:47685 if ($DEBUGGING >= 2) { print " IPv6 detected for value 1\n"; } $tempvar[1] = $1; # Remove zone index from IPv6 address if ($tempvar[1] =~ /(.*?)%.*/) { $tempvar[1] = $1; } } elsif ($tempvar[1] =~ /(.*?:.*)/) { # IPv6 # Expecting: fe80::ec21:4113:ff2b:1234%ens160 if ($DEBUGGING >= 2) { print " IPv6 detected for value 1\n"; } $tempvar[1] = $1; if ($tempvar[1] =~ /(.*?)%.*/) { $tempvar[1] = $1; } } } else { $tempvar[1] =~ /(\d+\.\d+\.\d+\.\d+)/; $tempvar[1] = $1; } # Clean again as we should now how an IP address or host name $tempvar[1] =~ s/[^a-zA-Z0-9.\-_:\[\]]//g; # Sanitize / Remove most non alphanumeric characters # Net-SNMP 5.4 has a bug which gives for the hostname if ($tempvar[0] =~ /^UNKNOWN$/) { $tempvar[0] = $tempvar[1]; } #Process varbinds #Separate everything out, keeping both the variable name and the value my $linenum = 1; while (defined(my $line = <$input>)) { push(@rawtrap, $line); if ($remove_backslash_from_quotes == 1) # Remove escape from quotes if enabled { $line =~ s/\\\"/"/g; } my $temp1; my $temp2; ($temp1, $temp2) = split (/ /, $line, 2); chomp ($temp1); # Variable NAME chomp ($temp2); # Variable VALUE chomp ($line); my $variable_fix; if ($linenum == 1) { if (defined($temp2) ) # Check if line 1 contains 'variable value' or just 'value' { $variable_fix = 0; } else { $variable_fix = 1; } } if ($variable_fix == 0 ) # We received variable name and value { # Clean up variable NAME $temp1 =~ s/[^a-zA-Z0-9.:\-_]//g; # Sanitize / Remove most non alphanumeric characters # Examples: # .1.3.6.1.2.1.1.3.0 # SNMPv2-SMI::enterprises.9.9.187.0.1 .... # OIDs can have -'s. _'s NOT permitted by Net-SNMP, but leave it in anyway # Make sure variable names are numerical $temp1 = translate_symbolic_to_oid($temp1); # If line begins with a double quote (") but does not END in a double quote then we need to merge # the following lines together into one until we find the closing double quote. Allow for escaped quotes. # Net-SNMP sometimes divides long lines into multiple lines.. if ( ($temp2 =~ /^\"/) && ( ! ($temp2 =~ /[^\\]\"$/)) ) { if ($DEBUGGING >= 2) { print " Multi-line value detected - merging onto one line...\n"; } $temp2 =~ s/[\r\n]//g; # Remove newline characters while (defined(my $line2 = <$input>)) { chomp $line2; push(@rawtrap, $line2); $temp2 .= " " . $line2; if (($line2 =~ /\"$/) && ($line2 !~ /\\\"$/)) # Ends in a non-escaped quote or it's a single line with a quote. { last; } } } # If the value is blank, set it to (null) if ($temp2 eq "") { $temp2 = "(null)"; } if ($temp2 =~ /^\"/ && $temp2 =~ /"$/) # Have quotes around it? { $temp2 = substr($temp2,1,(length($temp2)-2)); # Remove quotes } # Clean up variable VALUE $temp2 =~ s(`)(')g; #` Replace any back ticks with regular single quote $temp2 =~ s/[^a-zA-Z0-9 !"#%'()+,\-_.:\/=\@{}~\\]//g; # Sanitize / Remove most non alphanumeric characters push(@tempvar, $temp1); push(@tempvar, $temp2); } else # We received only variable value { # Should have been variable value, but only value found. Workaround # # Normally it is expected that a line contains a variable name # followed by a space followed by the value (except for the # first line which is the hostname and the second which is the # IP address). If there is no variable name on the line (only # one string), then add a variable string called 'variable' so # it is handled correctly in the next section. # This happens with ucd-snmp v4.2.3 but not v4.2.1 or v4.2.5. # This appears to be a bug in ucd-snmp v4.2.3. This works around # the problem by using 'variable' for the variable name, although # v4.2.3 should NOT be used with SNMPTT as it prevents SNMP V2 traps # from being handled correctly. $temp2 = $temp1; $temp1 = "variable"; if ($DEBUGGING >= 2) { print "Data passed from snmptrapd is incorrect. UCD-SNMP v4.2.3 is known to cause this\n"; } # If line begins with a double quote (") but does not END in a double quote then we need to merge # the following lines together into one until we find the closing double quote. Allow for escaped quotes. # Net-SNMP sometimes divides long lines into multiple lines.. if ( ($temp2 =~ /^\"/) && ( ! ($temp2 =~ /[^\\]\"$/)) ) { if ($DEBUGGING >= 2) { print " Multi-line value detected - merging onto one line...\n"; } $temp2 =~ s/[\r\n]//g; # Remove newline characters while (defined(my $line2 = <$input>)) { chomp $line2; push(@rawtrap, $line2); $temp2 .= " " . $line2; if (($line2 =~ /\"$/) && ($line2 !~ /\\\"$/)) # Ends in a non-escaped quote or it's a single line with a quote. { last; } } } # If the value is blank, set it to (null) if ($temp2 eq "") { $temp2 = "(null)"; } if ($temp2 =~ /^\"/ && $temp2 =~ /"$/) # Have quotes around it? { $temp2 = substr($temp2,1,(length($temp2)-2)); # Remove quotes } # Clean up variable VALUE $temp2 =~ s(`)(')g; #` Replace any back ticks with regular single quote $temp2 =~ s/[^a-zA-Z0-9 !"#%'()+,\-_.:\/=\@{}~\\]//g; # Sanitize / Remove most non alphanumeric characters push(@tempvar, $temp1); push(@tempvar, $temp2); } $linenum++; } if ($DEBUGGING >= 2) { # Print out raw trap passed from snmptrapd print "\nRaw trap passed from snmptrapd:\n"; for (my $i=0;$i <= $#rawtrap;$i++) { chomp($rawtrap[$i]); print "$rawtrap[$i]\n"; } # Print out all items passed from snmptrapd print "\nItems passed from snmptrapd:\n"; for (my $i=0;$i <= $#tempvar;$i++) { print "value $i: $tempvar[$i]\n\n"; } } # Copy what I need to new variables to make it easier to manipulate later # Standard variables $var[0] = $tempvar[0]; # hostname $var[1] = $tempvar[1]; # ip address $var[2] = $tempvar[3]; # uptime $var[3] = $tempvar[5]; # trapname / OID - assume first value after uptime is # the trap OID (value for .1.3.6.1.6.3.1.1.4.1.0) $var[4] = ""; # Clear ip address from trap agent $var[5] = ""; # Clear trap community string $var[6] = ""; # Clear enterprise $var[7] = ""; # Clear securityEngineID $var[8] = ""; # Clear securityName $var[9] = ""; # Clear contextEngineID $var[10] = ""; # Clear contextName # Make sure trap OID is numerical as event lookups are done using numerical OIDs only $var[3] = translate_symbolic_to_oid($var[3]); # Cycle through remaining variables searching for for agent IP (.1.3.6.1.6.3.18.1.3.0), # community name (.1.3.6.1.6.3.18.1.4.0) and enterpise (.1.3.6.1.6.3.1.1.4.3.0) # All others found are regular passed variables my $j=0; for (my $i=6;$i <= $#tempvar; $i+=2) { if ($tempvar[$i] =~ /^.1.3.6.1.6.3.18.1.3.0$/) # ip address from trap agent { $var[4] = $tempvar[$i+1]; # IPv6_TODO_DONE # Some systems pass the IP address as IpAddress:iaddress. This will pull # out just the IP address. Bug 27. if ($ipv6_enable == 1) { if (($var[4] =~ /(\d+\.\d+\.\d+\.\d+)/) && !($var[4] =~ /(.*?:.*)/) ) { # IPv4 $var[4] = $1; } elsif ($var[4] =~ /(.*?\:.*)/) { # IPv6 print "IPv6 detected\n"; $var[4] = $1; # Remove zone index from IPv6 address if ($var[4] =~ /(.*?)%.*/) { $var[4] = $1; } } else { $var[4] = ""; } } else { if ($var[4] =~ /(\d+\.\d+\.\d+\.\d+)/) { $var[4] = $1; } } } elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.18.1.4.0$/) # trap community string { $var[5] = $tempvar[$i+1]; } elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.1.1.4.3.0$/) # enterprise { # $var[6] = $tempvar[$i+1]; # Make sure enterprise value is numerical $var[6] = translate_symbolic_to_oid($tempvar[$i+1]); } elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.10.2.1.1.0$/) # securityEngineID { $var[7] = $tempvar[$i+1]; } elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.18.1.1.1.3$/) # securityName { $var[8] = $tempvar[$i+1]; } elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.18.1.1.1.4$/) # contextEngineID { $var[9] = $tempvar[$i+1]; } elsif ($tempvar[$i] =~ /^.1.3.6.1.6.3.18.1.1.1.5$/) # contextName { $var[10] = $tempvar[$i+1]; } else # application specific variables { $entvarname[$j] = $tempvar[$i]; $entvar[$j] = $tempvar[$i+1]; $j++; } } #if ($dns_enable == 1 && $var[0] =~ /^\d+\.\d+\.\d+\.\d+$/) # Only if it's not already resolved if ($dns_enable == 1 && ($var[0] =~ /^\d+\.\d+\.\d+\.\d+$/ || $var[0] =~ /\:/)) # Only if it's not already resolved { # IPv6_TODO_DONE #my $temp = gethostbyaddr(Socket::inet_aton($var[0]),Socket::AF_INET()); my $temp = ip_to_name($var[0]); if (defined ($temp)) { if ($DEBUGGING >= 1) { print "Host IP address ($var[0]) resolved to: $temp\n\n"; } $var[0] = $temp; } else { if ($DEBUGGING >= 1) { print "Host IP address ($var[0]) could not be resolved by DNS. Variable \$r / \$R etc will use the IP address\n\n"; } } } # IPv6_TODO_DONE # If the agent IP is blank, copy the IP from the host IP. # var[4] would only be blank if it wasn't passed from snmptrapd, which # should only happen with ucd-snmp 4.2.3, which you shouldn't be using anyway! if ($var[4] eq '') { $var[4] = $var[1]; if ($DEBUGGING >= 1) { print "Agent IP address was blank, so setting to the same as the host IP address of $var[1]\n\n"; } } # IPv6_TODO_DONE # If the agent IP is the same as the host IP, then just use the host DNS name, no need # to look up, as it's obviously the same.. if ($var[4] eq $var[1]) { if ($DEBUGGING >= 1) { print "Agent IP address ($var[4]) is the same as the host IP, so copying the host name: $var[0]\n\n"; } $agent_dns_name = $var[0]; } else { $agent_dns_name = $var[4]; # Default to IP address if ($dns_enable == 1 && $var[4] ne '') { #my $temp = gethostbyaddr(Socket::inet_aton($var[4]),Socket::AF_INET()); my $temp = ip_to_name($var[4]); if (defined ($temp)) { if ($DEBUGGING >= 1) { print "Agent IP address ($var[4]) resolved to: $temp\n\n"; } $agent_dns_name = $temp; } else { if ($DEBUGGING >= 1) { print "Agent IP address ($var[4]) could not be resolved by DNS. Variable \$A etc will use the IP address\n\n"; } } } } if ($strip_domain) { $var[0] = strip_domain_name($var[0], $strip_domain); $agent_dns_name = strip_domain_name($agent_dns_name, $strip_domain); } if ($DEBUGGING >= 1) { print "Trap received from $tempvar[0]: $tempvar[5]\n"; } if ($DEBUGGING >= 2) { print "0: hostname\n"; print "1: ip address\n"; print "2: uptime\n"; print "3: trapname / OID\n"; print "4: ip address from trap agent\n"; print "5: trap community string\n"; print "6: enterprise\n"; print "7: securityEngineID (snmptthandler-embedded required)\n"; print "8: securityName (snmptthandler-embedded required)\n"; print "9: contextEngineID (snmptthandler-embedded required)\n"; print "10: contextName (snmptthandler-embedded required)\n"; print "0+: passed variables\n\n"; #print out all standard variables for (my $i=0;$i <= $#var;$i++) { print "Value $i: $var[$i]\n\n"; } print "Agent dns name: $agent_dns_name\n\n"; #print out all enterprise specific variables for (my $i=0;$i <= $#entvar;$i++) { print "Ent Value $i (\$" . ($i+1) . "): $entvarname[$i]=$entvar[$i]\n\n"; } } # Generate hash of trap and detect duplicates if ($duplicate_trap_window) { my $md5 = Digest::MD5->new; # All variables except for uptime. $md5->add($var[0],$var[1].$var[3].$var[4].$var[5].$var[6].$var[7].$var[8].$var[9].$var[10]."@entvar"); my $trap_digest = $md5->hexdigest; if ($DEBUGGING >= 2) { print "Trap digest: $trap_digest\n"; } if ($duplicate_traps{$trap_digest}) { # Duplicate trap detected. Skipping trap... return -1; } $duplicate_traps{$trap_digest} = time(); } return 1; # Variables of trap received by SNMPTRAPD: # # $var[0] hostname # $var[1] ip address # $var[2] uptime # $var[3] trapname / OID # $var[4] ip address from trap agent # $var[5] trap community string # $var[6] enterprise # $var[7] securityEngineID (snmptthandler-embedded required) # $var[8] securityName (snmptthandler-embedded required) # $var[9] contextEngineID (snmptthandler-embedded required) # $var[10] contextName (snmptthandler-embedded required) # # $entvarname[0] passed variable name 1 # $entvarname[1] passed variable name 2 # # $entvar[0] passed variable 1 # $entvar[1] passed variable 2 # . # . # etc.. # ############################################################################## } sub searchfortrap { # Variables of trap received by SNMPTRAPD: # # $var[0] hostname # $var[1] ip address # $var[2] uptime # $var[3] trapname / OID # $var[4] ip address from trap agent # $var[5] trap community string # $var[6] enterprise # $var[7] securityEngineID (snmptthandler-embedded required) # $var[8] securityName (snmptthandler-embedded required) # $var[9] contextEngineID (snmptthandler-embedded required) # $var[10] contextName (snmptthandler-embedded required) # # $entvarname[0] passed variable name 1 # $entvarname[1] passed variable name 2 # # $entvar[0] passed variable 1 # $entvar[1] passed variable 2 # . # . # etc.. # # $event hash for each trapped defined in snmptt.conf with following values: # # 0: name of trap # 1: category # 2: severity # 3: FORMAT string # 4: EXEC string # 5: NODES string # 6: REGEX array # 7: MATCH array # 9: DESC array # # Example: To access FORMAT string for received trap: # $event{"$var[3]"}[3] # # or # # $event{"$receivedtrap"}[3] ############################################################################### # Check to see if received trap is defined in the snmptt.conf, and perform variable # substitution, logging etc if found # Look for matching OID $processed = 0; $skipped = 0; $receivedtrap = $var[3]; $receivedtrap_entry = $receivedtrap; # Will be used to processtrap to lookup EVENT info # Changed to actual EVENT entry if using a wildcard $trap_attempted_to_log = 0; $trap_successfully_logged = 0; # Check for exact match if (exists $event{"$receivedtrap"}) { if ($DEBUGGING >= 1) { print "Exact match of trap found in EVENT hash table\n\n"; } processtrap(); } else { if ($DEBUGGING >= 1) { print "Exact match of trap NOT found in EVENT hash table\n\n"; } } # Check for wildcard match in hash table if not already processed if ($processed == 0 && $skipped == 0) { my $receivedtraptemp = $receivedtrap; my $counter = 0; # Drill down only 40 times. Should never need this, but # it's here to prevent an infinite loop in this while statement # just in case if ($DEBUGGING >= 1) { print "Looking for wildcards in the EVENT hash table\n"; } while ($counter <= 40 && $receivedtraptemp ne '') { # Remove last digit of recevied trap, add a * and look for match $receivedtraptemp =~ s/(.*)\.\d+$/$1/; if ($DEBUGGING >= 2) { print "Drilling down looking for wildcards in the EVENT hash table\n"; print $receivedtraptemp.".\*\n\n"; } if (exists $event{$receivedtraptemp.'.*'}) { $receivedtrap_entry = $receivedtraptemp.'.*'; # Set $receivedtrap to matching trap with wildcard processtrap(); last; # found, so stop this while loop } $counter++; } } # Log to unknowntraplog etc if no match was found if ($processed == 0 && ($unknown_trap_nodes_match_mode == 0 || ($unknown_trap_nodes_match_mode != 0 && $skipped == 0)) ) { if ($DEBUGGING >= 1) { print "\n\nTrap not defined...\n\n"; } # Statistics $g_total_traps_unknown++; # # unknown_trap_PREexec # if (@unknown_trap_preexec) { for (my $i=0;$i <= $#unknown_trap_preexec;$i++) { my $j=$i+1; if ($DEBUGGING >= 1) { print "\n\nUnknown trap PREEXEC line #$j:$unknown_trap_preexec[$i]\n"; print " Performing variable substitution:\n"; } my $command; $_ = $unknown_trap_preexec[$i]; substitute(); $command = $_; if ($DEBUGGING >= 1) { print "\nUnknown trap variable substituted PREEXEC command #$j:$command\n"; } # Execute command if ($exec_escape == 1) { # Escape wildcard characters $command =~ s/\*/\\\*/g; $command =~ s/\?/\\\?/g; } my $result = `$command`; chomp $result; # Remove spaces before and after $result =~ /^\s*(.*?)\s*$/; $result = $1; if ($result eq '') { $result = "(no output from unknown PREEXEC #$j:)\n"; } print "\nUnknown trap variable substituted PREEXEC command output #$j:\n $result\n"; push(@unknown_trap_preexec_result, $result); } } # # unknown_trap_exec # if ($unknown_trap_exec ne "") { if ($DEBUGGING >= 1) { print "\n\nUnknown trap EXEC line: $unknown_trap_exec\n"; } my $command; if ($unknown_trap_exec_format ne "") { $_ = $unknown_trap_exec_format; substitute(); $command = $unknown_trap_exec . " \"$_\""; } else { $command = $unknown_trap_exec . " \""; $command .= scalar($trap_date_time) . ": Unknown trap ($var[3]) received from $var[0] at:"; #print out all standard variables for (my $i=0;$i <= $#var;$i++) { $command .= " Value $i: $var[$i]"; } #print out all enterprise specific variables for (my $i=0;$i <= $#entvar;$i++) { $command .= " Ent Value $i: $entvarname[$i]=$entvar[$i]"; } $command .= "\""; } if ($DEBUGGING >= 1) { print "Unknown trap EXEC command:$command\n"; } # Execute command if ($exec_escape == 1) { # Escape wildcard characters $command =~ s/\*/\\\*/g; $command =~ s/\?/\\\?/g; } system $command; } # Clear unknown trap PREEXEC result so it can't be used anywhere else @unknown_trap_preexec_result = (); if ($unknown_trap_log_enable == 1) { if ($unknown_trap_log_file ne "") { my $fh_LOGFILE; if (open $fh_LOGFILE, ">>", $unknown_trap_log_file) { print $fh_LOGFILE scalar($trap_date_time),": Unknown trap ($var[3]) received from $var[0] at: \n"; #print out all standard variables for (my $i=0;$i <= $#var;$i++) { print $fh_LOGFILE "Value $i: $var[$i]\n"; } #print out all enterprise specific variables for (my $i=0;$i <= $#entvar;$i++) { print $fh_LOGFILE "Ent Value $i: $entvarname[$i]=$entvar[$i]\n"; } print $fh_LOGFILE "\n\n"; close $fh_LOGFILE; } else { warn "Can not open log_file: $!"; } } my $message_short; if ( ($mysql_dbi_enable == 1) || ($postgresql_dbi_enable == 1) || ($dbd_odbc_enable == 1) || ($sql_win32_odbc_enable == 1) ) { if ($DEBUGGING >= 1) { print "Logging unknown trap to SQL\n"; } # Translate if enabled, and if we can $receivedtrap_trans = translate_log_trap_oid_sub($receivedtrap); $enterprise_trans = translate_enterprise_oid_format_sub($var[6]); if ($net_snmp_perl_enable == 1 && $db_translate_enterprise == 1) { $db_enterprise = $enterprise_trans; } else { $db_enterprise = $var[6]; } $_ = $db_unknown_trap_format; substitute(); $message_short = $_; } if ($mysql_dbi_enable == 1 && defined ($dbh_mysql) && $mysql_dbi_table_unknown ne "") { # Backslash any quotes my $message_short2 = $message_short; $message_short2 =~ s(\')(\\\')g; #' $message_short2 =~ s(\")(\\\")g; #" my $community = $var[5]; $community =~ s(\')(\\\')g; #' $community =~ s(\")(\\\")g; #" my @t_sql_custom_columns_unknown = (); if (@sql_custom_columns_unknown) { @t_sql_custom_columns_unknown = @sql_custom_columns_unknown; for (my $i = 1; $i <= $#t_sql_custom_columns_unknown; $i+=2) { $_ = $t_sql_custom_columns_unknown[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns_unknown[$i] = $_; } } mysql_insert($mysql_dbi_table_unknown, "trapoid", $receivedtrap_trans, "enterprise", $db_enterprise, "community", $community, "hostname", $agent_dns_name, "agentip", $var[4], "uptime", $var[2], "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns_unknown); } if ($postgresql_dbi_enable == 1 && defined ($dbh_postgresql) && $postgresql_dbi_table_unknown ne "") { # Backslash any quotes my $message_short2 = $message_short; $message_short2 =~ s(\')(\\\')g; #' $message_short2 =~ s(\")(\\\")g; #" my $community = $var[5]; $community =~ s(\')(\\\')g; #' $community =~ s(\")(\\\")g; #" my @t_sql_custom_columns_unknown = (); if (@sql_custom_columns_unknown) { @t_sql_custom_columns_unknown = @sql_custom_columns_unknown; for (my $i = 1; $i <= $#t_sql_custom_columns_unknown; $i+=2) { $_ = $t_sql_custom_columns_unknown[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns_unknown[$i] = $_; } } postgresql_insert($postgresql_dbi_table_unknown, "trapoid", $receivedtrap_trans, "enterprise", $db_enterprise, "community", $community, "hostname", $agent_dns_name, "agentip", $var[4], "uptime", $var[2], "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns_unknown); } if ($dbd_odbc_enable == 1 && defined ($dbh_odbc) && $dbd_odbc_table_unknown ne "") { # Double any single quotes my $message_short2 = $message_short; $message_short2 =~ s(\')('')g; #' my $community = $var[5]; $community =~ s(\')('')g; #' my @t_sql_custom_columns_unknown = (); if (@sql_custom_columns_unknown) { @t_sql_custom_columns_unknown = @sql_custom_columns_unknown; for (my $i = 1; $i <= $#t_sql_custom_columns_unknown; $i+=2) { $_ = $t_sql_custom_columns_unknown[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns_unknown[$i] = $_; } } odbc_insert($dbd_odbc_table_unknown, "trapoid", $receivedtrap_trans, "enterprise", $db_enterprise, "community", $community, "hostname", $agent_dns_name, "agentip", $var[4], "uptime", $var[2], "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns_unknown); } if ($sql_win32_odbc_enable == 1 && defined ($dbh_win32_odbc) && $sql_win32_odbc_table_unknown ne "") { # Double any single quotes my $message_short2 = $message_short; $message_short2 =~ s(\')('')g; #' my $community = $var[5]; $community =~ s(\')('')g; #' my @t_sql_custom_columns_unknown = (); if (@sql_custom_columns_unknown) { @t_sql_custom_columns_unknown = @sql_custom_columns_unknown; for (my $i = 1; $i <= $#t_sql_custom_columns_unknown; $i+=2) { $_ = $t_sql_custom_columns_unknown[$i]; print "Performing substitution on custom column: $_\n"; substitute(); print "Done performing substitution on custom column: $_\n"; $t_sql_custom_columns_unknown[$i] = $_; } } sql_win32_odbc_insert($sql_win32_odbc_table_unknown, "trapoid", $receivedtrap_trans, "enterprise", $db_enterprise, "community", $community, "hostname", $agent_dns_name, "agentip", $var[4], "uptime", $var[2], "traptime", $trap_date_time_sql, "formatline", $message_short2, @t_sql_custom_columns_unknown); } } } # If keep_unlogged_traps is set to 0, or if we didn't even try to log, set it as a successful log # so the trap file gets deleted from the spool dir (the trap wasn't found, or a node didn't match) if ($trap_attempted_to_log == 0 || $keep_unlogged_traps == 0) { $trap_successfully_logged = 1; } elsif ($keep_unlogged_traps == 1) # Delete spool if ONE successfully logged { if ($trap_successfully_logged >= 1) { $trap_successfully_logged = 1; } else { $trap_successfully_logged = 0; } } elsif ($keep_unlogged_traps == 2) # Delete spool if ALL successfully logged { if ($trap_successfully_logged == $trap_attempted_to_log) { $trap_successfully_logged = 1; } else { $trap_successfully_logged = 0; } } } sub findsnmpttini { ############################################################################## # Load config file start # # For Linux / Unix, try in order: # /etc/snmptt/snmptt.ini # /etc/snmp/snmptt.ini # /etc/snmptt.ini # /usr/local/etc/snmp/snmptt.ini # /usr/local/etc/snmptt.ini # # # For Windows, try %SystemRoot%\snmptt.ini only. # if ($ini ne '') { $configfile = $ini; } else { if ($^O ne "MSWin32") { my $fh_CONFIG; $configfile = '/etc/snmptt/snmptt.ini'; if( open( $fh_CONFIG, '<', '/etc/snmptt/snmptt.ini' ) ) { $configfile = '/etc/snmptt/snmptt.ini'; close $fh_CONFIG; } elsif( open( $fh_CONFIG, '<', '/etc/snmp/snmptt.ini' ) ) { $configfile = '/etc/snmp/snmptt.ini'; close $fh_CONFIG; } elsif ( open( $fh_CONFIG, '<', '/etc/snmptt.ini' ) ) { $configfile = '/etc/snmptt.ini'; close $fh_CONFIG; } elsif ( open( $fh_CONFIG, '<', '/usr/local/etc/snmp/snmptt.ini' ) ) { $configfile = '/usr/local/etc/snmp/snmptt.ini'; close $fh_CONFIG; } elsif ( open( $fh_CONFIG, '<', '/usr/local/etc/snmptt.ini' ) ) { $configfile = '/usr/local/etc/snmptt.ini'; close $fh_CONFIG; } } else { $configfile = $ENV{'SystemRoot'}."\\snmptt.ini"; } } } sub loadsnmpttini { ############################################################################## # # Load config file start # # ############################################################################## use Config::IniFiles; my $cfg; my $fh_CONFIG; if( open( $fh_CONFIG, '<', $configfile ) ) { close $fh_CONFIG; $cfg = new Config::IniFiles( -file => $configfile); if ($DEBUGGING >= 1) { print "Config file $configfile loaded\n"; } } else { if ($DEBUGGING >= 0) { print "Config file ($configfile) could not be loaded\n"; } exit(1); } if (! $cfg) { if ($DEBUGGING >= 0) { print "Error in config file - please check the syntax in the config file\n"; } exit(1); } @snmptt_conf_files = ''; # General if ( ($cfg->val('General', 'mode') eq "daemon") || ($daemoncmdline == 1) ) { $daemon = 1; } else { $daemon = 0; } $snmptt_system_name = $cfg->val('General', 'snmptt_system_name'); $multiple_event = $cfg->val('General', 'multiple_event'); $dns_enable = $cfg->val('General', 'dns_enable'); $strip_domain = $cfg->val('General', 'strip_domain'); @strip_domain_list = $cfg->val('General', 'strip_domain_list'); $net_snmp_perl_enable = $cfg->val('General', 'net_snmp_perl_enable'); $net_snmp_perl_cache_enable = $cfg->val('General', 'net_snmp_perl_cache_enable'); $net_snmp_perl_best_guess = $cfg->val('General', 'net_snmp_perl_best_guess'); $translate_log_trap_oid = $cfg->val('General', 'translate_log_trap_oid'); $translate_value_oids = $cfg->val('General', 'translate_value_oids'); $resolve_value_ip_addresses = $cfg->val('General', 'resolve_value_ip_addresses'); $translate_enterprise_oid_format = $cfg->val('General', 'translate_enterprise_oid_format'); $translate_trap_oid_format = $cfg->val('General', 'translate_trap_oid_format'); $translate_varname_oid_format = $cfg->val('General', 'translate_varname_oid_format'); $translate_integers = $cfg->val('General', 'translate_integers'); $wildcard_expansion_separator = $cfg->val('General', 'wildcard_expansion_separator'); $mibs_environment = $cfg->val('General', 'mibs_environment'); $allow_unsafe_regex = $cfg->val('General', 'allow_unsafe_regex'); $remove_backslash_from_quotes = $cfg->val('General', 'remove_backslash_from_quotes'); $dynamic_nodes = $cfg->val('General', 'dynamic_nodes'); $description_mode = $cfg->val('General', 'description_mode'); $description_clean = $cfg->val('General', 'description_clean'); $threads_enable = $cfg->val('General', 'threads_enable'); $threads_max = $cfg->val('General', 'threads_max'); $ipv6_enable = $cfg->val('General', 'ipv6_enable'); $date_format = $cfg->val('General', 'date_format'); $time_format = $cfg->val('General', 'time_format'); $date_time_format = $cfg->val('General', 'date_time_format'); # DaemonMode $daemon_fork = $cfg->val('DaemonMode', 'daemon_fork'); $daemon_uid = $cfg->val('DaemonMode', 'daemon_uid'); $pid_file = $cfg->val('DaemonMode', 'pid_file'); $spool_directory = $cfg->val('DaemonMode', 'spool_directory'); $sleep = $cfg->val('DaemonMode', 'sleep'); $use_trap_time = $cfg->val('DaemonMode', 'use_trap_time'); $keep_unlogged_traps = $cfg->val('DaemonMode', 'keep_unlogged_traps'); $duplicate_trap_window = $cfg->val('DaemonMode', 'duplicate_trap_window'); # Logging $stdout_enable = $cfg->val('Logging', 'stdout_enable'); $log_enable = $cfg->val('Logging', 'log_enable'); $log_format = $cfg->val('Logging', 'log_format'); $log_file = $cfg->val('Logging', 'log_file'); $log_system_enable = $cfg->val('Logging', 'log_system_enable'); $log_system_file = $cfg->val('Logging', 'log_system_file'); $unknown_trap_log_enable = $cfg->val('Logging', 'unknown_trap_log_enable'); $unknown_trap_log_file = $cfg->val('Logging', 'unknown_trap_log_file'); $unknown_trap_nodes_match_mode = $cfg->val('Logging', 'unknown_trap_nodes_match_mode'); $statistics_interval = $cfg->val('Logging', 'statistics_interval'); $syslog_enable = $cfg->val('Logging', 'syslog_enable'); $syslog_format = $cfg->val('Logging', 'syslog_format'); $syslog_module = $cfg->val('Logging', 'syslog_module'); $syslog_remote_dest = $cfg->val('Logging', 'syslog_remote_dest'); $syslog_remote_port = $cfg->val('Logging', 'syslog_remote_port'); $syslog_remote_proto = $cfg->val('Logging', 'syslog_remote_proto'); $syslog_app = $cfg->val('Logging', 'syslog_app'); $syslog_rfc_format = $cfg->val('Logging', 'syslog_rfc_format'); $syslog_facility = $cfg->val('Logging', 'syslog_facility'); @syslog_level_alert = $cfg->val('Logging', 'syslog_level_alert'); @syslog_level_crit = $cfg->val('Logging', 'syslog_level_crit'); @syslog_level_err = $cfg->val('Logging', 'syslog_level_err'); @syslog_level_warning = $cfg->val('Logging', 'syslog_level_warning'); @syslog_level_notice = $cfg->val('Logging', 'syslog_level_notice'); @syslog_level_info = $cfg->val('Logging', 'syslog_level_info'); @syslog_level_debug = $cfg->val('Logging', 'syslog_level_debug'); $syslog_level = $cfg->val('Logging', 'syslog_level'); $syslog_system_enable = $cfg->val('Logging', 'syslog_system_enable'); $syslog_system_app = $cfg->val('Logging', 'syslog_system_app'); $syslog_system_facility = $cfg->val('Logging', 'syslog_system_facility'); $syslog_system_level = $cfg->val('Logging', 'syslog_system_level'); $eventlog_enable = $cfg->val('Logging', 'eventlog_enable'); @eventlog_type_information = $cfg->val('Logging', 'eventlog_type_information'); @eventlog_type_warning = $cfg->val('Logging', 'eventlog_type_warning'); @eventlog_type_error = $cfg->val('Logging', 'eventlog_type_error'); $eventlog_type = $cfg->val('Logging', 'eventlog_type'); $eventlog_system_enable = $cfg->val('Logging', 'eventlog_system_enable'); # Exec $exec_enable = $cfg->val('Exec', 'exec_enable'); $pre_exec_enable = $cfg->val('Exec', 'pre_exec_enable'); @unknown_trap_preexec = $cfg->val('Exec', 'unknown_trap_preexec'); $unknown_trap_exec = $cfg->val('Exec', 'unknown_trap_exec'); $unknown_trap_exec_format = $cfg->val('Exec', 'unknown_trap_exec_format'); $exec_escape = $cfg->val('Exec', 'exec_escape'); # SQL $db_translate_enterprise = $cfg->val('SQL', 'db_translate_enterprise'); $db_unknown_trap_format = $cfg->val('SQL', 'db_unknown_trap_format'); $mysql_dbi_enable = $cfg->val('SQL', 'mysql_dbi_enable'); $mysql_dbi_host = $cfg->val('SQL', 'mysql_dbi_host'); $mysql_dbi_port = $cfg->val('SQL', 'mysql_dbi_port'); $mysql_dbi_database = $cfg->val('SQL', 'mysql_dbi_database'); $mysql_dbi_table = $cfg->val('SQL', 'mysql_dbi_table'); $mysql_dbi_table_unknown = $cfg->val('SQL', 'mysql_dbi_table_unknown'); $mysql_dbi_table_statistics = $cfg->val('SQL', 'mysql_dbi_table_statistics'); $mysql_dbi_username = $cfg->val('SQL', 'mysql_dbi_username'); $mysql_dbi_password = $cfg->val('SQL', 'mysql_dbi_password'); $mysql_ping_on_insert = $cfg->val('SQL', 'mysql_ping_on_insert'); $mysql_ping_interval = $cfg->val('SQL', 'mysql_ping_interval'); $postgresql_dbi_enable = $cfg->val('SQL', 'postgresql_dbi_enable'); $postgresql_dbi_module = $cfg->val('SQL', 'postgresql_dbi_module'); $postgresql_dbi_hostport_enable = $cfg->val('SQL', 'postgresql_dbi_hostport_enable'); $postgresql_dbi_host = $cfg->val('SQL', 'postgresql_dbi_host'); $postgresql_dbi_port = $cfg->val('SQL', 'postgresql_dbi_port'); $postgresql_dbi_database = $cfg->val('SQL', 'postgresql_dbi_database'); $postgresql_dbi_table = $cfg->val('SQL', 'postgresql_dbi_table'); $postgresql_dbi_table_unknown = $cfg->val('SQL', 'postgresql_dbi_table_unknown'); $postgresql_dbi_table_statistics = $cfg->val('SQL', 'postgresql_dbi_table_statistics'); $postgresql_dbi_username = $cfg->val('SQL', 'postgresql_dbi_username'); $postgresql_dbi_password = $cfg->val('SQL', 'postgresql_dbi_password'); $postgresql_ping_on_insert = $cfg->val('SQL', 'postgresql_ping_on_insert'); $postgresql_ping_interval = $cfg->val('SQL', 'postgresql_ping_interval'); $dbd_odbc_enable = $cfg->val('SQL', 'dbd_odbc_enable'); $dbd_odbc_dsn = $cfg->val('SQL', 'dbd_odbc_dsn'); $dbd_odbc_table = $cfg->val('SQL', 'dbd_odbc_table'); $dbd_odbc_table_unknown = $cfg->val('SQL', 'dbd_odbc_table_unknown'); $dbd_odbc_table_statistics = $cfg->val('SQL', 'dbd_odbc_table_statistics'); $dbd_odbc_username = $cfg->val('SQL', 'dbd_odbc_username'); $dbd_odbc_password = $cfg->val('SQL', 'dbd_odbc_password'); $dbd_odbc_ping_on_insert = $cfg->val('SQL', 'dbd_odbc_ping_on_insert'); $dbd_odbc_ping_interval = $cfg->val('SQL', 'dbd_odbc_ping_interval'); $sql_win32_odbc_enable = $cfg->val('SQL', 'sql_win32_odbc_enable'); $sql_win32_odbc_dsn = $cfg->val('SQL', 'sql_win32_odbc_dsn'); $sql_win32_odbc_table = $cfg->val('SQL', 'sql_win32_odbc_table'); $sql_win32_odbc_table_unknown = $cfg->val('SQL', 'sql_win32_odbc_table_unknown'); $sql_win32_odbc_table_statistics = $cfg->val('SQL', 'sql_win32_odbc_table_statistics'); $sql_win32_odbc_username = $cfg->val('SQL', 'sql_win32_odbc_username'); $sql_win32_odbc_password = $cfg->val('SQL', 'sql_win32_odbc_password'); @sql_custom_columns = $cfg->val('SQL', 'sql_custom_columns'); @sql_custom_columns_unknown = $cfg->val('SQL', 'sql_custom_columns_unknown'); $date_time_format_sql = $cfg->val('SQL', 'date_time_format_sql'); $stat_time_format_sql = $cfg->val('SQL', 'stat_time_format_sql'); # Debugging if ($debugcmdline == 0) { $DEBUGGING = $cfg->val('Debugging', 'DEBUGGING'); } if ($debugfilecmdline == 0) { $DEBUGGING_FILE = $cfg->val('Debugging', 'DEBUGGING_FILE'); } # TrapFiles @snmptt_conf_files = $cfg->val('TrapFiles', 'snmptt_conf_files'); $cfg->Delete; # # Defaults Start # if ($snmptt_system_name eq "") { if (hostname ne "") { $snmptt_system_name = hostname; } } if (! defined ($multiple_event)) { $multiple_event = 0} ; if (! defined ($dns_enable)) { $dns_enable = 0} ; if (! defined ($strip_domain)) { $strip_domain = 0} ; if (! defined ($net_snmp_perl_enable)) { $net_snmp_perl_enable = 0} ; if (! defined ($net_snmp_perl_cache_enable)) { $net_snmp_perl_cache_enable = 1} ; if (! defined ($net_snmp_perl_best_guess)) { $net_snmp_perl_best_guess = 2} ; if (! defined ($translate_log_trap_oid)) { $translate_log_trap_oid = 0} ; if (! defined ($translate_value_oids)) { $translate_value_oids = 0} ; if (! defined ($resolve_value_ip_addresses)) { $resolve_value_ip_addresses = 0} ; if (! defined ($translate_enterprise_oid_format)) { $translate_enterprise_oid_format = 1} ; if (! defined ($translate_trap_oid_format)) { $translate_trap_oid_format = 1} ; if (! defined ($translate_varname_oid_format)) { $translate_varname_oid_format = 1} ; if (! defined ($translate_integers)) { $translate_integers = 0} ; if (! defined ($wildcard_expansion_separator)) { $wildcard_expansion_separator = " "} ; if (! defined ($allow_unsafe_regex)) { $allow_unsafe_regex = 0} ; if (! defined ($remove_backslash_from_quotes)) { $remove_backslash_from_quotes = 0} ; if (! defined ($dynamic_nodes)) { $dynamic_nodes = 0} ; if (! defined ($description_mode)) { $description_mode = 0} ; if (! defined ($description_clean)) { $description_clean = 1} ; if (! defined ($threads_enable)) { $threads_enable = 0} ; if (! defined ($threads_max)) { $threads_max = 10} ; if (! defined ($ipv6_enable)) { $ipv6_enable = 0} ; if (! defined ($date_format)) { $date_format = "%a %b %e %Y"} ; if (! defined ($time_format)) { $time_format = "%H:%M:%S"} ; if (! defined ($date_time_format)) { $date_time_format = ""} ; if (! defined ($date_time_format_sql)) { $date_time_format_sql = ""} ; if (! defined ($stat_time_format_sql)) { $stat_time_format_sql = ""} ; if (! defined ($daemon_fork)) { $daemon_fork = 1} ; if (! defined ($daemon_uid)) { $daemon_uid = ''} ; if (! defined ($pid_file)) { $pid_file = ''} ; if (! defined ($spool_directory)) { $spool_directory = ''} ; if (! defined ($sleep)) { $sleep = 5} ; if (! defined ($use_trap_time)) { $use_trap_time = 1} ; if (! defined ($keep_unlogged_traps)) { $keep_unlogged_traps = 1} ; if (! defined ($duplicate_trap_window)) { $duplicate_trap_window = 0} ; if (! defined ($stdout_enable)) { $stdout_enable = 0} ; if (! defined ($log_enable)) { $log_enable = 1} ; if (! defined ($log_format)) { $log_format = ''} ; if (! defined ($log_file)) { $log_file = ''} ; if (! defined ($log_system_enable)) { $log_system_enable = 0} ; if (! defined ($log_system_file)) { $log_system_file = ''} ; if (! defined ($unknown_trap_log_enable)) { $unknown_trap_log_enable = 0} ; if (! defined ($unknown_trap_log_file)) { $unknown_trap_log_file = ''} ; if (! defined ($unknown_trap_nodes_match_mode)) { $unknown_trap_nodes_match_mode = 0} ; if (! defined ($syslog_enable)) { $syslog_enable = 0} ; if (! defined ($syslog_format)) { $syslog_format = ''} ; if (! defined ($syslog_module)) { $syslog_module = 0} ; if (! defined ($syslog_remote_dest)) { $syslog_remote_dest = 'localhost'} ; if (! defined ($syslog_remote_port)) { $syslog_remote_port = 514} ; if (! defined ($syslog_remote_proto)) { $syslog_remote_proto = 'UDP'} ; if (! defined ($syslog_app)) { $syslog_app = "snmptt"} ; if (! defined ($syslog_rfc_format)) { $syslog_rfc_format = 0} ; if (! defined ($syslog_facility)) { $syslog_facility = 'local0'} ; if (! defined ($statistics_interval)) { $statistics_interval = 0} ; if (! defined ($syslog_level)) { $syslog_level = 'warning'} ; if (! defined ($syslog_system_enable)) { $syslog_system_enable = 0} ; if (! defined ($syslog_system_app)) { $syslog_system_app = 'snmptt-sys'} ; if (! defined ($syslog_system_facility)) { $syslog_system_facility = 'local0'} ; if (! defined ($syslog_system_level)) { $syslog_system_level = 'warning'} ; if (! defined ($eventlog_enable)) { $eventlog_enable = 0} ; if (! defined ($eventlog_enable)) { $eventlog_enable = 'WARNING'} ; if (! defined ($eventlog_system_enable)) { $eventlog_system_enable = 0} ; if (! defined ($exec_enable)) { $exec_enable = 1} ; if (! defined ($pre_exec_enable)) { $pre_exec_enable = 1} ; if (! defined ($unknown_trap_exec)) { $unknown_trap_exec = ''} ; if (! defined ($unknown_trap_exec_format)) { $unknown_trap_exec_format = ''} ; if (! defined ($exec_escape)) { if ($^O =~ /MSWin32/) { $exec_escape = 0; } else { $exec_escape = 1; } } if (! defined ($db_translate_enterprise)) { $db_translate_enterprise = 0} ; if (! defined ($db_unknown_trap_format)) { $db_unknown_trap_format = '$-*'} ; if (! defined ($mysql_dbi_enable)) { $mysql_dbi_enable = 0} ; if (! defined ($mysql_dbi_host)) { $mysql_dbi_host = 'localhost'} ; if (! defined ($mysql_dbi_port)) { $mysql_dbi_port = '3306'} ; if (! defined ($mysql_dbi_database)) { $mysql_dbi_database = ''} ; if (! defined ($mysql_dbi_table)) { $mysql_dbi_table = ''} ; if (! defined ($mysql_dbi_table_unknown)) { $mysql_dbi_table_unknown = ''} ; if (! defined ($mysql_dbi_table_statistics)) { $mysql_dbi_table_statistics = ''} ; if (! defined ($mysql_dbi_username)) { $mysql_dbi_username = ''} ; if (! defined ($mysql_dbi_password)) { $mysql_dbi_password = ''} ; if (! defined ($mysql_ping_on_insert)) { $mysql_ping_on_insert = 1} ; if (! defined ($mysql_ping_interval)) { $mysql_ping_interval = 500} ; if (! defined ($postgresql_dbi_enable)) { $postgresql_dbi_enable = 0} ; if (! defined ($postgresql_dbi_module)) { $postgresql_dbi_module = 0} ; if (! defined ($postgresql_dbi_hostport_enable)) { $postgresql_dbi_hostport_enable = 0} ; if (! defined ($postgresql_dbi_host)) { $postgresql_dbi_host = 'localhost'} ; if (! defined ($postgresql_dbi_port)) { $postgresql_dbi_port = '5432'} ; if (! defined ($postgresql_dbi_database)) { $postgresql_dbi_database = ''} ; if (! defined ($postgresql_dbi_table)) { $postgresql_dbi_table = ''} ; if (! defined ($postgresql_dbi_table_unknown)) { $postgresql_dbi_table_unknown = ''} ; if (! defined ($postgresql_dbi_table_statistics)) { $postgresql_dbi_table_statistics = ''} ; if (! defined ($postgresql_dbi_username)) { $postgresql_dbi_username = ''} ; if (! defined ($postgresql_dbi_password)) { $postgresql_dbi_password = ''} ; if (! defined ($postgresql_ping_on_insert)) { $postgresql_ping_on_insert = 1} ; if (! defined ($postgresql_ping_interval)) { $postgresql_ping_interval = 500} ; if (! defined ($dbd_odbc_enable)) { $dbd_odbc_enable = 0} ; if (! defined ($dbd_odbc_dsn)) { $dbd_odbc_dsn = ''} ; if (! defined ($dbd_odbc_table)) { $dbd_odbc_table = ''} ; if (! defined ($dbd_odbc_table_unknown)) { $dbd_odbc_table_unknown = ''} ; if (! defined ($dbd_odbc_table_statistics)) { $dbd_odbc_table_statistics = ''} ; if (! defined ($dbd_odbc_username)) { $dbd_odbc_username = ''} ; if (! defined ($dbd_odbc_password)) { $dbd_odbc_password = ''} ; if (! defined ($dbd_odbc_ping_on_insert)) { $dbd_odbc_ping_on_insert = 1} ; if (! defined ($dbd_odbc_ping_interval)) { $dbd_odbc_ping_interval = 500} ; if (! defined ($sql_win32_odbc_enable)) { $sql_win32_odbc_enable = 0} ; if (! defined ($sql_win32_odbc_dsn)) { $sql_win32_odbc_dsn = ''} ; if (! defined ($sql_win32_odbc_table)) { $sql_win32_odbc_table = ''} ; if (! defined ($sql_win32_odbc_table_unknown)) { $sql_win32_odbc_table_unknown = ''} ; if (! defined ($sql_win32_odbc_table_statistics)) { $sql_win32_odbc_table_statistics = ''} ; if (! defined ($sql_win32_odbc_username)) { $sql_win32_odbc_username = ''} ; if (! defined ($sql_win32_odbc_password)) { $sql_win32_odbc_password = ''} ; if (! (@sql_custom_columns)) { @sql_custom_columns = ()} ; if (! (@sql_custom_columns_unknown)) { @sql_custom_columns_unknown = ()} ; if (! defined ($DEBUGGING)) { $DEBUGGING = 0} ; if (! defined ($DEBUGGING_FILE)) { $DEBUGGING_FILE = ''} ; # Make sure it's at least 3 characters, and remove quotes around wildcard_expansion_separator if (length ($wildcard_expansion_separator) < 3) { $wildcard_expansion_separator = "x x"; } $wildcard_expansion_separator = substr($wildcard_expansion_separator,1,(length($wildcard_expansion_separator)-2)); # # Defaults End # # print "Config file loaded\n"; # # Load config file end # ############################################################################## } sub signal_handler_reload { # # Daemon reload # $timetoreload = 1; reopen_debug_file(); } sub signal_handler_die { # # Daemon die # $timetodie = 1; } sub syslog_system { my $message = $_[0]; if ($syslog_module == 0) { # Sys::Syslog eval { if (Sys::Syslog::openlog('snmptt-sys','pid',$syslog_system_facility) ) { Sys::Syslog::syslog ($syslog_system_level, "%s", $message); Sys::Syslog::closelog(); } }; if ($@) { warn "Cannot write to syslog: $@\n"; print "Cannot write to syslog. Message to be logged: $message\n" if ($DEBUGGING >= 1); } } else { # Log::Syslog::Fast my $protocol = ($syslog_remote_proto eq "TCP" ? Log::Syslog::Fast->LOG_TCP : Log::Syslog::Fast->LOG_UDP); eval { if (my $logger = Log::Syslog::Fast->new($protocol, $syslog_remote_dest, $syslog_remote_port, Log::Syslog::Constants::get_facility($syslog_system_facility), Log::Syslog::Constants::get_severity($syslog_system_level), $snmptt_system_name, $syslog_system_app)) { if ($syslog_rfc_format == 1) { $logger->set_format(Log::Syslog::Fast->LOG_RFC3164_LOCAL); } elsif ($syslog_rfc_format == 2) { $logger->set_format(Log::Syslog::Fast->LOG_RFC5424); } else { $logger->set_format(Log::Syslog::Fast->LOG_RFC3164); } $logger->send($message); } }; if ($@) { warn "Cannot write to syslog: $@\n"; print "Cannot write to syslog. Message to be logged: $message\n" if ($DEBUGGING >= 1); } } } sub log_system { my $message = $_[0]; my @localtime_array = localtime(); my $log_time; if ($date_time_format eq "") { $log_time = localtime(); } else { $log_time = strftime $date_time_format, @localtime_array; } my $fh_LOGSYSFILE; if (open $fh_LOGSYSFILE, ">>", $log_system_file) { print $fh_LOGSYSFILE $log_time." $message\n"; close $fh_LOGSYSFILE; } else { warn "Can not open log file $log_system_file: $!"; print "Can not open syslog. Message to be logged: $message\n" if ($DEBUGGING >= 1); } } sub eventlog_system { my $message = $_[0]; my $eventid = $_[1]; my $type = $_[2]; my $trap_log = $_[3]; # Should be 1 if this is a trap log. Used # to set $trap_successfully_logged variable below. my %event_entry = ('Source' => "SNMPTT", 'EventType' => $type, 'Category' => '\0', 'EventID' => $eventid, 'Strings' => $message); if (my $eventlog=Win32::EventLog->new('Application') ) { unless ($eventlog->Report(\%event_entry) ) { warn "Can not create event log entry: $!"; $eventlog->Close(); return 1; } $eventlog->Close(); return 0; } else { warn "Can not open log_file: $!"; return 1; } } # IPv6_TODO_DONE sub checkip { my $node1 = $_[0]; # Should be a plain IP address my $node2 = $_[1]; # Can be a plain IP address, CIDR or range # Remove all white space $node1 =~ s/\s*//g; $node2 =~ s/\s*//g; # Check for an exact match if ($node1 eq $node2) { return 1; } #print "node1:$node1\n"; # First is an IP address, and second IP address is a CIDR address if ($node1 =~ /^\d+\.\d+\.\d+\.\d+$/ && $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/) { # Get IP address in binary $node1 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/; my $node1_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4); # Get CIDR network address in binary $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)/; my $cidr_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4); my $net_bits = $5; my $host_bits = 32 - $net_bits; $cidr_binary = substr($cidr_binary, 0, $net_bits); # Get network number for ip_address my $node1_binary_net = substr($node1_binary, 0, $net_bits); if ($node1_binary_net eq $cidr_binary) { return 1; } else { return 0; } } # First is an IP address, and second IP address is a network range elsif ($node1 =~ /^\d+\.\d+\.\d+\.\d+$/ && $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.(\d+)\.(\d+)/) { # Get IP address in binary $node1 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/; my $node1_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4); # Get left network address in binary $node2 =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.(\d+)\.(\d+)/; my $left_binary = dec2bin($1) . dec2bin($2) .dec2bin($3) .dec2bin($4); my $right_binary = dec2bin($5) . dec2bin($6) .dec2bin($7) .dec2bin($8); if (bin2dec($node1_binary) >= bin2dec($left_binary) && bin2dec($node1_binary) <= bin2dec($right_binary)) { return 1; } else { return 0; } } # Didn't match at all, so return 0. else { return 0; } } # IPv6_TODO_DONE sub checkipv6 { my $node1 = $_[0]; # Should be a plain IP address my $node2 = $_[1]; # Can be a plain IP address or CIDR # Remove all white space $node1 =~ s/\s*//g; $node2 =~ s/\s*//g; # Check for an exact match if ($node1 eq $node2) { return 1; } #print "node1:$node1\n"; #print "node2:$node2\n"; my $node1ip = new Net::IP($node1); if (!$node1ip) { if ($DEBUGGING >= 1) { print " Invalid IPv6 address detected (could be a date/time - $node1): " . Net::IP::Error() . "\n"; } return 0; } my $node2ip = new Net::IP($node2); if (!$node1ip) { if ($DEBUGGING >= 1) { print " Invalid IPv6 address detected ($node2): " . Net::IP::Error() . "\n"; } return 0; } my $result = $node1ip->overlaps($node2ip); #print "IPv6 overlap:\n"; #print "IP_A_IN_B_OVERLAP: $Net::IP::IP_A_IN_B_OVERLAP\n"; #print "IP_PARTIAL_OVERLAP: $Net::IP::IP_PARTIAL_OVERLAP\n"; #print "IP_IDENTICAL: $Net::IP::IP_IDENTICAL\n"; #print "\n"; { no warnings; if ($result == $Net::IP::IP_A_IN_B_OVERLAP || $result == $Net::IP::IP_IDENTICAL) { #print "Yes: $result\n"; return 1; } else { #print "No: $result\n"; return 0; } } } sub dec2bin { my $str = unpack("B8", pack("C", shift)); #$str =~ s/^0+(?=\d)//; # otherwise you'll get leading zeros return $str; } sub bin2dec { return unpack("N", pack("B32", substr("0" x 32 . shift, -32))); } sub translate_value_oids_sub { my $string = $_[0]; if ($string =~ /\.\d+/) # If it appears to contain an OID { if ($DEBUGGING >= 2) { print " Value appears to contain an OID or IP address.\n"; } my $string_temp = $string; my @oids = (); my $done = 1; my $noinf = 0; # IPv6_TODO_DONE # Find any IPv4 addresses and remove from $string_temp $noinf = 0; while ($done) { my $found; if ($string_temp =~ /(?= 100) # Safe guard to make sure regex pattern search / replace doesn't cause an # infinit loop in the while. Shouldn't, but just in case.. { $done = 0; if ($DEBUGGING >= 2) { print " Possible infinit loop (1) in translate_value_oids_sub averted.\n"; } } } # Find all the OIDs and put them in a @oids array, and remove from $string_temp $done = 1; my $oid_found = 0; $noinf = 0; while ($done) { my $found; if ($string_temp =~ /(?= 100) # Safe guard to make sure regex pattern search / replace doesn't cause an # infinit loop in the while. Shouldn't, but just in case.. { $done = 0; if ($DEBUGGING >= 2) { print " Possible infinit loop (2) in translate_value_oids_sub averted.\n"; } } } if ($oid_found == 1 && $DEBUGGING >= 2) { print " Value contains an OID.\n"; } # Reverse sort the list to make sure large OIDs are done first, so we don't get half # converted OIDs. Probably not needed due to look ahead and behind expression below @oids = reverse sort(@oids); #print "oids: @oids\n"; # Replace all OIDs in $string with textual names if possible foreach my $oid (@oids) { my $temp = my_translateObj($oid,$translate_value_oids); if (defined ($temp)) { $string =~ s/(?= 2) { print " OID: $oid=$temp\n"; } } } } else { if ($DEBUGGING >= 2) { print " Value does not appear to contain an OID\n"; } } # Return the new string return $string; } # IPv6_TODO_DONE sub resolve_value_ip_addresses_sub { my $string = $_[0]; if ($string =~ /(?= 2) { print " Value appears to contain an IP address.\n"; } my $string_temp = $string; my @hostnames = (); my $done = 1; my $noinf = 0; # Find any IP addresses and put them in a @hostnames array, and remove from $string_temp $done = 1; my $hostname_found = 0; $noinf = 0; while ($done) { my $found; if ($string_temp =~ /(?= 100) # Safe guard to make sure regex pattern search / replace doesn't cause an # infinit loop in the while. Shouldn't, but just in case.. { $done = 0; if ($DEBUGGING >= 2) { print " Possible infinit loop (1) in resolve_value_ip_addresses_sub averted.\n"; } } } if ($hostname_found == 1 && $DEBUGGING >= 2) { print " Value contains an IP address.\n"; } #print "hostnames: @hostnames\n"; # Replace all ip addresses in $string with textual names if possible foreach my $hostname (@hostnames) { #my $temp = gethostbyaddr(Socket::inet_aton($hostname),Socket::AF_INET()); my $temp = ip_to_name($hostname); if (defined ($temp)) { if ($DEBUGGING >= 2) { print " IP address ($hostname) resolved to: $temp\n"; } if ($strip_domain) { $temp = strip_domain_name($temp, $strip_domain); } $string =~ s/(?= 2) { print " IP address ($hostname) could not be resolved by DNS.\n"; } } } } else { if ($DEBUGGING >= 2) { print " Value does not appear to contain an IP address\n"; } } # Return the new string return $string; } sub resolve_value_ipv6_addresses_sub { my $string = $_[0]; # https://community.helpsystems.com/forums/intermapper/miscellaneous-topics/5acc4fcf-fa83-e511-80cf-0050568460e4?_ga=2.113564423.1432958022.1523882681-2146416484.1523557976 # https://raw.githubusercontent.com/richb-intermapper/IPv6-Regex/master/test-ipv6-regex.pl # IPv6 regex by dartware (Stephen Ryan at Dartware). Also matches if it contains a zone index (%ens160). if ($string =~ /\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*/) # If it appears to contain an IPv6 address { if ($DEBUGGING >= 2) { print " Value appears to contain an IPv6 address.\n"; } my $string_temp = $string; my @hostnames = (); my $done = 1; my $noinf = 0; # Find any IP addresses and put them in a @hostnames array, and remove from $string_temp $done = 1; my $hostname_found = 0; $noinf = 0; while ($done) { my $found; if ($string_temp =~ /\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*/ ) { $found = 1; } else { $found = 0; } if ($found) { my $hostname = $1; # We use &$ for IPv4 but only $1 for IPv6 push (@hostnames, $hostname); #print "ip:$&\n"; $string_temp =~ s/(?= 100) # Safe guard to make sure regex pattern search / replace doesn't cause an # infinit loop in the while. Shouldn't, but just in case.. { $done = 0; if ($DEBUGGING >= 2) { print " Possible infinit loop (1) in resolve_value_ipv6_addresses_sub averted.\n"; } } } if ($hostname_found == 1 && $DEBUGGING >= 2) { print " Value contains an IPv6 address.\n"; } #print "hostnames: @hostnames\n"; # Replace all ip addresses in $string with textual names if possible foreach my $hostname (@hostnames) { #my $temp = gethostbyaddr(Socket::inet_aton($hostname),Socket::AF_INET()); my $temp = ip_to_name($hostname); if (defined ($temp)) { if ($DEBUGGING >= 2) { print " IPv6 address ($hostname) resolved to: $temp\n"; } if ($strip_domain) { $temp = strip_domain_name($temp, $strip_domain); } $string =~ s/(?= 2) { print " IPv6 address ($hostname) could not be resolved by DNS.\n"; } } } } else { if ($DEBUGGING >= 2) { print " Value does not appear to contain an IPv6 address\n"; } } # Return the new string return $string; } #IPv6_TODO_DONE sub match { my $match = shift; my $value; my $not = 0; my $result = 0; if ($DEBUGGING >= 1) { print " MATCH statement: $match\n"; } if ($match =~ /^\$(\d+):(.*)/) { my $match_var = "\$$1"; $value = $entvar[$1-1]; $match = $2; if ($DEBUGGING >= 1) { print " MATCH variable ($match_var) is an enterprise variable. No substitution needed\n"; } } else { $match =~ /^(\$.*?):(.*)/; my $match_var = $1; $_ = $match_var; $match = $2; if ($DEBUGGING >= 1) { print " Performing substitution on MATCH variable: $match_var\n"; } substitute(); if ($DEBUGGING >= 1) { print " Done performing substitution on MATCH variable $match_var (converted to: $_)\n"; } $value = $_; } if ($value eq '' || $match eq '') { return 0; } if ($match =~ /^\!(.*)/) { $not = 1; $match = $1; } else { $not = 0; } # Match is a REGEX if ($match =~ /\(.*\)/) { #print "Regex detected\n"; $result = match_regex($value, $match); $result = match_result($result, $not); if ($DEBUGGING >= 1) { print " REGEX: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . ($result == 1 ? "true" : "false") . "\n"; } } # Match is an IPv6 address elsif ($ipv6_enable == 1 && $match =~ /(.*?:.*)/) { #print "IPv6 address of some sort\n"; $result = checkipv6($value, $match); $result = match_result($result, $not); if ($DEBUGGING >= 1) { print " IPv6 ADDRESS: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . ($result == 1 ? "true" : "false") . "\n"; } } # Match is an IPv4 address elsif ($match =~ /\d+\.\d+\.\d+\.\d+/) { #print "IP address of some sort\n"; $result = checkip($value, $match); $result = match_result($result, $not); if ($DEBUGGING >= 1) { print " IPv4 ADDRESS: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . ($result == 1 ? "true" : "false") . "\n"; } } # Match is a bitwise and elsif ($match =~ /&\d+/) { #print "Bitwise and detected\n"; $result = match_bitwise_and($value, $match); $result = match_result($result, $not); if ($DEBUGGING >= 1) { print " BITWISE AND: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . ($result == 1 ? "true" : "false") . "\n"; } } # Match is a number elsif ($match =~ /\d+/) { #print "Number detected\n"; $result = match_number($value, $match); $result = match_result($result, $not); if ($DEBUGGING >= 1) { print " NUMBER: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . " Result=" . ($result == 1 ? "true" : "false") . "\n"; } } # Unknown entry - ignore it else { if ($DEBUGGING >= 1) { print " IGNORED: value=$value, match=" . ($not == 1 ? "!$match" : "$match") . "\n"; $result = 0; } } if ($result == 1) { #print "true\n"; return 1; } else { #print "false\n"; return 0; } } sub match_result { my $result = shift; my $not = shift; if ($not == 0) { return $result; } else { if ($result >= 1) { return 0; } else { return 1; } } return 0; } sub match_number { my $value = shift; my $match = shift; my $result; if ($match =~ /,/) { my @temp = split (",",$match); foreach my $a (@temp) { if ($a == $value) { return 1; } } } elsif ($match =~ /^\>(.*)/) { if ($value > $1) { return 1; } } elsif ($match =~ /^\<(.*)/) { if ($value < $1) { return 1; } } elsif ($match =~/(\d+)-(\d+)/) { if ($value >= $1 && $value <= $2) { return 1; } } elsif ($match == $value) { return 1; } return 0; } sub match_regex { my $value = shift; my $regex_temp = shift; my $result; my $modifier_i = 0; #print "value: $value\n"; #print "regex: $regex_temp\n"; if ($regex_temp =~ /i$/) { #print "! i modifier\n"; $modifier_i = 1; } $regex_temp =~ /\((.*)\)/; $regex_temp = $1; #print ":$regex_temp:\n"; if ($regex_temp ne '') { if ($DEBUGGING >= 1) { print " Doing REGEX MATCH: ($regex_temp)\n"; } if ($modifier_i == 1) { if ($value =~ /$regex_temp/i) { #print "Match!\n"; return 1; } } else { if ($value =~ /$regex_temp/) { #print "Match!\n"; return 1; } } } return 0; } sub match_bitwise_and { my $value = shift; my $match = shift; # Return if value is not a number if (! ($value =~ /\d+/)) { return 0; } # Remove & from value $match =~ s/^&//; my $result; # Make sure values are passed as numbers by adding 0 to each my $temp = 0+$value & 0+$match; if ($temp > 0) { return 1; } return 0; } sub my_translateObj { my $oid = shift; my $mode = shift; # Check cache first my $my_translateObj_cache_value = $my_translateObj_cache{$oid}; if ($my_translateObj_cache_value) { if ($DEBUGGING >= 2) { print " OID found in cache: '$oid' -> '$my_translateObj_cache_value'\n"; } return $my_translateObj_cache_value; } my $use_long = 0; my $include_module = 0; my $temp; if ($mode == 1) # Short text { $use_long = 0; $include_module = 0; } elsif ($mode == 2) # Short module::text { $use_long = 0; $include_module = 1; } elsif ($mode == 3) # Long text { $use_long = 1; $include_module = 0; } elsif ($mode == 4) # Long module::text { $use_long = 1; $include_module = 1; } $temp = SNMP::translateObj($oid,$use_long,$include_module); if (defined ($temp)) { # If it ends in a single ., chop it off if ($temp =~ /\.$/) { chop $temp; } } # Update cache if net_snmp_perl_cache_enable is enabled. if ($net_snmp_perl_cache_enable == 1) { $my_translateObj_cache{$oid} = $temp; } return $temp; # Will be either the translated trap, or undef if translateObj failed } # IPv6_TODO # Strip domain name from hostname sub strip_domain_name { my $name = shift; my $mode = shift; # If mode = 1, strip off all domain names leaving only the host if ($mode == 1 && !($name =~ /^\d+\.\d+\.\d+\.\d+$/) && !($name =~ /(.*?:.*)/)) { if ($name =~ /\./) # Contain a . ? { $name =~ /^([^\.]+?)\./; $name = $1; } } # If mode = 2, strip off the domains as listed in strip_domain_list in .ini file elsif ($mode == 2 && !($name =~ /^\d+\.\d+\.\d+\.\d+$/)) { if (@strip_domain_list) { foreach my $strip_domain_list_temp (@strip_domain_list) { if ($strip_domain_list_temp =~ /^\..*/) # If domain from list starts with a '.' then remove it first { ($strip_domain_list_temp) = $strip_domain_list_temp =~ /^\.(.*)/; } if ($name =~ /^.+\.$strip_domain_list_temp/) # host is something . domain name? { $name =~ /(.*)\.$strip_domain_list_temp/; # strip the domain name $name = $1; last; # Only process once } } } } return $name; } sub my_getType { my $oid = shift; # Problem #1: UCD-SNMP 4.2.3 allows getType and mapEnum to be passed a numerical OID. # Net-SNMP 5.0.8 does not. Because of this, lets try using the OID first. If that # fails, then we try the short textual OID instead. my $type = SNMP::getType($oid); if (defined($type)) { #print "my_getType: numeric\n"; return $type; } else { my $temp_textOID = my_translateObj($oid,0); #print "temp_textOID:$temp_textOID\n"; my $type = SNMP::getType($temp_textOID); if (defined($type)) { #print "my_getType: symbolic\n"; return $type; } else { #print "my_getType: failed\n"; return undef; } } } sub my_mapEnum { my $oid = shift; my $value = shift; # Check cache first my $my_mapEnum_cache_value = $my_mapEnum_cache{$oid}; if ($my_mapEnum_cache_value) { if ($DEBUGGING >= 2) { print " ENUM found in cache: '$oid' -> '$my_mapEnum_cache_value'\n"; } return $my_mapEnum_cache_value; } # Problem #1: UCD-SNMP 4.2.3 allows getType and mapEnum to be passed a numerical OID. # Net-SNMP 5.0.8 does not. Because of this, lets try using the OID first. If that # fails, then we try the short textual OID instead. my $result; my $enum = SNMP::mapEnum($oid, $value); if (defined($enum)) { #print "my_mapEnum: numeric\n"; #return $enum; $result = $enum; } else { my $temp_textOID = my_translateObj($oid,0); #print "temp_textOID:$temp_textOID\n"; my $enum = SNMP::mapEnum($temp_textOID, $value); if (defined($enum)) { #print "my_mapEnum: symbolic\n"; #return $enum; $result = $enum; } else { #print "my_mapEnum: failed\n"; #return undef; $result = undef; } } # Update cache if net_snmp_perl_cache_enable is enabled. if ($net_snmp_perl_cache_enable == 1) { $my_mapEnum_cache{$oid} = $result; } return $result; } # IPv6_TODO_DONE sub process_nodes { my $nodes_list = shift; my @nodes = (); my @nodes2 = (); # print "Processing NODES\n"; # Put all the NODES entries into @nodes, and then go through them all and put # them into @nodes2. This is done so files that are in the NODES list can be # read in and merged into @nodes2 to allow a NODES line to contain both host # names / ip addresses AND the entries from nodes files. @nodes = split /\s/, $nodes_list; foreach my $a (@nodes) { # Contain a \ or /? Must be a filename and not an IP address / network number # as long as it doesn't look like an IPv6 or IPv6 address. #if ( ($a =~ /\\|\//) && !($a =~ /\d+\.\d+\.\d+\.\d+/)) if ( ($a =~ /\\|\//) && !($a =~ /\d+\.\d+\.\d+\.\d+/) && !($a =~ /(.*?:.*)/)) { #print "!Dynamic enabled\n"; my $fh_NODESFILE; if (open $fh_NODESFILE, '<', $a) { while (defined(my $line = <$fh_NODESFILE>)) { chomp($line); # Allow comment lines starting with a # if (!($line =~ /((^#)|(\s+#)).*/)) { my @temp2 = split /\s/, $line; foreach my $b (@temp2) { push (@nodes2, $b); } } } close $fh_NODESFILE; } else { warn "Can not open NODES file: $a $!"; } } else # Must be a single node { push (@nodes2, $a); } } return @nodes2; } # Used when reading received traps to symbolic names in variable names and # values to numerical sub translate_symbolic_to_oid { my $temp = shift; # Check to see if OID passed from snmptrapd is fully numeric. If not, try to translate if (! ($temp =~ /^(\.\d+)+$/)) { # Not numeric # Try to convert to numerical if ($DEBUGGING >= 2) { print "Symbolic trap variable name detected ($temp). Will attempt to translate to a numerical OID\n"; } if ($net_snmp_perl_enable == 1) { my $temp3 = SNMP::translateObj("$temp",0); if (defined ($temp3) ) { if ($DEBUGGING >= 2) { print " Translated to $temp3\n\n"; } $temp = $temp3; } else { # Could not translate default to numeric if ($DEBUGGING >= 2) { print " Could not translate - will leave as-is\n\n"; } } } else { if ($DEBUGGING >= 2) { print " Could not translate - Net-SNMP Perl module not enabled - will leave as-is\n\n "; } } } return $temp; } sub translate_log_trap_oid_sub { my $trap_oid_temp = shift; if ($net_snmp_perl_enable == 1 && $translate_log_trap_oid) { # Try to convert to text if ($DEBUGGING >= 2) { print "\nOID of trap: $trap_oid_temp. Will attempt to translate to text\n"; } my $temp2 = my_translateObj("$trap_oid_temp",$translate_log_trap_oid); if (defined ($temp2) ) { if ($DEBUGGING >= 2) { print " Translated to $temp2\n"; } $trap_oid_temp = $temp2; } else { # Could not translate default to numeric if ($DEBUGGING >= 2) { print " Could not translate - defaulting to numeric\n"; } } } return $trap_oid_temp; } sub translate_enterprise_oid_format_sub { my $enterprise_oid_temp = shift; if ($net_snmp_perl_enable == 1 && $var[6] ne '') { if ($DEBUGGING >= 2) { print "\nOID of enterprise: $var[6]. Will attempt to translate to text\n"; } my $temp = my_translateObj("$var[6]",$translate_enterprise_oid_format); if (defined ($temp) ) { if ($DEBUGGING >= 2) { print " Translated to $temp\n"; } $enterprise_oid_temp = $temp; } else { # Could not translate default to numeric if ($DEBUGGING >= 2) { print " Could not translate - defaulting to numeric\n"; } } } return $enterprise_oid_temp; } # Log statistics to syslog, event log etc sub log_statistics { if ($DEBUGGING >= 1) { print "Total traps received: $g_total_traps_received\n"; print "Total traps translated: $g_total_traps_translated\n"; print "Total traps ignored: $g_total_traps_ignored\n"; if ($unknown_trap_nodes_match_mode != 0) { print "Total traps skipped: $g_total_traps_skipped\n"; } print "Total unknown traps: $g_total_traps_unknown\n\n"; } if ($syslog_system_enable == 1) { if ($unknown_trap_nodes_match_mode != 0) { syslog_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total traps skipped=$g_total_traps_skipped,Total unknown traps=$g_total_traps_unknown"); } else { syslog_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total unknown traps=$g_total_traps_unknown"); } } if ($log_system_enable == 1) { if ($unknown_trap_nodes_match_mode != 0) { log_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total traps skipped=$g_total_traps_skipped,Total unknown traps=$g_total_traps_unknown"); } else { log_system("Total traps received=$g_total_traps_received,Total traps translated=$g_total_traps_translated,Total traps ignored=$g_total_traps_ignored,Total unknown traps=$g_total_traps_unknown"); } } if ($eventlog_system_enable == 1) { my $message; if ($unknown_trap_nodes_match_mode != 0) { $message = "" . "Total traps received: $g_total_traps_received\n" . "Total traps translated: $g_total_traps_translated\n" . "Total traps ignored: $g_total_traps_ignored:\n" . "Total traps skipped: $g_total_traps_skipped:\n" . "Total unknown traps: $g_total_traps_unknown\n"; } else { $message = "" . "Total traps received: $g_total_traps_received\n" . "Total traps translated: $g_total_traps_translated\n" . "Total traps ignored: $g_total_traps_ignored:\n" . "Total unknown traps: $g_total_traps_unknown\n"; } eventlog_system($message,1,$eventlog_information); } if ($mysql_dbi_enable == 1 && defined ($dbh_mysql) && $mysql_dbi_table_statistics ne "") { my $stat_time_temp; if ($stat_time_format_sql eq "") { $stat_time_temp = localtime(); } else { $stat_time_temp = strftime $stat_time_format_sql, localtime(); } if ($unknown_trap_nodes_match_mode != 0) { mysql_insert($mysql_dbi_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_skipped", $g_total_traps_skipped, "total_unknown", $g_total_traps_unknown); } else { mysql_insert($mysql_dbi_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_unknown", $g_total_traps_unknown); } } if ($postgresql_dbi_enable == 1 && defined ($dbh_postgresql) && $postgresql_dbi_table_statistics ne "") { my $stat_time_temp; if ($stat_time_format_sql eq "") { $stat_time_temp = localtime(); } else { $stat_time_temp = strftime $stat_time_format_sql, localtime(); } if ($unknown_trap_nodes_match_mode != 0) { postgresql_insert($postgresql_dbi_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_skipped", $g_total_traps_skipped, "total_unknown", $g_total_traps_unknown); } else { postgresql_insert($postgresql_dbi_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_unknown", $g_total_traps_unknown); } } if ($dbd_odbc_enable == 1 && defined ($dbh_odbc) && $dbd_odbc_table_statistics ne "") { my $stat_time_temp; if ($stat_time_format_sql eq "") { $stat_time_temp = localtime(); } else { $stat_time_temp = strftime $stat_time_format_sql, localtime(); } if ($unknown_trap_nodes_match_mode != 0) { odbc_insert($dbd_odbc_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_skipped", $g_total_traps_skipped, "total_unknown", $g_total_traps_unknown); } else { odbc_insert($dbd_odbc_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_unknown", $g_total_traps_unknown); } } if ($sql_win32_odbc_enable == 1 && defined ($dbh_win32_odbc) && $sql_win32_odbc_table_statistics ne "") { my $stat_time_temp; if ($stat_time_format_sql eq "") { $stat_time_temp = localtime(); } else { $stat_time_temp = strftime $stat_time_format_sql, localtime(); } if ($unknown_trap_nodes_match_mode != 0) { sql_win32_odbc_insert($sql_win32_odbc_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_skipped", $g_total_traps_skipped, "total_unknown", $g_total_traps_unknown); } else { sql_win32_odbc_insert($sql_win32_odbc_table_statistics, "stat_time", $stat_time_temp, "total_received", $g_total_traps_received, "total_translated", $g_total_traps_translated, "total_ignored", $g_total_traps_ignored, "total_unknown", $g_total_traps_unknown); } } $g_last_statistics_logged = time(); } sub signal_log_statistics { # # Statistics log # $timetologstatistics = 1; } # Open connections to MySQL, PostresQL, ODBC etc sub create_db_connections { dbh_mysql_connect(); dbh_postgresql_connect(); dbh_odbc_connect(); dbh_win32_odbc_connect(); } sub close_db_connections { dbh_mysql_close(); dbh_postgresql_close(); dbh_odbc_close(); dbh_win32_odbc_close(); } sub dbh_mysql_close { if (defined $dbh_mysql) { $dbh_mysql->disconnect; $dbh_mysql = undef; } } sub dbh_postgresql_close { if (defined $dbh_postgresql) { $dbh_postgresql->disconnect; $dbh_postgresql = undef; } } sub dbh_odbc_close { if (defined $dbh_odbc) { $dbh_odbc->disconnect; $dbh_odbc = undef; } } sub dbh_win32_odbc_close { if (defined $dbh_win32_odbc) { $dbh_win32_odbc->Close(); $dbh_win32_odbc = undef; } } sub dbh_mysql_connect() { if ($mysql_dbi_enable == 1) { dbh_mysql_close(); unless ($dbh_mysql = DBI->connect("DBI:mysql:database=$mysql_dbi_database;host=$mysql_dbi_host;" . "port=$mysql_dbi_port",$mysql_dbi_username,$mysql_dbi_password) ) { my $msg = "MySQL error: Unable to connect to database: $DBI::errstr"; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,12,$eventlog_error); } } } } sub dbh_postgresql_connect() { if ($postgresql_dbi_enable == 1) { my $connect_string; my $dbi_module; if ($postgresql_dbi_module == 0) { $dbi_module = "PgPP"; } else { $dbi_module = "Pg"; } if ($postgresql_dbi_hostport_enable == 0) { # No network support $connect_string = "DBI:$dbi_module:dbname=$postgresql_dbi_database;"; } else { # Network support - include host and port $connect_string = "DBI:$dbi_module:dbname=$postgresql_dbi_database;host=$postgresql_dbi_host;port=$postgresql_dbi_port"; } dbh_postgresql_close(); unless ($dbh_postgresql = DBI->connect($connect_string,$postgresql_dbi_username,$postgresql_dbi_password) ) { my $msg = "Postgres error: Unable to connect to database: $DBI::errstr"; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,18,$eventlog_error); } } } } sub dbh_odbc_connect() { if ($dbd_odbc_enable == 1) { dbh_odbc_close(); unless ($dbh_odbc = DBI->connect("DBI:ODBC:$dbd_odbc_dsn",$dbd_odbc_username,$dbd_odbc_password) ) { my $msg = "DBI DBD:ODBC error: Unable to connect to DSN: $DBI::errstr"; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,13,$eventlog_error); } } } } sub dbh_win32_odbc_connect() { if ($sql_win32_odbc_enable == 1) { dbh_win32_odbc_close(); unless ($dbh_win32_odbc = new Win32::ODBC("DSN=$sql_win32_odbc_dsn","UID=$sql_win32_odbc_username","PWD=$sql_win32_odbc_password") ) { my $msg = "SQL error: Unable to connect to DSN $sql_win32_odbc_dsn:" . Win32::ODBC::Error(); warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($eventlog_system_enable == 1) { eventlog_system($msg,13,$eventlog_error); } } } } sub mysql_ping { if ($mysql_dbi_enable == 1) { if (defined ($dbh_mysql)) { my $rc = $dbh_mysql->ping; if ($dbh_mysql->{'errno'} != 0) { dbh_mysql_connect(); } } else { dbh_mysql_connect(); } } $g_last_mysql_ping = time(); if ($DEBUGGING >= 2) { print "MYSQL Ping\n" } } sub postgresql_ping { if ($postgresql_dbi_enable == 1) { if (defined ($dbh_postgresql)) { my $rc = $dbh_postgresql->ping; if (!$rc) { dbh_postgresql_connect(); } } else { dbh_postgresql_connect(); } } $g_last_postgresql_ping = time(); if ($DEBUGGING >= 2) { print "PostgreSQL Ping\n" } } sub dbd_odbc_ping { if ($dbd_odbc_enable == 1) { if (defined ($dbh_odbc)) { my $rc = $dbh_odbc->ping; if (!$rc) { dbd_odbc_connect(); } } else { dbd_odbc_connect(); } } $g_last_dbd_odbc_ping = time(); if ($DEBUGGING >= 2) { print "DBD_ODBC Ping\n" } } sub mysql_insert { my $table = shift; my @data = @_; # If the number of elements in @data is odd, remove the last element # Note: $# returns the last element # so it's reall #$data + 1 #print "mod :" . $#data % 2 . "\n"; if ($#data % 2 == 0) { pop @data; } #print "------------------ mysql_insert ---------------\n"; my $sql_prepare = "INSERT INTO $table ("; my @sql_execute; for (my $i = 0; $i < $#data;) { #print $data[$i]. "\n"; $sql_prepare .= $data[$i]; push (@sql_execute, $data[$i+1]); $i+=2; if ($i < ($#data)) { $sql_prepare .= ","; } } $sql_prepare .= ") VALUES (?"; $sql_prepare .= ",?" x ($#data / 2); $sql_prepare .= ")"; #foreach my $x (@sql_execute) { # print "$x\n"; #} #print "sql_prepare: $sql_prepare\n"; #print "sql_execute: @sql_execute\n"; # Make sure the connection is up if ($mysql_ping_on_insert == 1) { mysql_ping(); } if (defined ($dbh_mysql)) { my $prepare_successful = 0; my $do_successful = 0; #my $sql_statement = "INSERT INTO $mysql_dbi_table (eventname, eventid, #trapoid, enterprise, community, hostname, agentip, category, severity, #uptime, traptime, formatline) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"; my $sth_mysql = $dbh_mysql->prepare($sql_prepare); unless (defined ($sth_mysql)) { my $msg = "MySQL error " . $dbh_mysql->{'errno'} . ": Unable to perform PREPARE: ".$dbh_mysql->errstr; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,23,$eventlog_error); } } else { $prepare_successful = 1; } if ($prepare_successful == 1) { unless (defined ($sth_mysql->execute(@sql_execute))) { my $msg = "MySQL error " . $dbh_mysql->{'errno'} . ": Unable to perform INSERT INTO (EXECUTE): ".$dbh_mysql->errstr; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,15,$eventlog_error); } } else { $do_successful = 1; } } if ($do_successful == 1) { return 1; } } return 0; } sub postgresql_insert { my $table = shift; my @data = @_; # If the number of elements in @data is odd, remove the last element # Note: $# returns the last element # so it's reall #$data + 1 #print "mod :" . $#data % 2 . "\n"; if ($#data % 2 == 0) { pop @data; } #print "------------------ postgresql_insert ---------------\n"; my $sql_prepare = "INSERT INTO $table ("; my @sql_execute; for (my $i = 0; $i < $#data;) { $sql_prepare .= $data[$i]; push (@sql_execute, $data[$i+1]); $i+=2; if ($i < ($#data)) { $sql_prepare .= ","; } } $sql_prepare .= ") VALUES (?"; $sql_prepare .= ",?" x ($#data / 2); $sql_prepare .= ")"; #foreach my $x (@sql_execute) { # print "$x\n"; #} #print "sql_prepare: $sql_prepare\n"; #print "sql_execute: @sql_execute\n"; # Make sure the connection is up if ($postgresql_ping_on_insert == 1) { postgresql_ping(); } if (defined ($dbh_postgresql)) { my $prepare_successful = 0; my $do_successful = 0; # my $sql_statement = "INSERT INTO $postgresql_dbi_table (eventname, eventid, # trapoid, enterprise, community, hostname, agentip, category, severity, # uptime, traptime, formatline) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"; my $sth_postgresql = $dbh_postgresql->prepare($sql_prepare); unless (defined ($sth_postgresql)) { my $msg = "Postgres error: Unable to perform PREPARE: ".$dbh_postgresql->errstr; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,22,$eventlog_error); } } else { $prepare_successful = 1; } if ($prepare_successful == 1) { unless (defined ($sth_postgresql->execute(@sql_execute))) { my $msg = "Postgres error: Unable to perform INSERT INTO (EXECUTE): ".$dbh_postgresql->errstr; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,19,$eventlog_error); } } else { $do_successful = 1; } } if ($do_successful == 1) { return 1; } } return 0; } sub odbc_insert { my $table = shift; my @data = @_; # If the number of elements in @data is odd, remove the last element # Note: $# returns the last element # so it's reall #$data + 1 #print "mod :" . $#data % 2 . "\n"; if ($#data % 2 == 0) { pop @data; } #print "------------------ odbc_insert ---------------\n"; my $sql_statement = "INSERT INTO $table ("; my @sql_values; for (my $i = 0; $i < $#data;) { $sql_statement .= $data[$i]; push (@sql_values, $data[$i+1]); $i+=2; if ($i < ($#data)) { $sql_statement .= ","; } } $sql_statement .= ") VALUES ("; for (my $i = 0; $i <= $#sql_values;) { $sql_statement .= "\'" . $sql_values[$i] . "\'"; $i++; if ($i <= ($#sql_values)) { $sql_statement .= ","; } } $sql_statement .= ")"; #print "sql_statement: $sql_statement\n"; # Make sure the connection is up if ($dbd_odbc_ping_on_insert == 1) { dbd_odbc_ping(); } if (defined ($dbh_odbc)) { unless (defined ($dbh_odbc->do($sql_statement))) { my $msg = warn "DBI DBD::ODBC error: Unable to perform INSERT INTO: ".$dbh_odbc->errstr; warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($syslog_system_enable == 1) { syslog_system($msg); } if ($log_system_enable == 1) { log_system($msg); } if ($eventlog_system_enable == 1) { eventlog_system($msg,16,$eventlog_error); } } else { return 1; } return 0; } } sub sql_win32_odbc_insert { my $table = shift; my @data = @_; # If the number of elements in @data is odd, remove the last element # Note: $# returns the last element # so it's reall #$data + 1 #print "mod :" . $#data % 2 . "\n"; if ($#data % 2 == 0) { pop @data; } #print "------------------ sql_win32_odbc_insert ---------------\n"; my $sql_statement = "INSERT INTO $table ("; my @sql_values; for (my $i = 0; $i < $#data;) { $sql_statement .= $data[$i]; push (@sql_values, $data[$i+1]); $i+=2; if ($i < ($#data)) { $sql_statement .= ","; } } $sql_statement .= ") VALUES ("; for (my $i = 0; $i <= $#sql_values;) { $sql_statement .= "\'" . $sql_values[$i] . "\'"; $i++; if ($i <= ($#sql_values)) { $sql_statement .= ","; } } $sql_statement .= ")"; #print "sql_statement: $sql_statement\n"; if (defined ($dbh_win32_odbc)) { if (defined ($dbh_win32_odbc->Sql($sql_statement))) { my $msg = "Win32::ODBC error: Unable to perform INSERT INTO: ".Win32::ODBC::Error(); warn $msg, "\n"; if ($DEBUGGING >= 1) { print $msg, "\n"; } if ($eventlog_system_enable == 1) { eventlog_system($msg,17,$eventlog_error); } } else { return 1; } return 0; } } sub reopen_debug_file { if ($DEBUGGING_FILE ne '') { close $fh_DEBUGFILE; if (open $fh_DEBUGFILE, ">>", $DEBUGGING_FILE) { select $fh_DEBUGFILE; # change default output to debug file $debug_file_used = 1; $debug_file_open_error = 0; print "Debug file $DEBUGGING_FILE re-opened under uid $daemon_uid\n"; warn "Debug file $DEBUGGING_FILE re-opened under uid $daemon_uid\n"; } else { warn "could not re-open debug output file ($!)"; if ($syslog_system_enable == 1 && $daemon == 1) { syslog_system("Could not re-open debug output file!"); } if ($log_system_enable == 1 && $daemon == 1) { log_system("Could not re-open debug output file!"); } if ($eventlog_system_enable == 1 && $daemon == 1){ eventlog_system("Could not re-open debug output file!",14,$eventlog_error); } return 0; } return 1; } return 0; } sub preexec_run { # # Variable substitution for PREEXEC string # if ($DEBUGGING >= 1) { print "\n\nPREEXEC line(s):\n"; } if (defined ($event2[9][0])) # if PREEXEC string has been defined { print "PREEXEC defined\n"; if (defined ($event2[9][0])) { for (my $i=0; defined($event2[9][$i]); $i++) { $_ = $event2[9][$i]; print "Performing substitution on PREEXEC line: $_\n"; substitute(); print "Done performing substitution on PREEXEC line: $_\n"; my $command = $_; if ($pre_exec_enable == 1) { if ($DEBUGGING >= 1) { print "PREEXEC command: $command\n"; } # Execute command if ($exec_escape == 1) { # Escape wildcard characters $command =~ s/\*/\\\*/g; $command =~ s/\?/\\\?/g; } my $result = `$command`; chomp $result; # Remove spaces before and after $result =~ /^\s*(.*?)\s*$/; $result = $1; if ($result eq '') { $result = "(no output from PREEXEC)\n"; } print " command output: $result\n"; push (@preexec_var, $result); } } } else { if ($DEBUGGING >= 1) { print " PREEXEC line not defined\n"; } } } } sub exec_thread_sub { my $command = shift; $thread_exec_semaphore->down; if ($exec_escape == 1) { # Escape wildcard characters $command =~ s/\*/\\\*/g; $command =~ s/\?/\\\?/g; } if ($DEBUGGING >= 1) { print "EXECing command in thread:$command\n"; } system $command; $thread_exec_semaphore->up; } # IPv6 address including a zone (%ens160) will resolve fine. sub ip_to_name { my $ip = shift; my $hostname = ""; my $err; my @addrs = (); if ($DEBUGGING >= 1) { print "Converting IP $ip to DNS\n"; } ( $err, @addrs ) = getaddrinfo( $ip, 0, { flags => Socket->AI_NUMERICHOST } ); if ($err) { if ($DEBUGGING >= 1) { print "Error converting IP $ip to hostname\n"; } $hostname = undef; } else { #$addrs[0]->{addr} = ""; ( $err, $hostname ) = getnameinfo( $addrs[0]->{addr} ); if ($err) { if ($DEBUGGING >= 1) { print "Error converting IP $ip to hostname\n"; } $hostname = undef; } } return $hostname; } sub name_to_ip { my $hostname = shift; my @ips = (); my $err; my @addrs = (); ( $err, @addrs ) = getaddrinfo( $hostname, "", {socktype => Socket->SOCK_RAW} ); if ($err) { if ($DEBUGGING >= 1) { print "Error converting hostname $hostname to IP\n"; } $hostname = undef; } else { for my $addr (@addrs) { ( $err, my $host ) = Socket::getnameinfo( $addr->{addr}, Socket->NI_NUMERICHOST ); if ($err) { if ($DEBUGGING >= 1) { print "Error converting hostname $hostname to IP\n"; } $hostname = undef; last; } else { push(@ips, $host); #print "$host\n"; } } } return @ips; } snmptt_1.5/snmptt-eventlog.mc0000664000000000000000000000505314277306025015166 0ustar rootrootMessageIdTypedef=DWORD LanguageNames=(English=0x409:MSG00409) MessageId=0 Severity=Success Facility=Application Language=English %1 . MessageId=1 Severity=Success Facility=Application Language=English %1 . MessageId=2 Severity=Success Facility=Application Language=English %1 . MessageId=3 Severity=Success Facility=Application Language=English %1 . MessageId=4 Severity=Success Facility=Application Language=English %1 . MessageId=5 Severity=Success Facility=Application Language=English %1 . MessageId=6 Severity=Success Facility=Application Language=English %1 . MessageId=7 Severity=Success Facility=Application Language=English %1 . MessageId=8 Severity=Success Facility=Application Language=English %1 . MessageId=9 Severity=Success Facility=Application Language=English %1 . MessageId=10 Severity=Success Facility=Application Language=English %1 . MessageId=11 Severity=Success Facility=Application Language=English %1 . MessageId=12 Severity=Success Facility=Application Language=English %1 . MessageId=13 Severity=Success Facility=Application Language=English %1 . MessageId=14 Severity=Success Facility=Application Language=English %1 . MessageId=15 Severity=Success Facility=Application Language=English %1 . MessageId=16 Severity=Success Facility=Application Language=English %1 . MessageId=17 Severity=Success Facility=Application Language=English %1 . MessageId=18 Severity=Success Facility=Application Language=English %1 . MessageId=19 Severity=Success Facility=Application Language=English %1 . MessageId=20 Severity=Success Facility=Application Language=English %1 . MessageId=21 Severity=Success Facility=Application Language=English %1 . MessageId=22 Severity=Success Facility=Application Language=English %1 . MessageId=23 Severity=Success Facility=Application Language=English %1 . MessageId=24 Severity=Success Facility=Application Language=English %1 . MessageId=25 Severity=Success Facility=Application Language=English %1 . MessageId=26 Severity=Success Facility=Application Language=English %1 . MessageId=27 Severity=Success Facility=Application Language=English %1 . MessageId=28 Severity=Success Facility=Application Language=English %1 . MessageId=29 Severity=Success Facility=Application Language=English %1 . MessageId=30 Severity=Success Facility=Application Language=English %1 . snmptt_1.5/snmptt-init.d0000775000000000000000000000324614277306025014137 0ustar rootroot#!/bin/bash # init file for snmptt # Alex Burger - 08/29/02 # - 09/08/03 - Added snmptt.pid support to Stop function # - 05/17/09 - Added LSB init keywords, change priority, add # INIT INFO. # chkconfig: - 49 51 # description: SNMP Trap Translator daemon # # processname: /usr/sbin/snmptt # pidfile: /var/run/snmptt.pid ### BEGIN INIT INFO # Provides: snmptt # Default-Stop: 0 1 6 # Required-Start: $syslog $local_fs # Required-Stop: $syslog $local_fs # Should-Start: $network snmptrapd # Should-Stop: $network snmptrapd # Short-Description: SNMP Trap Translator daemon ### END INIT INFO # source function library . /etc/init.d/functions OPTIONS="--daemon" RETVAL=0 prog="snmptt" start() { echo -n $"Starting $prog: " daemon /usr/sbin/snmptt $OPTIONS RETVAL=$? echo touch /var/lock/subsys/snmptt return $RETVAL } stop() { echo -n $"Stopping $prog: " killproc /usr/sbin/snmptt 2>/dev/null RETVAL=$? echo rm -f /var/lock/subsys/snmptt if test -f /var/run/snmptt.pid ; then [ $RETVAL -eq 0 ] && rm -f /var/run/snmptt.pid fi return $RETVAL } reload(){ echo -n $"Reloading config file: " killproc snmptt -HUP RETVAL=$? echo return $RETVAL } restart(){ stop start } condrestart(){ [ -e /var/lock/subsys/snmptt ] && restart return 0 } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload|force-reload) reload ;; try-restart|condrestart) condrestart ;; status) status snmptt RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|status|restart|try-restart|condrestart|reload|force-reload}" RETVAL=1 esac exit $RETVAL snmptt_1.5/snmptt-net-snmp-test0000775000000000000000000001652314277306025015472 0ustar rootroot#!/usr/bin/perl # # SNMPTT Net-SNMP-Test v1.0 # # Copyright 2002-2022 Alex Burger # alex_b@users.sourceforge.net # # 4/13/2003 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################## # # http://www.sourceforge.net/projects/snmptt # ############################################################################### # # OPTIONS START # # Allows you to set the MIBS environment variable used by SNMPTT # Leave blank or comment out to have the systems enviroment settings used # To have all MIBS processed, set to ALL $mibs_environment = "RFC1213-MIB"; # This sets the best_guess parameter used by the UCD-SNMP / NET-SNMP Perl module for # translating symbolic nams to OIDs and vice versa. # For UCD-SNMP, and Net-SNMP 5.0.8 and previous versions, set this value to 0. # For Net-SNMP 5.1, or any Net-SNMP with patch 722075 applied, set this value to 2. # A value of 2 is equivalent to -IR on Net-SNMP command line utilities. # UCD-SNMP and Net-SNMP 5.0.8 and previous may not be able to translate certain formats of # symbolic names such as RFC1213-MIB::sysDescr. Net-SNMP 5.1 or patch 722075 will allow # all possibilities to be translated. $net_snmp_perl_best_guess = 1; # # OPTIONS END # ############################################################################# # $snmpttnet_snmp_test_version = "v1.0"; sub showversion { print "\nSNMPTT Net-SNMP Test $snmpttnet_snmp_test_version\n"; print "(c) 2003 Alex Burger\n"; print "http://snmptt.sourceforge.net\n\n"; } ############################################################################## # Process command line arguments $| = 1; use Getopt::Long; $DEBUGGING = 0; $version = 0; $debug = 0; $help = 0; GetOptions ('version' => \$version, 'help' => \$help, 'best_guess:i' => \$best_guess); if ($version == 1) { &showversion; exit(0); } if ($help == 1) { $USAGE = qq/This program is used to perform various translations of numeric and symbolic OIDS using the UCD-SNMP \/ NET-SNMP Perl module and the translateObj function. The test results should assist with determining if the installed Perl module will function as expected with SNMPTT. For UCD-SNMP, and Net-SNMP 5.0.8 and previous versions, use --best_guess=0. For Net-SNMP 5.1, or any Net-SNMP with patch 722075 applied, use --best_guess=2. Usage: net-snmp-test [] Options: --help Display this message --version Display author and version information --best_guess=n Display author and version information /; &showversion; print $USAGE . "\n"; exit(0); } use SNMP; &showversion; if (defined ($mibs_environment)) { $ENV{'MIBS'} = $mibs_environment; } $SNMP::save_descriptions = 1; &SNMP::initMib(); if (defined ($best_guess)) { $SNMP::best_guess = $best_guess; } else { $SNMP::best_guess = $net_snmp_perl_best_guess; } $include_module = 1; my $test; my $long_names; my $include_module; print "MIBS:$mibs_environment\n"; print "best_guess: $SNMP::best_guess\n"; print "\n\nTesting translateObj\n"; print "********************\n"; $test = '.1.3.6.1.2.1.1.1'; $expect = 'sysDescr'; $long_names = 0; $include_module = 0; &translate($test, $expect, $long_names, $include_module); $test = '.1.3.6.1.2.1.1.1'; $expect = 'RFC1213-MIB::sysDescr'; $long_names = 0; $include_module = 1; &translate($test, $expect, $long_names, $include_module); $test = '.1.3.6.1.2.1.1.1'; $expect = '.iso.org.dod.internet.mgmt.mib-2.system.sysDescr'; $long_names = 1; $include_module = 0; &translate($test, $expect, $long_names, $include_module); if ($SNMP::VERSION =~ /^4./) { print "Note: UCD-SNMP v$SNMP::VERSION detected. UCD-SNMP does not normally\n return the entire tree when requesting a long name\n"; } $test = '.1.3.6.1.2.1.1.1'; $expect = 'RFC1213-MIB::.iso.org.dod.internet.mgmt.mib-2.system.sysDescr'; $long_names = 1; $include_module = 1; &translate($test, $expect, $long_names, $include_module); $test = 'sysDescr'; $expect = '.1.3.6.1.2.1.1.1'; $long_names = 0; $include_module = 0; &translate($test, $expect, $long_names, $include_module); $test = 'RFC1213-MIB::sysDescr'; $expect = '.1.3.6.1.2.1.1.1'; $long_names = 0; $include_module = 0; &translate($test, $expect, $long_names, $include_module); $test = 'system.sysDescr'; $expect = '.1.3.6.1.2.1.1.1'; $long_names = 0; $include_module = 0; &translate($test, $expect, $long_names, $include_module); $test = 'RFC1213-MIB::system.sysDescr'; $expect = '.1.3.6.1.2.1.1.1'; $long_names = 0; $include_module = 0; &translate($test, $expect, $long_names, $include_module); $test = '.iso.org.dod.internet.mgmt.mib-2.system.sysDescr'; $expect = '.1.3.6.1.2.1.1.1'; $long_names = 0; $include_module = 0; &translate($test, $expect, $long_names, $include_module); print "\n\nTesting getType\n"; print "***************\n"; $test = '.1.3.6.1.2.1.4.1'; # ipForwarding $expect = 'INTEGER'; &my_getType($test, $expect,); $test = 'ipForwarding'; # ipForwarding $expect = 'INTEGER'; &my_getType($test, $expect,); print "\n\nTesting Description\n"; print "*******************\n"; $test = '.1.3.6.1.2.1.4.1'; # ipForwarding $expect = 'The indication of whether this entity is acting'; &my_getDescription($test, $expect,); print "\n"; sub translate { my $test = shift; my $expect = shift; my $long_names = shift; my $include_module = shift; print "\nTesting: $test, long_names=" . ($long_names == 1 ? "enabled" : "disabled") . ", include_module=" . ($include_module == 1 ? "enabled" : "disabled") . "\n"; $translated = &SNMP::translateObj("$test",$long_names,$include_module); if ($translated eq $expect) { print "Test passed. Result: $translated\n"; } else { print "Test FAILED! Expected: $expect\n"; print " Received: $translated\n"; } } sub my_getType { my $test = shift; my $expect = shift; print "\nTesting: $test\n"; $type = &SNMP::getType("$test"); if ($type eq $expect) { print "Test passed. Result: $type\n"; } else { print "Test FAILED! Expected: $expect\n"; print " Received: $type\n"; } } sub my_getDescription { my $test = shift; my $expect = shift; my $description_temp = $SNMP::MIB{$test}->{description}; $description_temp =~ s/\n\s+/\n/g; if ($description_temp =~ $expect) { print "Test passed. Result:\n"; print "-------------------------------------------------\n"; print "$description_temp\n"; print "-------------------------------------------------\n"; } else { print "Test FAILED! Expected a string containing:\n"; print "$expect\n\n"; print "Received:\n"; print "-------------------------------------------------\n"; print "$description_temp\n"; print "-------------------------------------------------\n"; } } snmptt_1.5/snmptt.ini0000664000000000000000000007322614277306025013534 0ustar rootroot# # SNMPTT v1.5 Configuration File # # Linux / Unix # [General] # Name of this system for $H variable. If blank, system name will be the computer's # hostname via Sys::Hostname. snmptt_system_name = # Set to either 'standalone' or 'daemon' # standalone: snmptt called from snmptrapd.conf # daemon: snmptrapd.conf calls snmptthandler # Ignored by Windows. See documentation mode = standalone # Set to 1 to allow multiple trap definitions to be executed for the same trap. # Set to 0 to have it stop after the first match. # This option should normally be set to 1. See the section 'SNMPTT.CONF Configuration # file Notes' in the SNMPTT documentation for more information. # Note: Wildcard matches are only matched if there are NO exact matches. This takes # into consideration the NODES list. Therefore, if there is a matching trap, but # the NODES list prevents it from being considered a match, the wildcard entry will # only be used if there are no other exact matches. multiple_event = 1 # SNMPTRAPD passes the IP address of device sending the trap, and the IP address of the # actual SNMP agent. These addresses could differ if the trap was sent on behalf of another # device (relay, proxy etc). # If DNS is enabled, the agent IP address is converted to a host name using a DNS lookup # (which includes the local hosts file, depending on how the OS is configured). This name # will be used for: NODES entry matches, hostname field in logged traps (file / database), # and the $A variable. Host names on the NODES line will be resolved and the IP address # will then be used for comparing. # Set to 0 to disable DNS resolution # Set to 1 to enable DNS resolution dns_enable = 0 # Set to 0 to enable the use of FQDN (Fully Qualified Domain Names). If a host name is # passed to SNMPTT that contains a domain name, it will not be altered in any way by # SNMPTT. This also affects resolve_value_ip_addresses. # Set to 1 to have SNMPTT strip the domain name from the host name passed to it. For # example, server01.domain.com would be changed to server01 # Set to 2 to have SNMPTT strip the domain name from the host name passed to it # based on the list of domains in strip_domain_list strip_domain = 0 # List of domain names that should be stripped when strip_domain is set to 2. # List can contain one or more domains. For example, if the FQDN of a host is # server01.city.domain.com and the list contains domain.com, the 'host' will be # set as server01.city. strip_domain_list = </dev/null 2>/dev/null || true endscript } snmptt_1.5/snmptt.service0000664000000000000000000000043314277306025014403 0ustar rootroot[Unit] Description=SNMP Trap Translator (SNMPTT) After=syslog.target network.target snmptrapd.service [Service] Type=forking ExecStart=/usr/sbin/snmptt --daemon RemainAfterExit=yes PIDFile=/var/run/snmptt.pid ExecReload=/bin/kill -HUP $MAINPID [Install] WantedBy=multi-user.target snmptt_1.5/snmpttconvert0000775000000000000000000000635114277306025014355 0ustar rootroot#!/usr/bin/perl # # SNMPTTCONVERT v1.5 # # Copyright 2002-2022 Alex Burger # alex_b@users.sourceforge.net # # 4/11/2002 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################## use strict; use warnings; # # http://www.sourceforge.net/projects/snmptt # # Debugging: 0 = no output messages # 1 = output some basic messages # 2 = out all messages use constant DEBUGGING => 0; # Set this to '' to have no default EXEC line added, or modify as needed. #$defaultexec = ''; my $defaultexec = '#EXEC qpage -f TRAP notifygroup1 "'; if (DEBUGGING >= 1) { print "\nLoading @ARGV\n"; } my @trapdconf; while (<>) { chomp; #remove at end of line s/\015//; # Remove any DOS carriage returns push(@trapdconf, $_); #add to each line to @trapconf array } if (DEBUGGING >= 1) { print "Finished loading\n\n"; } my $currentline=0; my ($line, $line2, $line3, $line4); my $temp; while ($currentline <= $#trapdconf) { $line = $trapdconf[$currentline]; # $_ = 'COMPAQ_11001 {.1.3.6.1.4.1.232} 6 11001 A "LOGONLY" 1'; # enterprise = .1.3.6.1.4.1.232 # 6 means it's an enterprise trap so do enterprise.0.specific below # specific = 11001 if ( $line =~ /(\w+)\s+\{(.*)\}\s+(\d+)\s+(\d+)\s+([CAMcam-])\s+(".+").*/ ) { if ($3 == 6) { $temp = "EVENT $1 $2.0.$4 $6 Normal"; } else { $temp = "EVENT $1 $2.$4 $6 Normal"; # Not sure if this is correct } print "#\n#\n#\n"; print "$temp\n"; $currentline++; # Increment to the next line $line3 = $trapdconf[$currentline]; # FORMAT line print "FORMAT $line3\n"; if ($defaultexec ne '') { print $defaultexec.$line3,"\"\n"; } $currentline++; # Increment to the next line $line3 = $trapdconf[$currentline]; while ( ($currentline <= $#trapdconf) && !($line3 =~ /(\w+)\s+\{(.*)\}\s+(\d+)\s+(\d+)\s+([CAMcam])\s+(".+").*/ ) ) { # Keep going through the file until the next EVENT or the end of trapd.conf # is reached # Check to see if next line is a FORMAT line (it should be!) if ($line3 =~ /^SDESC/) { # It's a SDESC line print "SDESC\n"; $currentline++; # Increment to the next line $line4 = $trapdconf[$currentline]; while (! ($line4 =~ /^EDESC/) ) { print $line4,"\n"; $currentline++; # Increment to the next line $line4 = $trapdconf[$currentline]; } print "EDESC\n"; } $currentline++; # Increment to the next line $line3 = $trapdconf[$currentline]; } $currentline--; } $currentline++; # Increment to the next line $line2 = $trapdconf[$currentline]; # Get next line } snmptt_1.5/snmpttconvertmib0000775000000000000000000011411214277306025015040 0ustar rootroot#!/usr/bin/perl # # SNMPTTCONVERTMIB v1.5 # # Copyright 2002-2022 Alex Burger # alex_b@users.sourceforge.net # # 8/14/2002 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################## # # http://www.sourceforge.net/projects/snmptt # ############################################################################### use strict; use warnings; # # OPTIONS START # # Set this to '' to have no default EXEC line added, or modify as needed. # Can also set on the command line with --exec='string' my $defaultexec = ''; # Choose what type of quotes (if any) you want around the SUMMARY text pulled from the MIB. #$defaultexecquote = ''; # no quotes #$defaultexecquote = "\'"; # single (') quotes my $defaultexecquote = "\""; # double (") quotes # Set this to 1 to have the --TYPE string prepended to the --SUMMARY string. # Set to 0 to disable my $prepend_type = 1; # # OPTIONS END # ############################################################################# # my $snmpttconvertmib_version = "v1.5"; sub showversion { print "\nSNMPTTCONVERTMIB $snmpttconvertmib_version\n"; print "(c) 2002-2021 Alex Burger\n"; print "http://snmptt.sourceforge.net\n\n"; } ############################################################################## # Process command line arguments $| = 1; use Getopt::Long; use File::Basename; use File::Spec; my $DEBUGGING = 0; my $version = 0; my $debug = 0; my $help = 0; my $net_snmp_perl = 0; my $in = ''; my $out = ''; my $nodes = ''; my $no_description = 0; my $no_variables = 0; my $no_format_summary = 0; my $no_format_desc = 0; my $format = 0; my $format_desc = 0; my $no_desc_wildcard = 0; my $no_severity = 0; my $severity = 'Normal'; my $exec = ''; my $exec_mode = 0; my $exec_file = ''; my $preexec = ''; my $preexec_file = ''; my @exec_array = (); my @preexec_array = (); GetOptions ('version' => \$version, 'debug:i' => \$debug, 'help' => \$help, 'in=s' => \$in, 'out=s' => \$out, 'net_snmp_perl' => \$net_snmp_perl, 'nodes=s' => \$nodes, 'no_description' => \$no_description, 'no_variables' => \$no_variables, 'no_format_summary' => \$no_format_summary, 'no_format_desc' => \$no_format_desc, 'no_severity' => \$no_severity, 'severity=s' => \$severity, 'format=n' => \$format, 'format_desc=n' => \$format_desc, 'no_desc_wildcard' => \$no_desc_wildcard, 'exec_mode=n' => \$exec_mode, 'exec_file=s' => \$exec_file, 'exec=s' => \$exec, 'preexec_file=s' => \$preexec_file, 'preexec=s' => \$preexec); if ($version == 1) { &showversion; exit(0); } if ($help == 1) { &show_help(); exit(0); } # Replace any spaces with -'s in severity $severity =~ s/ /-/g; if ($debug == 1) { $DEBUGGING = 1; } if ($debug == 2) { $DEBUGGING = 2; } if (($in eq "") || ($out eq "")) { print "\n**** Missing arguments! ****\n"; &show_help(); exit 1; } if (($exec ne "") && ($exec_file ne "")) { print "\n**** Please specify either --exec or --exec_file, not both. ****\n"; &show_help(); exit 1; } if (($preexec ne "") && ($preexec_file ne "")) { print "\n**** Please specify either --preexec or --preexec_file, not both. ****\n"; &show_help(); exit 1; } # Get complete path of input file (MIB) in a portable way (needed for -m switch for snmptranslate) my $dirname = dirname $in; my $basename = basename $in; my $input = File::Spec->catfile($dirname, $basename); # Get complete path of output file (.conf) in a portable way $dirname = dirname $out; $basename = basename $out; my $output = File::Spec->catfile($dirname, $basename); if (($exec eq "") && ($exec_file eq "")) { push(@exec_array, $defaultexec); } if ($exec ne '') { #$defaultexec = $exec; push(@exec_array, $exec); print "exec: $exec\n"; } if ($preexec ne '') { push(@preexec_array, $preexec); print "preexec: $preexec\n"; } #print "nodes: $nodes\n"; if ($net_snmp_perl == 1) { print "\n\n***** UCD-SNMP / NET-SNMP Perl module enabled *****\n\n"; } print "\n\n***** Processing MIB file *****\n\n"; my $snmptranslate_use_On; check_snmptranslate_version(); print "severity: $severity\n"; print "\nFile to load is: $input\n"; print "File to APPEND TO: $output\n"; if ($exec) { print "Exec argument: $exec\n"; } if ($exec_file) { print "Exec file: $exec_file\n"; } if ($preexec) { print "PreExec argument: $preexec\n"; } if ($preexec_file) { print "PreExec file: $preexec_file\n"; } # Set MIBS environment variable to the filename of the MIB file (not the mib name - if a file contains # multiple MIB definitions in one file, the mib name will not work - at least with 5.0.8 and older) $ENV{MIBS} = $input; print "\nMIBS environment var: $ENV{MIBS}\n"; ################################### # Load input file if ($DEBUGGING >= 1) { print "\nLoading $input\n"; } my $fh_INPUTFILE; unless (open $fh_INPUTFILE, "<", $input) { die "Cannot open input file: $!"; } my @mibfile; while (<$fh_INPUTFILE>) { chomp; # remove at end of line s/\015//; # Remove any DOS carriage returns push(@mibfile, $_); # add to each line to @trapconf array } close $fh_INPUTFILE; if ($DEBUGGING >= 1) { print "Finished loading $input\n\n"; } ################################### # Load exec_file if ($exec_file) { if ($DEBUGGING >= 1) { print "\nLoading $exec_file\n"; } my $fh_EXECFILE; unless (open $fh_EXECFILE, "<", $exec_file) { die "Cannot open input file: $!"; } while (<$fh_EXECFILE>) { chomp; # remove at end of line s/\015//; # Remove any DOS carriage returns push(@exec_array, $_); # add to each line to @trapconf array } close $fh_EXECFILE; if ($DEBUGGING >= 1) { print "Finished loading $exec_file\n\n"; } } my $currentline=0; if ($exec_array[0] ne "") { print "Exec(s): \n"; foreach my $temp (@exec_array) { print " $temp\n"; } } ################################### # Load preexec_file if ($preexec_file) { if ($DEBUGGING >= 1) { print "\nLoading $preexec_file\n"; } my $fh_PREEXECFILE; unless (open $fh_PREEXECFILE, "<", $preexec_file) { die "Cannot open input file: $!"; } while (<$fh_PREEXECFILE>) { chomp; # remove at end of line s/\015//; # Remove any DOS carriage returns push(@preexec_array, $_); # add to each line to @trapconf array } close $fh_PREEXECFILE; if ($DEBUGGING >= 1) { print "Finished loading $preexec_file\n\n"; } } $currentline=0; if ($preexec_array[0] ne "") { print "PreExec(s): \n"; foreach my $temp (@preexec_array) { print " $temp\n"; } } ################################### # Open output file my $fh_OUTPUTFILE; unless (open $fh_OUTPUTFILE, ">>", $output) { die "Cannot open output file: $!"; } ################################### # A mib file can contain multiple BEGIN definitions. This finds the first one # to make sure we have at least one definition. # Determine name of MIB file my $mib_name = ''; while ($currentline <= $#mibfile) { my $line = $mibfile[$currentline]; # Sometimes DEFINITIONS ::= BEGIN will appear on the line following the mib name. # Look for DEFINITIONS ::= BEGIN with nothing (white space allowed) around it and a previous line with # only a single word with whitespace around it. if ($currentline > 0 && $line =~ /^\s*DEFINITIONS\s*::=\s*BEGIN\s*$/ && $mibfile[$currentline-1] =~ /^\s*(\S+)\s*$/) { # We should have found the mib name $mib_name = $1; print "\nSplit line DEFINITIONS ::= BEGIN found ($1).\n"; $mib_name =~ s/\s+//g; last; } elsif ($line =~ /(.*)DEFINITIONS\s*::=\s*BEGIN/) { $mib_name = $1; $mib_name =~ s/\s+//g; last; } $currentline++; } print "MIB name: $mib_name\n"; if ($mib_name eq '') { print "\n\nAborting!!!\n"; print "Could not find DEFINITIONS ::= BEGIN statement in MIB file!\n\n"; exit (1); } if ($net_snmp_perl == 1) { require SNMP; { no warnings; # Variable is only used once $SNMP::save_descriptions = 1; # Need them only for looking up variable descriptions. # Do TRAP definition by hand to be able to pull out # the SUMMARY lines } &SNMP::initMib(); print "\n\n***** Using UCD-SNMP / NET-SNMP Perl module *****\n\n"; } my $total_translations = 0; my $successful_translations = 0; my $failed_translations = 0; $currentline=0; #if ($net_snmp_perl == 0) if (1) { # Process the trap files by hand while ($currentline <= $#mibfile) { my $line = $mibfile[$currentline]; # Sometimes DEFINITIONS ::= BEGIN will appear on the line following the mib name. # Look for DEFINITIONS ::= BEGIN with nothing (white space allowed) around it and a previous line with # only a single word with whitespace around it. if ($currentline > 0 && $line =~ /^\s*DEFINITIONS\s*::=\s*BEGIN\s*$/ && $mibfile[$currentline-1] =~ /^\s*(\S+)\s*$/) { # We should have found the mib name print "\n\nSplit line DEFINITIONS ::= BEGIN found ($1).\n"; $mib_name = $1; $mib_name =~ s/\s+//g; print "Processing MIB: $mib_name\n"; print $fh_OUTPUTFILE "#\n#\n#\n#\n"; print $fh_OUTPUTFILE "MIB: $mib_name (file:$input) converted on " . scalar(localtime) . " using snmpttconvertmib $snmpttconvertmib_version\n"; $currentline++; # Increment to the next line next; } elsif ($line =~ /(.*)DEFINITIONS\s*::=\s*BEGIN/) { $mib_name = $1; $mib_name =~ s/\s+//g; print "\n\nProcessing MIB: $mib_name\n"; print $fh_OUTPUTFILE "#\n#\n#\n#\n"; print $fh_OUTPUTFILE "MIB: $mib_name (file:$input) converted on " . scalar(localtime) . " using snmpttconvertmib $snmpttconvertmib_version\n"; $currentline++; # Increment to the next line next; } # TRAP-TYPE (V1) / NOTIFICATION-TYPE (V2) # # eg: 'mngmtAgentTrap-23003 TRAP-TYPE'; # eg: 'ciscoSystemClockChanged NOTIFICATION-TYPE'; if ( $line =~ /(.*)\s*TRAP-TYPE.*/ || $line =~ /(.*)\s*(?