I've got a fun little problem that I have hacked together a solution for, but I'm thinking there's probably a more elegant way of going about it.
(I also had one heck of a time googling any information on the hurdles I had to face, so while I'm looking for suggestions on cleaning this up, I also hope this may be of help to others who are trying to do the same sort of thing)
My managed switch has quite a lot of capabilities, but what it doesn't have is the ability to email me when one of my network drops disconnects. (It's for a wildlife camera that I have a long ethernet cable to, and I want to know immediately if it is ever stolen or drops offline or whatever.)
Of course, I immediately thought "Sounds like a Raspberry Pi project!" So I configured the switch to kick out an snmptrap packet to the IP Address of my Raspberry Pi, gave it a custom community ID of WLDLifesnmp, and just turned on the 'link down' notification in the management interface of the switch.
On the Pi:
sudo apt-get install snmptrapd sudo apt-get install snmpd sudo apt-get install snmp
The above installs everything I needed, (maybe even more than I needed, not sure) and the next step was to tweak the default snmptrapd.conf file in /etc/snmp/
# /etc/snmp/snmptrapd.conf # # EXAMPLE-trap.conf: # An example configuration file for configuring the Net-SNMP snmptrapd agent. ######################################################################## # # This file is intended to only be an example. # When the snmptrapd agent starts up, this is where it will look for it. # # All lines beginning with a '#' are comments and are intended for you # to read. All other lines are configuration commands for the agent. # # PLEASE: read the snmptrapd.conf(5) manual page as well! # authCommunity log,execute,net private authCommunity log,execute,net public #added the following for my custom community authCommunity log,execute,net WLDLifesnmp # #traphandle OID, <<path to handler script>>
Full disclosure: I know next to nothing about snmp in a large sense. "just enough to be dangerous" so I left the log and net in the community. I could probably get away with just the execute for the WLDLifesnmp community, but it works so I left it. The most important part was finding the exact OID that was being tripped when that particular port on the switch disconnected. So I manually ran snmptrapd from the command line, just to watch what came through:
>sudo snmptrapd -f -Lo -c /etc/snmp/snmptrapd.conf NET-SNMP version 126.96.36.199 AgentX subagent connected NET-SNMP version 188.8.131.52
and what I got was a bit of a mess (this is from a connection, not disconnection):
NET-SNMP version 184.108.40.206 2017-07-27 18:04:17 <UNKNOWN> [UDP: [172.24.3.13]:42290-> [172.24.3.29]:162]:iso.220.127.116.11.18.104.22.168 = Timeticks: (100893) 0:16:48.93 iso.22.214.171.124.126.96.36.199.1.0 = OID: iso.188.8.131.52.184.108.40.206.5.4 iso.220.127.116.11.18.104.22.168.1.17 = INTEGER: 17 iso.22.214.171.124.126.96.36.199.7.17 = INTEGER: 1 iso.188.8.131.52.184.108.40.206.8.17 = INTEGER: 1
The key to this whole process is that last line in the snmptrapd.conf file: the traphandle. Once I could see the OID that was being triggered from the connection/disconnection, I could add the capture to the conf file: (connection is ' OID: iso.220.127.116.11.18.104.22.168.5.4' disconnection is ' OID: iso.22.214.171.124.126.96.36.199.5.3')
#traphandle OID, <<path to handler script>> #handle the disconnection trap: traphandle 188.8.131.52.184.108.40.206.5.3 /usr/local/bin/snmp/snmpdisco.sh
I created the /usr/local/bin/snmp/snmpdisco.sh file with just the following in it, echoing the results to a text file so I could try to make sense of it:
cd /usr/local/bin mkdir snmp cd snmp sudo nano snmpdisco.sh
entering the following
#!/bin/bash read host read ip vars= while read oid val do if [ "$vars" = "" ] then vars="$oid = $val" else vars="$vars, $oid = $val" fi done echo trap: $1 $host $ip $vars >>/var/log/snmptest.log
be sure to make it executable:
sudo chmod +x snmpdisco.sh
Then I re-ran from the command line the snmptrapd function, unplugged and replugged a few network cables on the switch to see what I'd get... if anything. Hopefully something. I DID! I got the following mess in the /var/log/snmptest.log file:
trap: UDP: [172.24.3.13]:42290->[172.24.3.29]:162 iso.220.127.116.11.18.104.22.168 = 1:5:13:19.16, iso.22.214.171.124.126.96.36.199.1.0 = iso.188.8.131.52.184.108.40.206.3, iso.220.127.116.11.18.104.22.168.1.39 = 39, iso.22.214.171.124.126.96.36.199.7. 39 = 1, iso.188.8.131.52.184.108.40.206.8.39 = 2 trap: UDP: [172.24.3.13]:42290->[172.24.3.29]:162 iso.220.127.116.11.18.104.22.168 = 1:5:14:20.83, iso.22.214.171.124.126.96.36.199.1.0 = iso.188.8.131.52.184.108.40.206.3, iso.220.127.116.11.18.104.22.168.1.35 = 35, iso.22.214.171.124.126.96.36.199.7. 35 = 1, iso.188.8.131.52.184.108.40.206.8.35 = 2
this is not going to be easy to figure out which port is tripped. I only want to notify if it's port 17 on my switch, not the rest of them (the above is port 39 and 35, respectively).. I have to somehow figure out how to strip that info out, but I have no idea how to do that with bash scripting. (again, I know enough about bash scripting to be dangerous) Python, however, I like Python, so maybe I can export the results to a little Python script and see what sort of packaging is being used here. It sort of looks like it's somewhat of a JSON package or something, I could not find any documentation on what sort encapsulation the MIB is using under the hood. So... enter a tiny python3 script that should run through each chunk of $vars, and write the contents on a single line to a text file:
#/usr/local/bin/snmp/snmptest.py import sys f = open("testexport.txt","w") for arg in sys.argv: f.write(arg+"\n") f.close()
Now, maybe I can export the results from the bash script over to the python script instead, and parse through whatever is being sent, so I can at least sort of reverse engineer the capsule to see how I can grab that port number:
Tail end of the snmpdisco.sh script:
... fi done echo trap: $1 $host $ip $vars >>/var/log/snmptest.log python3 snmptest.py $vars
Run the physical disconnection again, and check the output of the python created text file: WOOT! Looks like whatever that package is, is an array of 16 chunks:
##contents of testexport.txt snmptest.py iso.220.127.116.11.18.104.22.168 = 1:5:45:27.31, iso.22.214.171.124.126.96.36.199.1.0 = iso.188.8.131.52.184.108.40.206.3, iso.220.127.116.11.18.104.22.168.1.22 = 22, iso.22.214.171.124.126.96.36.199.7.22 = 1, iso.188.8.131.52.184.108.40.206.8.22 = 2
Bingo, there it is. In the above example, it was port 22 I disconnected, and there it is in all it's glory, all by itself in argv.
I think I've done it, as messy as it may be. The above process will trip on ANY disconnection of the switch, but the Python script can filter out the noise and only look for port 17 of the switch. I think I'll trip an LED as well, so I have a visual alert, but at this juncture the only thing left to do is add a traphandle for the reverse (a reconnection).
So as happy as I may be to have hacked my way through this, it does seem like a messy inelegant way of doing it. I'm open to suggestions on how to clean this up a bit.