On the 5GHz WiFi band we have a lot of channels to choose from. More than 16 depending on the country your in. That in comparison with 2.4GHz where we only have 3 non overlapping channels (1, 6 and 11).
Most of the 5GHz channels are unfortunately shared with radar. If a access point detects a radar signal it has to leave the channel for at least 30 minutes. This is called Dynamic Frequency Selection (DFS). Here in Sweden there are only four channels (36, 40, 44, 48) that are not DFS channels.
When I design a WiFi network I want to avoid radar events , or DFS-events which I’m going to call it from now, but I still like to use more than four channels on the 5GHz band. The solution I use is to change the channel plan on that office/building/floor when I recieve a DFS-event to avoid the usage of that specific channel that generated the alarm.
Different vendors use different ways to report the events, at my company we use Cisco WLC and it only reports DFS-events via SNMP-traps. You can see the traps at the Advanced menu in the GUI under ”Most Recent Traps”, but I have better things to do than looking at a log all day.
You might be able to use Cisco Prime to catch the events but I have not found a good way to configure that. I looked at some other solutions too but did not find what I was looking for. I also asked a bunch of people but it seems that no one has a good solution to the problem, so I decided to write a small program to have a easy way to catch and display the traps.
First I tried some different Python SNMP libraries but I did not get the Cisco MIB to play along. I had recently played a bit with the pyshark (https://kiminewt.github.io/pyshark/) library so I decided to use it. It’s a wrapper for tshark that is included in Wireshark, and it’s able in a relative simple way to capture a packet so it can be manipulated with Python.
My program is really simple and just prints the log to the screen when an event occurs. It’ easy to extend it to write to a log file, syslog message or even send a mail (tried that but got annoyed by all the mails), but I leave that to you. When I run the program I have commented out the line that says the event is cleared, I already know that it will be after 30 minutes. In my environment the code runs under Linux Red Hat Enterprise 7.5 with Python 2.7.5, TShark 1.10.14 and pyshark 0.3.7.11. To use the default SNMP-Trap port udp:162 you have to run the script as root.
You have to set up the Cisco WLC to send traps to the computer witch runs the script. To see every trap that are being sent to the script and not just the DFS events set the global variable DEBUG = True.
#!/usr/bin/python VERSION = '0.01' """ Listens for Cisco WLC SNMP-TRAPS with DFS events and write information to the console. The code uses pyshark to capture the SNMP-packet, so Wireshark/tshark needs to be installed on the system. Ref: Cisco MIBs: http://www.mibdepot.com/cgi-bin/getmib3.cgi?win=mib_a&i=1&n=AIRESPACE-WIRELESS-MIB&r=cisco&f=AIRESPACE-WIRELESS-MIB.my&v=v2&t=tree Dan Larsson | https://sa3ark.se/ | Summer of 2018 """ import pyshark import time TRAP_PORT = 162 INTERFACE = 'eth0' DEBUG = False # Set to True to show every packet. def main(): """ Main loop """ print('- Open listen socket on port %i' % TRAP_PORT) cap = pyshark.LiveCapture(interface=INTERFACE, bpf_filter='port '+str(TRAP_PORT)) print('- Start listening for traps. Stop with ctrl-c') try: for packet in cap.sniff_continuously(): if DEBUG: print(packet) snmp_trap(packet) except: print('- Shutting down!') exit() def snmp_trap(data): """ Looks for specific traps that refer to DFS/Radar events. Prints information about the event. :param data: pyshark packet """ # DFS event Detected if data.snmp.value_oid == '1.3.6.1.4.1.14179.2.6.3.81': ap_name, ap_channel = decode_snmp_trap(data.snmp) print('%s -> DFS event Detected on %s at channel %s' % (time.ctime(), ap_name, ap_channel)) # DFS event Cleared elif data.snmp.value_oid == '1.3.6.1.4.1.14179.2.6.3.82': ap_name, ap_channel = decode_snmp_trap(data.snmp) print('%s -> DFS event Cleared on %s at channel %s' % (time.ctime(), ap_name, ap_channel)) def decode_snmp_trap(raw_trap): """ Decode trap data and return, AP-NAME and Channel number :param raw_trap: Raw trap data :return: ap_name, ap_channel """ trap_list = str(raw_trap).split('\n') ap_name = trap_list[20].split(' ')[2].strip().decode('hex') ap_channel = trap_list[21].split(' ')[2].strip() return(ap_name, ap_channel) if __name__ == '__main__': main()