Last Update: 2022-07-28

snmpd & traps

Using OpenBSD's snmpd to handle SNMP-traps and write your own trap handler.

1  goals

This document explains how to configure OpenBSD's snmpd to receive SNMP traps. We'll also cover the implementation of a generic trap handler, aswell as a specialized one.

2  snmpd

OpenBSD has its own SNMP daemon (snmpd), the only downside is the missing SNMPv3 trap support. First of all, we'll need a configuration file /etc/snmpd.conf:
listen on udp 192.168.0.100 notify snmpv1 snmpv2c
trap community "TEST"
trap handle 1.3.6.1.4.1.11.2.14.11.5.1.12.1.5.0.1 "/home/_snmpd/loop.sh"
trap handle default "/home/_snmpd/traphandler.sh"
SNMP traps are received on the UDP port 162, here snmpd only listens on the address 192.168.0.100. The daemon will handle version 1 and 2c messages. As a "security" feature the community is set to TEST.
There are two trap handlers:
  1. A special trap handler loop.sh for the OID 1.3.6.1.4.1.11.2.14.11.5.1.12.1.5.0.1, which translates to hpicfBridgeLoopProtectLoopDetectedNotification for an old HP switch. It will be descriped in chapter 3.2.
  2. A default trap handler traphandler.sh for all other SNMP traps, which we'll cover in 3.1.
Next lets verify the configuration and start snmpd:
# snmpd -n -v
# rcctl enable snmpd
# rcctl start snmpd
You may need to add a filter rule to /etc/pf.conf:
pass in quick on $ext_if proto udp to port 162
where $ext_if is your network interface. Now load the current ruleset:
# pfctl -f /etc/pf.conf

3  trap handler

For our generic trap handler we first need a directory:
# mkdir /home/_snmpd
# chown _snmpd:_snmpd /home/_snmpd
# chmod 700 /home/_snmpd
To write a trap handler, it's a good idea to have a look at the snmpd.conf(5) manpage, quote:
"The invoked command will receive the following information about the trap on standard input, one per line, in this order: the resolved hostname of the host sending the trap, the IP address of the host sending the trap, and any variable bindings contained in the trap (the OID followed by the value, separated by a single space)."
The important thing to understand is that snmpd calls the handler and the information is passed via standard input.

3.1  default trap handler

Here is a primitive generic trap handler, which handles all traps without a specialized handler (/home/_snmpd/traphandler.sh):
#!/bin/sh

read -r var1
read -r var2
read -r var3
read -r var4
read -r var5
read -r var6
read -r var7
read -r var8
read -r var9
read -r var10

echo "TRAP:" >> /home/_snmpd/trap.log
echo "1:  $var1" >> /home/_snmpd/trap.log
echo "2:  $var2" >> /home/_snmpd/trap.log
echo "3:  $var3" >> /home/_snmpd/trap.log
echo "4:  $var4" >> /home/_snmpd/trap.log
echo "5:  $var5" >> /home/_snmpd/trap.log
echo "6:  $var6" >> /home/_snmpd/trap.log
echo "7:  $var7" >> /home/_snmpd/trap.log
echo "8:  $var8" >> /home/_snmpd/trap.log
echo "9:  $var9" >> /home/_snmpd/trap.log
echo "10: $var10" >> /home/_snmpd/trap.log
echo >> /home/_snmpd/trap.log
Don't forget to make your handler executable:
# chmod 700 traphandler.sh
You'll see traps like this in /home/_snmpd/trap.log:
TRAP:
1:  192.168.0.10
2:  192.168.0.10
3:  iso.org.dod.internet.mgmt.mib_2.system.sysUpTime.0 70034
4:  iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTrap.snmpTrapOID.0 
        iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTraps.linkDown
5:  iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifIndex.4 4
6:  iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifAdminStatus.4 2
7:  iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifOperStatus.4 2
8:  iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifDescr.4 "4"
9:  iso.org.dod.internet.mgmt.mib_2.ifMIB.ifMIBObjects.ifXTable.ifXEntry.ifAlias.4 ""
10:
If you don't want snmpd to convert the OIDs, use the -N flag:
# rcctl set snmpd flags -N
# rcctl restart snmpd

3.2  loop trap

Now for the special trap handler. The test device is an old HP 2915 switch, which can be configured to send out SNMP traps if a loop is detected. The idea is the once a loop is detected, the loop handler does a SNMP request to the switch to get its location and mails all the information to user@localhost.

First we have to configure the switch:
> enable
# configure terminal
(config)# loop-protection 1-8
(config)# loop-protection trap loop-detected
(config)# loop-protection disable-timer 300
(config)# snmp-server community "TEST" restricted
(config)# snmp-server location "Room 123"
(config)# snmp-server host 192.168.0.5 community TEST trap-level not-info
(config)# snmp-server enable
(config)# write memory
Now lets have a look at the loop information, which you can get from the generic trap handler of the last chapter:
1:  192.168.0.10
2:  192.168.0.10
3:  iso.org.dod.internet.mgmt.mib_2.system.sysUpTime.0 71458
4:  iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects.snmpTrap.snmpTrapOID.0
        iso.org.dod.internet.private.enterprises.hp.2.14.11.5.1.12.1.5.0.1
5:  iso.org.dod.internet.mgmt.mib_2.interfaces.ifTable.ifEntry.ifIndex.7 7
6:  iso.org.dod.internet.private.enterprises.hp.2.14.11.5.1.12.1.5.2.1.1.4.7 10
7:  iso.org.dod.internet.private.enterprises.hp.2.14.11.5.1.12.1.5.2.1.1.5.4 8

Most important are the arguments 1 or 2 and 4, which is the IP address of the switch on which the loop has happend and the switch port on which it was detected.

Here is the loop trap handler /home/_snmpd/loop.sh:
#!/bin/sh

read -r src
read -r
read -r
read -r
read -r iface

iface=`echo $iface | cut -d ' ' -f 2`

location=`snmp get -v 1 -c TEST $src 'sysLocation.0' | cut -d ' ' -f 4`

echo "Loop detected!\n\nHost: $src\nInterface: $iface\nLocation: $location" \
	| mail -r "snmpd@localhost" -s "Loop detected!" user@localhost
First we get the switch port, on which the loop was detected. Then there is an SNMP request to the switch for the sysLocation.0 variable, using the community "TEST". The last line is for the E-Mail delivery, which of cause only works if smtpd is running. And don't forget to make the script executable:
# chmod 700 loop.sh
You can read your mails using the mail command:
From snmpd@localhost Fri Jul 29 13:33:18 2022
Delivered-To: user@localhost
From: snmpd@localhost
To: user@localhost
Subject: Loop detected!

Loop detected!

Host: 192.168.0.10
Interface: 7
Location: Room 123