In a previous post we talked about how to set a WiFi interface to monitor mode manually, but also through a script. In this post, we’ll talk about how to set the channel and the channel width on a monitor interface but also now to do channel hopping while running a packet capture.
For the purposes of this post I use the same setup with the previous post in terms of hardware, WiFi dongle, driver and iw version.
Supported Channels and Widths
In order to be able to set 80 MHz channels with iw, you need version 4.14 or greater. In my case, I have installed version 5.9:
iw --version iw version 5.9
The syntax of settings channels has changed after version 4.14, and in order to get the correct syntax you can enter an incomplete command to get the help output as follows:
netbeez$ iw dev wlan0 set channel Usage: iw [options] dev <devname> set channel <channel> [NOHT|HT20|HT40+|HT40-|5MHz|10MHz|80MHz] Options: --debug enable netlink debugging
Once you set the interface to monitor mode the channel and channel width are left at the same configuration they were while the interface was connected to your WiFi network (if it was ever). In my case, it was on channel 149 at 80MHz:
netbeez$ iw wlan0 info Interface wlan0 ifindex 3 wdev 0x1 addr 20:0d:b0:47:57:79 type monitor wiphy 0 channel 149 (5745 MHz), width: 80 MHz, center1: 5775 MHz txpower 18.00 dBm
As always, when setting a channel and channel width we have to make sure that
- They have acceptable values (e.g. we can’t set channel 1 at 40- MHz)
- The driver and hardware support the requested channel and width values
The first step it to determine what channels your setup supports with the following:
netbeez$ iw list Wiphy phy0 wiphy index: 0 max # scan SSIDs: 9 max scan IEs length: 2304 bytes max # sched scan SSIDs: 0 max # match sets: 0 max # scan plans: 1 max scan plan interval: -1 . . . . . . . . . Frequencies: * 2412 MHz [1] (20.0 dBm) * 2417 MHz [2] (20.0 dBm) * 2422 MHz [3] (20.0 dBm) * 2427 MHz [4] (20.0 dBm) * 2432 MHz [5] (20.0 dBm) * 2437 MHz [6] (20.0 dBm) * 2442 MHz [7] (20.0 dBm) * 2447 MHz [8] (20.0 dBm) * 2452 MHz [9] (20.0 dBm) * 2457 MHz [10] (20.0 dBm) * 2462 MHz [11] (20.0 dBm) * 2467 MHz [12] (20.0 dBm) (no IR) * 2472 MHz [13] (20.0 dBm) (no IR) * 2484 MHz [14] (20.0 dBm) (no IR) . . . . . . . . . Frequencies: * 5180 MHz [36] (20.0 dBm) (no IR) * 5200 MHz [40] (20.0 dBm) (no IR) * 5220 MHz [44] (20.0 dBm) (no IR) * 5240 MHz [48] (20.0 dBm) (no IR) * 5260 MHz [52] (20.0 dBm) (no IR, radar detection) * 5280 MHz [56] (20.0 dBm) (no IR, radar detection) * 5300 MHz [60] (20.0 dBm) (no IR, radar detection) * 5320 MHz [64] (20.0 dBm) (no IR, radar detection) * 5500 MHz [100] (20.0 dBm) (no IR, radar detection) * 5520 MHz [104] (20.0 dBm) (no IR, radar detection) * 5540 MHz [108] (20.0 dBm) (no IR, radar detection) * 5560 MHz [112] (20.0 dBm) (no IR, radar detection) * 5580 MHz [116] (20.0 dBm) (no IR, radar detection) * 5600 MHz [120] (20.0 dBm) (no IR, radar detection) * 5620 MHz [124] (20.0 dBm) (no IR, radar detection) * 5640 MHz [128] (20.0 dBm) (no IR, radar detection) * 5660 MHz [132] (20.0 dBm) (no IR, radar detection) * 5680 MHz [136] (20.0 dBm) (no IR, radar detection) * 5700 MHz [140] (20.0 dBm) (no IR, radar detection) * 5720 MHz [144] (20.0 dBm) (no IR, radar detection) * 5745 MHz [149] (20.0 dBm) (no IR) * 5765 MHz [153] (20.0 dBm) (no IR) * 5785 MHz [157] (20.0 dBm) (no IR) * 5805 MHz [161] (20.0 dBm) (no IR) * 5825 MHz [165] (20.0 dBm) (no IR) * 5845 MHz [169] (disabled) * 5865 MHz [173] (disabled) * 5885 MHz [177] (disabled) . . . . . . . . .
This output is very useful, but it doesn’t specify explicitly what widths are allowed and supported on each channel. It can be determined by trying to set each width for each channel to see if the driver allows you to avoid throwing any errors or warning messages. The following two commands will set the channel and also confirm that the channel has been set properly:
netbeez$ iw dev wlan0 set channel 149 80MHz netbeez$ iw wlan0 info Interface wlan0 ifindex 3 wdev 0x1 addr 20:0d:b0:47:57:79 type monitor wiphy 0 channel 149 (5745 MHz), width: 80 MHz, center1: 5715 MHz txpower 18.00 dBm
If you choose a channel and width combination that is not acceptable you will get an error as follows:
netbeez$ iw dev wlan0 set channel 1 80MHz command failed: Invalid argument (-22)
Given that there are 14 channels in 2.4 GHz, 25 in 5GHz, and that you have four channel options (HT20, HT40+, HT40-, 80MHz) you would need to run 156 commands to get that information. Of course, that’s not practical and here is a rudimentary script that takes the channel option as input and prints in the output if the specific channel and width combination are acceptable:
#!/bin/bash channels_24="1 2 3 4 5 6 7 8 9 10 11 12 13 14" channels_50="36 40 44 48 52 56 60 64 100 104 108 112 116 120 124 128 132 136 140 144 149 153 157 161 165" channel_width_20="HT20" channel_width_40plus="HT40+" channel_width_40minus="HT40-" channel_width_80="80MHz" channels="${channels_24} ${channels_50}" if [[ "${1}" != "HT20" ]] && [[ "${1}" != "HT40-" ]] && [[ "${1}" != "HT40+" ]] && [[ "${1}" != "80MHz" ]]; then echo "Wrong input" echo "Available channel widths HT20, HT40+, HT40-, 80MHz" exit 1 else width="${1}" fi iw dev wlan0 set type monitor ifconfig wlan0 up for channel in ${channels}; do echo "Setting channel ${channel}, ${width}" if /home/netbeez/iw dev wlan0 set channel "${channel}" "${width}"; then if iw wlan0 info | grep "channel ${channel}"; then echo "OK: channel ${channel}, ${width}" else echo "OK: channel ${channel}, ${width} not set properly" fi else echo "ERROR: channel ${channel}, ${channel_width}" fi done
And you get an output as follows:
netbeez$ ./test_channels.sh HT40- Setting channel 1, HT40- command failed: Invalid argument (-22) ERROR: channel 1, Setting channel 2, HT40- command failed: Invalid argument (-22) ERROR: channel 2, Setting channel 3, HT40- command failed: Invalid argument (-22) ERROR: channel 3, Setting channel 4, HT40- command failed: Invalid argument (-22) ERROR: channel 4, Setting channel 5, HT40- channel 5 (2432 MHz), width: 40 MHz, center1: 2422 MHz OK: channel 5, HT40- . . . . . . . . . Setting channel 13, HT40- channel 13 (2472 MHz), width: 40 MHz, center1: 2462 MHz OK: channel 13, HT40- Setting channel 14, HT40- command failed: Invalid argument (-22) ERROR: channel 14, . . . . . . . . . Setting channel 144, HT40- channel 144 (5720 MHz), width: 40 MHz, center1: 5710 MHz OK: channel 144, HT40- Setting channel 149, HT40- command failed: Invalid argument (-22) ERROR: channel 149, Setting channel 153, HT40- channel 153 (5765 MHz), width: 40 MHz, center1: 5755 MHz OK: channel 153, HT40- Setting channel 157, HT40- channel 157 (5785 MHz), width: 40 MHz, center1: 5775 MHz OK: channel 157, HT40- Setting channel 161, HT40- channel 161 (5805 MHz), width: 40 MHz, center1: 5795 MHz OK: channel 161, HT40- Setting channel 165, HT40- channel 165 (5825 MHz), width: 40 MHz, center1: 5815 MHz OK: channel 165, HT40-
As you can see, in 2.4 GHz you can set a 40- width in channels 5 to 13.
To get a complete list run the script with the following inputs:
netbeez$ ./test_channels.sh HT20 netbeez$ ./test_channels.sh HT40+ netbeez$ ./test_channels.sh HT40- netbeez$ ./test_channels.sh 80MHz
Configure Channel and Width
Now that you know what channels and widths are supported by your hardware, you can simply set the capture channel and width with the following:
netbeez$ iw dev wlan0 set channel 6 HT20
And start a basic capture without filters on interface “wlan0” as follows:
netbeez$ tcpdump -i wlan0 -w capture.pcap tcpdump: listening on wlan0, link-type IEEE802_11_RADIO (802.11 plus radiotap header), capture size 262144 bytes ^C1224 packets captured 1281 packets received by filter 0 packets dropped by kernel 9 packets dropped by interface
I let this run for a few seconds and I stopped it by hitting Ctrl+C. Now you can open this file with a tool like wireshark for further analysis.
Channel Hopping
To do channel hopping, you’d have to change the channel while the capture is running. Practically, that’s not feasible to do manually especially if you need a dwell time in the order of milliseconds.
Here is a rudimentary script that does the channel hopping across all allowable channels based on the channel width and dwell time of 100 ms
#!/bin/bash channels_24="1 2 3 4 5 6 7 8 9 10 11 12 13 14" channels_50="36 40 44 48 52 56 60 64 100 104 108 112 116 120 124 128 132 136 140 144 149 153 157 161 165" channel_width_20="HT20" channel_width_40plus="HT40+" channel_width_40minus="HT40-" channel_width_80="80MHz" channels="${channels_24} ${channels_50}" width="${1}" if [[ "${1}" = "HT20" ]]; then channels="${channels_24} ${channels_50}" elif [[ "${1}" = "HT40+" ]]; then channels="1 2 3 4 5 6 7 8 9 36 40 44 48 52 56 60 100 104 108 112 116 120 124 128 132 136 140 153 157" elif [[ "${1}" = "HT40-" ]]; then channels="5 6 7 8 9 10 11 12 13 40 44 48 52 56 60 64 104 108 112 116 120 124 128 132 136 140 144 153 157 161" elif [[ "${1}" = "80MHz" ]]; then channels="${channels_50}" fi iw dev wlan0 set type monitor ifconfig wlan0 up for channel in ${channels}; do echo "Setting channel ${channel}, ${width}" iw dev wlan0 set channel "${channel}" "${width}" sleep 0.1 done
To run this script in parallel with the tcpdump capture you have either to use two different terminals (one for the channel hopping script and one for the tcpdump capture) or to run the channel hopping script as a background process and then launch the tcpdump capturing command as follows:
netbeez$ ./set_channels.sh HT20 & [2] 15917 netbeez$ tcpdump -i wlan0 -w capture.pcap tcpdump: listening on wlan0, link-type IEEE802_11_RADIO (802.11 plus radiotap header), capture size 262144 bytes ^C930 packets captured 930 packets received by filter 0 packets dropped by kernel
Once you stop tcpdump with “Ctrl+C” you can stop the channel hopping script with
netbeez$ kill 15917