b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | Automated hostapd/wpa_supplicant testing with mac80211_hwsim |
| 2 | ------------------------------------------------------------ |
| 3 | |
| 4 | This directory contains testing infrastructure and test cases to run |
| 5 | automated tests of full hostapd and wpa_supplicant functionality. This |
| 6 | testing is done with the help of mac80211_hwsim which is Linux kernel |
| 7 | driver that simulates IEEE 802.11 radios without requiring any |
| 8 | additional hardware. This setup most of the hostapd and wpa_supplicant |
| 9 | functionality (and large parts of the Linux cfg80211 and mac80211 |
| 10 | functionality for that matter) to be tested. |
| 11 | |
| 12 | mac80211_hwsim is loaded with five simulated radios to allow different |
| 13 | device combinations to be tested. wlantest is used analyze raw packets |
| 14 | captured through the hwsim0 monitor interface that capture all frames |
| 15 | sent on all channels. wlantest is used to store the frames for |
| 16 | analysis. Three wpa_supplicant processes are used to control three |
| 17 | virtual radios and one hostapd process is used to dynamically control |
| 18 | the other two virtual radios. wpa_supplicant/hostapd test functionality |
| 19 | is used to verify that data connection (both unicast and broadcast) |
| 20 | works between two netdevs. |
| 21 | |
| 22 | The python scripts and tools in this directory control test case |
| 23 | execution. They interact wpa_supplicant and hostapd through control |
| 24 | interfaces to perform the operations. In addition, wlantest_cli is used |
| 25 | to verify that operations have been performed correctly and that the |
| 26 | network connection works in the expected way. |
| 27 | |
| 28 | These test cases are run automatically against the hostap.git commits |
| 29 | for regression testing and to help in keeping the hostap.git main |
| 30 | branch in stable state. Results from these tests are available here: |
| 31 | http://buildbot.w1.fi/hwsim/ |
| 32 | |
| 33 | |
| 34 | Building binaries for testing |
| 35 | ----------------------------- |
| 36 | |
| 37 | You will need to build (or use already built) components to be |
| 38 | tested. These are available in the hostap.git repository and can be |
| 39 | built for example as follows: |
| 40 | |
| 41 | cd ../../wpa_supplicant |
| 42 | cp ../tests/hwsim/example-wpa_supplicant.config .config |
| 43 | make clean |
| 44 | make |
| 45 | cd ../hostapd |
| 46 | cp ../tests/hwsim/example-hostapd.config .config |
| 47 | make clean |
| 48 | make hostapd hostapd_cli hlr_auc_gw |
| 49 | cd ../wlantest |
| 50 | make clean |
| 51 | make |
| 52 | |
| 53 | Alternatively, the build.sh script here can be used to run these steps |
| 54 | with conditional creation of .config files only if they do not exist. |
| 55 | |
| 56 | The test scripts can find the binaries in the locations where they were |
| 57 | built. It is also possible to install wlantest_cli somewhere on the path |
| 58 | to use pre-built tools. |
| 59 | |
| 60 | Please note that some of the configuration parameters used to enable |
| 61 | more testing coverage may require development packages that may not be |
| 62 | installed by default in many distributions. For example, following |
| 63 | Debian/Ubuntu packages are likely to be needed: |
| 64 | - binutils-dev |
| 65 | - libsqlite3-dev |
| 66 | - libpcap-dev |
| 67 | |
| 68 | example-setup.txt provides more complete step-by-step example on how a |
| 69 | test setup can be built. |
| 70 | |
| 71 | |
| 72 | wpaspy |
| 73 | ------ |
| 74 | |
| 75 | The python scripts use wpaspy.py to interact with the wpa_supplicant |
| 76 | control interface, but the run-tests.py script adds the (relative) |
| 77 | path into the environment so it doesn't need to be installed. |
| 78 | |
| 79 | |
| 80 | mac80211_hwsim |
| 81 | -------------- |
| 82 | |
| 83 | mac80211_hwsim kernel module is available from the upstream Linux |
| 84 | kernel. Some Linux distributions enable it by default. If that's not the |
| 85 | case, you can either enable it in the kernel configuration |
| 86 | (CONFIG_MAC80211_HWSIM=m) and rebuild your kernel or use Backports with |
| 87 | CPTCFG_MAC80211_HWSIM=m to replace the wireless LAN components in the |
| 88 | base kernel. |
| 89 | |
| 90 | |
| 91 | sudo |
| 92 | ---- |
| 93 | |
| 94 | Some parts of the testing process requires root privileges. The test |
| 95 | scripts are currently using sudo to achieve this. To be able to run the |
| 96 | tests, you'll probably want to enable sudo with a timeout to not expire |
| 97 | password entry very quickly. For example, use this in the sudoers file: |
| 98 | |
| 99 | Defaults env_reset,timestamp_timeout=180 |
| 100 | |
| 101 | Or on a dedicated test system, you could even disable password prompting |
| 102 | with this in sudoers: |
| 103 | |
| 104 | %sudo ALL=NOPASSWD: ALL |
| 105 | |
| 106 | |
| 107 | Other network interfaces |
| 108 | ------------------------ |
| 109 | |
| 110 | Some of the test scripts are still using hardcoded interface names, so |
| 111 | the easiest way of making things work is to avoid using other network |
| 112 | devices that may use conflicting interface names. For example, unload |
| 113 | any wireless LAN driver before running the tests and make sure that |
| 114 | wlan0..4 gets assigned as the interface names for the mac80211_hwsim |
| 115 | radios. It may also be possible to rename the interface expectations in |
| 116 | run-tests.py to allow other names to be used. |
| 117 | |
| 118 | Please also note that some commonly enabled tools, like NetworkManager, |
| 119 | may end up trying to control new network interfaces automatically. This |
| 120 | can result in conflicts with the test scripts and you may need to |
| 121 | disable such network services or at least mark the mac80211_hwsim wlan# |
| 122 | interfaces as umanaged. As an example, this can be done in |
| 123 | /etc/NetworkManager/NetworkManager.conf with following addition: |
| 124 | |
| 125 | [keyfile] |
| 126 | unmanaged-devices=mac:02:00:00:00:00:00;mac:02:00:00:00:01:00;mac:02:00:00:00:02:00;mac:02:00:00:00:03:00;mac:02:00:00:00:04:00 |
| 127 | |
| 128 | |
| 129 | Running tests |
| 130 | ------------- |
| 131 | |
| 132 | Simplest way to run a full set of the test cases is by running |
| 133 | run-all.sh in tests/hwsim directory. This will use start.sh to load the |
| 134 | mac80211_hwsim module and start wpa_supplicant, hostapd, and various |
| 135 | test tools. run-tests.sh is then used to run through all the defined |
| 136 | test cases and stop.sh to stop the programs and unload the kernel |
| 137 | module. |
| 138 | |
| 139 | run-all.sh can be used to run the same test cases under different |
| 140 | conditions: |
| 141 | |
| 142 | # run normal test cases |
| 143 | ./run-all.sh |
| 144 | |
| 145 | # run normal test cases under valgrind |
| 146 | ./run-all.sh valgrind |
| 147 | |
| 148 | # run normal test cases with Linux tracing |
| 149 | ./run-all.sh trace |
| 150 | |
| 151 | # run normal test cases with multi channel support (see details below) |
| 152 | ./run-all.sh channels=<num of channels> |
| 153 | |
| 154 | run-all.sh directs debug logs into the logs subdirectory (or $LOGDIR if |
| 155 | present in the environment). Log file names include the current UNIX |
| 156 | timestamp and a postfix to identify the specific log: |
| 157 | - *.log0 = wpa_supplicant debug log for the first radio |
| 158 | - *.log1 = wpa_supplicant debug log for the second radio |
| 159 | - *.log2 = wpa_supplicant debug log for the third radio |
| 160 | - *.hostapd = hostapd debug log |
| 161 | - hwsim0 = wlantest debug log |
| 162 | - hwsim0.pcapng = capture with all frames exchanged during the tests |
| 163 | - *.log = debug prints from the test scripts |
| 164 | - trace.dat = Linux tracing record (if enabled) |
| 165 | - hlr_auc_gw - hlr_auc_gw (EAP-SIM/AKA/AKA' authentication) log |
| 166 | - auth_serv - hostapd as RADIUS authentication server log |
| 167 | |
| 168 | |
| 169 | For manual testing, ./start.sh can be used to initialize interfaces and |
| 170 | programs and run-tests.py to execute one or more test |
| 171 | cases. run-tests.py output verbosity can be controlled with -d (more |
| 172 | verbose debug output) and -q (less verbose output) on the command |
| 173 | line. "-f <module name>" (pointing to file test_<module name>.py) can be |
| 174 | used to specify that all test cases from a single file are to be |
| 175 | run. Test name as the last command line argument can be specified that a |
| 176 | single test case is to be run (e.g., "./run-tests.py ap_pmf_required"). |
| 177 | |
| 178 | Notice that some tests require the driver to support concurrent |
| 179 | operation on multi channels in order to run. These tests will be skipped |
| 180 | in case the driver does not support multi channels. To enable support |
| 181 | for multi channel, the number of supported channel is passed as an |
| 182 | argument to run-all.sh or start.sh |
| 183 | |
| 184 | |
| 185 | Adding/modifying test cases |
| 186 | --------------------------- |
| 187 | |
| 188 | All the test cases are defined in the test_*.py files. These are python |
| 189 | scripts that can use the local helper classes to interact with the test |
| 190 | components. While various python constructs can be used in the scripts, |
| 191 | only a minimal level of python knowledge should really be needed to |
| 192 | modify and add new test cases. The easiest starting point for this is |
| 193 | likely to take a look at some of the example scripts. When working on a |
| 194 | new test, run-tests.py with -d and the test case name on the command |
| 195 | line is a convenient way of verifying functionality. |
| 196 | |
| 197 | run-tests.py will automatically import all test cases from the test_*.py |
| 198 | files in this directory. All functions starting with the "test_" prefix |
| 199 | in these files are assumed to be test cases. Each test case is named by |
| 200 | the function name following the "test_" prefix. |
| 201 | |
| 202 | |
| 203 | Results database |
| 204 | ---------------- |
| 205 | |
| 206 | run-tests.py can be requested to write results from the execution of |
| 207 | each test case into an sqlite database. The "-S <path to database>" and |
| 208 | "-b <build id>" command line arguments can be used to do that. The |
| 209 | database must have been prepared before this, e.g., with following: |
| 210 | |
| 211 | cat | sqlite3 /tmp/example.db <<EOF |
| 212 | CREATE TABLE results (test,result,run,time,duration,build,commitid); |
| 213 | CREATE INDEX results_idx ON results (test); |
| 214 | CREATE INDEX results_idx2 ON results (run); |
| 215 | CREATE TABLE tests (test,description); |
| 216 | CREATE UNIQUE INDEX tests_idx ON tests (test); |
| 217 | CREATE TABLE logs (test,run,type,contents); |
| 218 | CREATE INDEX logs_idx ON logs (test); |
| 219 | CREATE INDEX logs_idx2 ON logs (run); |
| 220 | EOF |