From 94c8da4730f04a75cbba88d84799fb1b76d50424 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 16 Mar 2022 10:39:08 -0700 Subject: [PATCH 01/21] Workflow's for syncing with upstream, build, unit test, and test-runner --- .github/workflows/ci.yml | 236 +++++++++++++++++++++++++++ .github/workflows/pw-to-pr-email.txt | 16 ++ .github/workflows/pw-to-pr.json | 14 ++ .github/workflows/schedule_work.yml | 43 +++++ 4 files changed, 309 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pw-to-pr-email.txt create mode 100644 .github/workflows/pw-to-pr.json create mode 100644 .github/workflows/schedule_work.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..8e140ad8c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,236 @@ +name: IWD CI + +# +# The basic flow of the CI is as follows: +# +# 1. Get all inputs, or default values, and set as 'setup' job output +# 2. Find any cached binaries (hostapd, wpa_supplicant, kernel etc) +# 3. Checkout all dependent repositories +# 4. Tar all local files. This is an unfortunate requirement since github jobs +# cannot share local files. Since there are multiple CI's acting on the same +# set of repositories it makes more sense to retain these and re-download +# them for each CI job. +# 5. Run each CI, currently 'main' and 'musl'. +# * 'main' is the default IWD CI which runs all the build steps as well +# as test-runner +# * 'musl' uses an alpine docker image to test the build on musl-libc +# +# Both CI's use the 'iwd-ci-v2' repo which calls into 'ci-docker'. The +# 'ci-docker' action essentially re-implements the native Github docker +# action but allows arbitrary options to be passed in (e.g. privileged or +# mounting non-standard directories) +# + +on: + pull_request: + workflow_dispatch: + inputs: + tests: + description: Tests to run (comma separated, no spaces) + default: all + kernel: + description: Kernel version + default: '5.16' + hostapd_version: + description: Hostapd and wpa_supplicant version + default: '2_10' + ell_ref: + description: ELL reference + default: refs/heads/workflow + + repository_dispatch: + types: [ell-dispatch] + +jobs: + setup: + runs-on: ubuntu-22.04 + outputs: + tests: ${{ steps.inputs.outputs.tests }} + kernel: ${{ steps.inputs.outputs.kernel }} + hostapd_version: ${{ steps.inputs.outputs.hostapd_version }} + ell_ref: ${{ steps.inputs.outputs.ell_ref }} + repository: ${{ steps.inputs.outputs.repository }} + ref_branch: ${{ steps.inputs.outputs.ref_branch }} + steps: + # + # This makes CI inputs consistent depending on how the CI was invoked: + # * pull_request trigger won't have any inputs, so these need to be set + # to default values. + # * workflow_dispatch sets all inputs from the user input + # * repository_dispatch sets all inputs based on the JSON payload of + # the request. + # + - name: Setup Inputs + id: inputs + run: | + if [ ${{ github.event_name }} == 'workflow_dispatch' ] + then + TESTS=${{ github.event.inputs.tests }} + KERNEL=${{ github.event.inputs.kernel }} + HOSTAPD_VERSION=${{ github.event.inputs.hostapd_version }} + ELL_REF=${{ github.event.inputs.ell_ref }} + REF="$GITHUB_REF" + REPO="$GITHUB_REPOSITORY" + elif [ ${{ github.event_name }} == 'repository_dispatch' ] + then + TESTS=all + KERNEL=5.16 + HOSTAPD_VERSION=2_10 + ELL_REF=${{ github.event.client_payload.ref }} + REF=$ELL_REF + REPO=${{ github.event.client_payload.repo }} + else + TESTS=all + KERNEL=5.16 + HOSTAPD_VERSION=2_10 + ELL_REF="refs/heads/workflow" + REF="$GITHUB_REF" + REPO="$GITHUB_REPOSITORY" + fi + + # + # Now that the inputs are sorted, set the output of this step to these + # values so future jobs can refer to them. + # + echo ::set-output name=tests::$TESTS + echo ::set-output name=kernel::$KERNEL + echo ::set-output name=hostapd_version::$HOSTAPD_VERSION + echo ::set-output name=ell_ref::$ELL_REF + echo ::set-output name=repository::$REPO + echo ::set-output name=ref_branch::$REF + + - name: Cache UML Kernel + id: cache-uml-kernel + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }} + key: um-linux-${{ steps.inputs.outputs.kernel }}_ubuntu22 + + - name: Cache Hostapd + id: cache-hostapd + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }} + ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }} + key: hostapd_${{ steps.inputs.outputs.hostapd_version }}_ssl3 + + - name: Cache WpaSupplicant + id: cache-wpas + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }} + ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }} + key: wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }}_ssl3 + + - name: Checkout IWD + uses: actions/checkout@v3 + with: + path: iwd + repository: IWDTestBot/iwd + token: ${{ secrets.ACTION_TOKEN }} + + - name: Checkout ELL + uses: actions/checkout@v3 + with: + path: ell + repository: IWDTestBot/ell + ref: ${{ steps.inputs.outputs.ell_ref }} + + - name: Checkout CiBase + uses: actions/checkout@v3 + with: + repository: IWDTestBot/cibase + path: cibase + + - name: Checkout CI + uses: actions/checkout@v3 + with: + repository: IWDTestBot/iwd-ci-v2 + path: iwd-ci + + - name: Tar files + run: | + tar -cvf archive.tar \ + ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }} \ + ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }} \ + ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }} \ + ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }} \ + ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }} \ + iwd \ + ell \ + cibase \ + iwd-ci \ + cache + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: iwd-artifacts + path: | + archive.tar + + iwd-alpine-ci: + runs-on: ubuntu-22.04 + needs: setup + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + name: iwd-artifacts + + - name: Untar + run: tar -xf archive.tar + + - name: Modprobe pkcs8_key_parser + run: | + sudo modprobe pkcs8_key_parser + + - name: Alpine CI + uses: IWDTestBot/iwd-ci-v2@master + with: + ref_branch: ${{ needs.setup.outputs.ref_branch }} + repository: ${{ needs.setup.outputs.repository }} + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + ci: musl + + iwd-ci: + runs-on: ubuntu-22.04 + needs: setup + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + name: iwd-artifacts + + - name: Untar + run: tar -xf archive.tar + + - name: Modprobe pkcs8_key_parser + run: | + sudo modprobe pkcs8_key_parser + echo ${{ needs.setup.outputs.ref_branch }} + echo ${{ needs.setup.outputs.repository }} + + - name: Run CI + uses: IWDTestBot/iwd-ci-v2@master + with: + ref_branch: ${{ needs.setup.outputs.ref_branch }} + repository: ${{ needs.setup.outputs.repository }} + tests: ${{ needs.setup.outputs.tests }} + kernel: ${{ needs.setup.outputs.kernel }} + hostapd_version: ${{ needs.setup.outputs.hostapd_version }} + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + ci: main + + - name: Upload Logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-runner-logs + path: ${{ github.workspace }}/log diff --git a/.github/workflows/pw-to-pr-email.txt b/.github/workflows/pw-to-pr-email.txt new file mode 100644 index 000000000..0ad6d7659 --- /dev/null +++ b/.github/workflows/pw-to-pr-email.txt @@ -0,0 +1,16 @@ +This is an automated email and please do not reply to this email. + +Dear Submitter, + +Thank you for submitting the patches to the IWD mailing list. +While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository. + +----- Output ----- +{} + +Please resolve the issue and submit the patches again. + + +--- +Regards, +IWDTestBot diff --git a/.github/workflows/pw-to-pr.json b/.github/workflows/pw-to-pr.json new file mode 100644 index 000000000..b4491413c --- /dev/null +++ b/.github/workflows/pw-to-pr.json @@ -0,0 +1,14 @@ +{ + "email": { + "enable": true, + "server": "smtp.gmail.com", + "port": 587, + "user": "iwd.ci.bot@gmail.com", + "starttls": true, + "default-to": "prestwoj@gmail.com", + "only-maintainers": false, + "maintainers": [ + "prestwoj@gmail.com" + ] + } +} diff --git a/.github/workflows/schedule_work.yml b/.github/workflows/schedule_work.yml new file mode 100644 index 000000000..cfc14fba9 --- /dev/null +++ b/.github/workflows/schedule_work.yml @@ -0,0 +1,43 @@ +name: Sync Upstream +on: + schedule: + - cron: "*/15 * * * *" + workflow_dispatch: + +jobs: + repo-sync: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v2 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Manage Repo + uses: IWDTestBot/action-manage-repo@master + with: + src_repo: "https://git.kernel.org/pub/scm/network/wireless/iwd.git" + src_branch: "master" + dest_branch: "master" + workflow_branch: "workflow" + github_token: ${{ secrets.GITHUB_TOKEN }} + + create_pr: + needs: repo-sync + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Patchwork to PR + uses: IWDTestBot/action-patchwork-to-pr@master + with: + pw_key_str: "user" + github_token: ${{ secrets.ACTION_TOKEN }} + email_token: ${{ secrets.EMAIL_TOKEN }} + patchwork_token: ${{ secrets.PATCHWORK_TOKEN }} + config: https://raw.githubusercontent.com/IWDTestBot/iwd/workflow/.github/workflows/pw-to-pr.json + patchwork_id: "408" + email_message: https://raw.githubusercontent.com/IWDTestBot/iwd/workflow/.github/workflows/pw-to-pr-email.txt From 008b0fc2ad875cf5c21f9fed5e3921d6aaf9bc40 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 24 Jun 2022 15:27:03 -0700 Subject: [PATCH 02/21] workflow: use newer commit for hostapd --- .github/workflows/ci.yml | 61 +++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e140ad8c..4bf5b1347 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ on: default: '5.16' hostapd_version: description: Hostapd and wpa_supplicant version - default: '2_10' + default: '09a281e52a25b5461c4b08d261f093181266a554' ell_ref: description: ELL reference default: refs/heads/workflow @@ -75,14 +75,14 @@ jobs: then TESTS=all KERNEL=5.16 - HOSTAPD_VERSION=2_10 + HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF=${{ github.event.client_payload.ref }} REF=$ELL_REF REPO=${{ github.event.client_payload.repo }} else TESTS=all KERNEL=5.16 - HOSTAPD_VERSION=2_10 + HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF="refs/heads/workflow" REF="$GITHUB_REF" REPO="$GITHUB_REPOSITORY" @@ -152,17 +152,25 @@ jobs: - name: Tar files run: | - tar -cvf archive.tar \ - ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }} \ - ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }} \ - ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }} \ - ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }} \ - ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }} \ - iwd \ - ell \ - cibase \ - iwd-ci \ - cache + FILES="iwd ell cibase iwd-ci cache" + + if [ "${{ steps.cache-uml-kernel.outputs.cache-hit }}" == 'true' ] + then + FILES+=" ${{ github.workspace }}/cache/um-linux-${{ steps.inputs.outputs.kernel }}" + fi + + if [ "${{ steps.cache-hostapd.outputs.cache-hit }}" == 'true' ] + then + FILES+=" ${{ github.workspace }}/cache/hostapd_${{ steps.inputs.outputs.hostapd_version }}" + FILES+=" ${{ github.workspace }}/cache/hostapd_cli_${{ steps.inputs.outputs.hostapd_version }}" + fi + if [ "${{ steps.cache-wpas.outputs.cache-hit }}" == 'true' ] + then + FILES+=" ${{ github.workspace }}/cache/wpa_supplicant_${{ steps.inputs.outputs.hostapd_version }}" + FILES+=" ${{ github.workspace }}/cache/wpa_cli_${{ steps.inputs.outputs.hostapd_version }}" + fi + + tar -cvf archive.tar $FILES - name: Upload artifacts uses: actions/upload-artifact@v3 @@ -209,6 +217,31 @@ jobs: - name: Untar run: tar -xf archive.tar + - name: Cache UML Kernel + id: cache-uml-kernel + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/cache/um-linux-${{ needs.setup.outputs.kernel }} + key: um-linux-${{ needs.setup.outputs.kernel }}_ubuntu22 + + - name: Cache Hostapd + id: cache-hostapd + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/hostapd_${{ needs.setup.outputs.hostapd_version }} + ${{ github.workspace }}/cache/hostapd_cli_${{ needs.setup.outputs.hostapd_version }} + key: hostapd_${{ needs.setup.outputs.hostapd_version }}_ssl3 + + - name: Cache WpaSupplicant + id: cache-wpas + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/cache/wpa_supplicant_${{ needs.setup.outputs.hostapd_version }} + ${{ github.workspace }}/cache/wpa_cli_${{ needs.setup.outputs.hostapd_version }} + key: wpa_supplicant_${{ needs.setup.outputs.hostapd_version }}_ssl3 + - name: Modprobe pkcs8_key_parser run: | sudo modprobe pkcs8_key_parser From 0f39088ac5db799f1c1873494c152b1bca83edd9 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 7 Sep 2022 14:51:41 -0700 Subject: [PATCH 03/21] ci: remove cache/ from tar file list This is taken care of by the individual cache items and if none exist, tar fails. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4bf5b1347..09bbb2961 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,7 +152,7 @@ jobs: - name: Tar files run: | - FILES="iwd ell cibase iwd-ci cache" + FILES="iwd ell cibase iwd-ci" if [ "${{ steps.cache-uml-kernel.outputs.cache-hit }}" == 'true' ] then From ea7ebfd0a74d17829a327024840c6d71ad5b479c Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 14 Sep 2022 15:35:30 -0700 Subject: [PATCH 04/21] ci: use kernel 5.19 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09bbb2961..20b2e8419 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ on: default: all kernel: description: Kernel version - default: '5.16' + default: '5.19' hostapd_version: description: Hostapd and wpa_supplicant version default: '09a281e52a25b5461c4b08d261f093181266a554' @@ -74,14 +74,14 @@ jobs: elif [ ${{ github.event_name }} == 'repository_dispatch' ] then TESTS=all - KERNEL=5.16 + KERNEL=5.19 HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF=${{ github.event.client_payload.ref }} REF=$ELL_REF REPO=${{ github.event.client_payload.repo }} else TESTS=all - KERNEL=5.16 + KERNEL=5.19 HOSTAPD_VERSION=09a281e52a25b5461c4b08d261f093181266a554 ELL_REF="refs/heads/workflow" REF="$GITHUB_REF" From d784c058759f315dac5a1a3b1a8e343c17974fde Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 14 Oct 2022 08:58:15 -0700 Subject: [PATCH 05/21] ci: use iwd-ci after renaming to remove -v2 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20b2e8419..3f9d6981a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ name: IWD CI # as test-runner # * 'musl' uses an alpine docker image to test the build on musl-libc # -# Both CI's use the 'iwd-ci-v2' repo which calls into 'ci-docker'. The +# Both CI's use the 'iwd-ci' repo which calls into 'ci-docker'. The # 'ci-docker' action essentially re-implements the native Github docker # action but allows arbitrary options to be passed in (e.g. privileged or # mounting non-standard directories) @@ -147,7 +147,7 @@ jobs: - name: Checkout CI uses: actions/checkout@v3 with: - repository: IWDTestBot/iwd-ci-v2 + repository: IWDTestBot/iwd-ci path: iwd-ci - name: Tar files @@ -196,7 +196,7 @@ jobs: sudo modprobe pkcs8_key_parser - name: Alpine CI - uses: IWDTestBot/iwd-ci-v2@master + uses: IWDTestBot/iwd-ci@master with: ref_branch: ${{ needs.setup.outputs.ref_branch }} repository: ${{ needs.setup.outputs.repository }} @@ -249,7 +249,7 @@ jobs: echo ${{ needs.setup.outputs.repository }} - name: Run CI - uses: IWDTestBot/iwd-ci-v2@master + uses: IWDTestBot/iwd-ci@master with: ref_branch: ${{ needs.setup.outputs.ref_branch }} repository: ${{ needs.setup.outputs.repository }} From 114121ee414f080debb4b65416b742d51bfd555f Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 14 Oct 2022 10:18:25 -0700 Subject: [PATCH 06/21] ci: remove set-output use, now deprecated --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f9d6981a..393341c27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,12 +92,12 @@ jobs: # Now that the inputs are sorted, set the output of this step to these # values so future jobs can refer to them. # - echo ::set-output name=tests::$TESTS - echo ::set-output name=kernel::$KERNEL - echo ::set-output name=hostapd_version::$HOSTAPD_VERSION - echo ::set-output name=ell_ref::$ELL_REF - echo ::set-output name=repository::$REPO - echo ::set-output name=ref_branch::$REF + echo "tests=$TESTS" >> $GITHUB_OUTPUT + echo "kernel=$KERNEL" >> $GITHUB_OUTPUT + echo "hostapd_version=$HOSTAPD_VERSION" >> $GITHUB_OUTPUT + echo "ell_ref=$ELL_REF" >> $GITHUB_OUTPUT + echo "repository=$REPO" >> $GITHUB_OUTPUT + echo "ref_branch=$REF" >> $GITHUB_OUTPUT - name: Cache UML Kernel id: cache-uml-kernel From 344d5430e38d641d50af6098f2e042bb52f2d49c Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 7 Nov 2024 06:12:51 -0800 Subject: [PATCH 07/21] Update kernel to 6.2 and hostapd/wpa_s to 2.11 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 393341c27..993ce662d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,10 +30,10 @@ on: default: all kernel: description: Kernel version - default: '5.19' + default: '6.2' hostapd_version: description: Hostapd and wpa_supplicant version - default: '09a281e52a25b5461c4b08d261f093181266a554' + default: 'hostapd_2_11' ell_ref: description: ELL reference default: refs/heads/workflow From 409879fc7ff08b6d00dc3a574559b9c60248c499 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 13 Feb 2025 08:18:29 -0800 Subject: [PATCH 08/21] Update upload/download-artifact to v4 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 993ce662d..a9582eb14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,7 +173,7 @@ jobs: tar -cvf archive.tar $FILES - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: iwd-artifacts path: | @@ -184,7 +184,7 @@ jobs: needs: setup steps: - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: iwd-artifacts @@ -210,7 +210,7 @@ jobs: needs: setup steps: - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: iwd-artifacts @@ -263,7 +263,7 @@ jobs: - name: Upload Logs if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-runner-logs path: ${{ github.workspace }}/log From 46c60c3d1237151cc26e07e8e8e4f5272bfe8d43 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 18 Apr 2025 12:03:43 -0700 Subject: [PATCH 09/21] Add coverity Github action --- .github/workflows/coverity.yml | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 .github/workflows/coverity.yml diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml new file mode 100644 index 000000000..91f9073d3 --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,86 @@ +name: Coverity Scan and Submit +description: Runs a coverity scan, then sends results to the cloud +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + scan-and-submit: + runs-on: ubuntu-22.04 + steps: + - name: Lookup latest tool + id: cache-lookup + run: | + hash=$(curl https://scan.coverity.com/download/cxx/linux64 \ + --data "token=${{ secrets.COVERITY_IWD_TOKEN }}&project=IWD&md5=1"); + echo "hash=${hash}" >> $GITHUB_OUTPUT + + - name: Get cached coverity tool + id: build-cache + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/cov-analysis + key: cov-build-cxx-linux64-${{ steps.cache-lookup.outputs.hash }} + + - name: Download Coverity Build Tool + if: steps.build-cache.outputs.cache-hit != 'true' + run: | + curl https://scan.coverity.com/download/cxx/linux64 \ + --no-progress-meter \ + --output cov-analysis.tar.gz \ + --data "token=${{ secrets.COVERITY_IWD_TOKEN }}&project=IWD" + shell: bash + working-directory: ${{ github.workspace }} + + - if: steps.build-cache.outputs.cache-hit != 'true' + run: mkdir cov-analysis + shell: bash + working-directory: ${{ github.workspace }} + + - if: steps.build-cache.outputs.cache-hit != 'true' + run: tar -xzf cov-analysis.tar.gz --strip 1 -C cov-analysis + shell: bash + working-directory: ${{ github.workspace }} + + - name: Checkout IWD + uses: actions/checkout@v3 + with: + path: ${{ github.workspace }}/iwd + repository: IWDTestBot/iwd + token: ${{ secrets.ACTION_TOKEN }} + + - name: Checkout ELL + uses: actions/checkout@v3 + with: + path: ${{ github.workspace }}/ell + repository: IWDTestBot/ell + token: ${{ secrets.ACTION_TOKEN }} + + - name: Configure IWD + run: | + cd ${{ github.workspace }}/iwd + ./bootstrap-configure --disable-manual-pages + + - name: Build with cov-build + run: | + export PATH="${{ github.workspace }}/cov-analysis/bin:${PATH}" + cov-build --dir cov-int make -j4 + shell: bash + working-directory: ${{ github.workspace }}/iwd + + - name: Tar results + run: tar -czvf cov-int.tgz cov-int + shell: bash + working-directory: ${{ github.workspace }}/iwd + + - name: Submit results to Coverity Scan + if: ${{ ! inputs.dry_run }} + run: | + curl \ + --form token="${{ secrets.COVERITY_IWD_TOKEN }}" \ + --form email="iwd.ci.bot@gmail.com" \ + --form file=@cov-int.tgz \ + "https://scan.coverity.com/builds?project=IWD" + shell: bash + working-directory: ${{ github.workspace }}/iwd From 0e452d2020a4b27bce8b1f0c992f292524197894 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Mon, 18 Aug 2025 07:55:14 -0700 Subject: [PATCH 10/21] Fix hostap branch name --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9582eb14..51153d6dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ on: default: '6.2' hostapd_version: description: Hostapd and wpa_supplicant version - default: 'hostapd_2_11' + default: 'hostap_2_11' ell_ref: description: ELL reference default: refs/heads/workflow From 8b0515a1f86c845a44a7759814b5f8c691e6a367 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:51 -0700 Subject: [PATCH 11/21] vendor_quirks: initial skeleton This module will provide a database for known issues or quirks with wireless vendors. The vendor_quirks_append_for_oui() API is intended to be called from scan.c when parsing vendor attributes. This will lookup any quirks associated with the OUI provided and combine them into an existing vendor_quirk structure. This can be repeated against all the vendor OUI's seen in the scan then referenced later to alter IWD behavior. In the future more critera could be added such as MAC address prefix or more generalized IE matches e.g. vendor_quirks_append_for_mac() vendor_quirks_append_for_ie() etc. --- Makefile.am | 2 ++ src/vendor_quirks.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ src/vendor_quirks.h | 39 ++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 src/vendor_quirks.c create mode 100644 src/vendor_quirks.h diff --git a/Makefile.am b/Makefile.am index 92adfa6e8..c01cd4c43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -274,6 +274,8 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h \ src/dpp.c \ src/udev.c \ src/pmksa.h src/pmksa.c \ + src/vendor_quirks.h \ + src/vendor_quirks.c \ $(eap_sources) \ $(builtin_sources) diff --git a/src/vendor_quirks.c b/src/vendor_quirks.c new file mode 100644 index 000000000..18a9ba7a2 --- /dev/null +++ b/src/vendor_quirks.c @@ -0,0 +1,67 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2025 Locus Robotics Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "src/vendor_quirks.h" + +static const struct { + uint8_t oui[3]; + struct vendor_quirk quirks; +} oui_quirk_db[] = { + { } +}; + +void vendor_quirks_append_for_oui(const uint8_t *oui, + struct vendor_quirk *quirks) +{ + size_t i; + + for (i = 0; i < L_ARRAY_SIZE(oui_quirk_db); i++) { + const struct vendor_quirk *quirk = &oui_quirk_db[i].quirks; + + if (memcmp(oui_quirk_db[i].oui, oui, 3)) + continue; + + quirks->ignore_bss_tm_candidates |= + quirk->ignore_bss_tm_candidates; + quirks->replay_counter_mismatch |= + quirk->replay_counter_mismatch; + } +} + +const char *vendor_quirks_to_string(struct vendor_quirk quirks) +{ + static char out[1024]; + size_t s = 0; + + if (!s) + return NULL; + + return out; +} diff --git a/src/vendor_quirks.h b/src/vendor_quirks.h new file mode 100644 index 000000000..b5d726ca0 --- /dev/null +++ b/src/vendor_quirks.h @@ -0,0 +1,39 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2025 Locus Robotics Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __IWD_VENDOR_QUIRKS_H +#define __IWD_VENDOR_QUIRKS_H + + +#include + +struct vendor_quirk { + bool ignore_bss_tm_candidates : 1; + bool replay_counter_mismatch : 1; +}; + +void vendor_quirks_append_for_oui(const uint8_t *oui, + struct vendor_quirk *quirks); + +const char *vendor_quirks_to_string(struct vendor_quirk quirks); + +#endif /* __IWD_VENDOR_QUIRKS_H */ From e3cbf18aaa0f3fd8caadb3e0c630cfccebd4b42d Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:52 -0700 Subject: [PATCH 12/21] vendor_quirks: implement two vendor quirks ignore_bss_tm_candidates: When a BSS requests a station roam it can optionally include a list of BSS's that can be roamed to. IWD uses this list and only scans on those frequencies. In some cases though the AP's list contains very poor options and it would be better for IWD to request a full neighbor report. replay_counter_mismatch: On some Aruba APs there is a mismatch in the replay counters between what is seen in scans versus authentications/associations. This difference is not allowed in the spec, therefore IWD will not connect. This quirk is intended to relax that check. --- src/vendor_quirks.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vendor_quirks.c b/src/vendor_quirks.c index 18a9ba7a2..4fba0c336 100644 --- a/src/vendor_quirks.c +++ b/src/vendor_quirks.c @@ -25,6 +25,7 @@ #endif #include +#include #include @@ -34,7 +35,16 @@ static const struct { uint8_t oui[3]; struct vendor_quirk quirks; } oui_quirk_db[] = { - { } + { + /* Cisco Meraki */ + { 0x00, 0x18, 0x0a }, + { .ignore_bss_tm_candidates = true }, + }, + { + /* Hewlett Packard, owns Aruba */ + { 0x00, 0x0b, 0x86 }, + { .replay_counter_mismatch = true }, + }, }; void vendor_quirks_append_for_oui(const uint8_t *oui, @@ -58,8 +68,15 @@ void vendor_quirks_append_for_oui(const uint8_t *oui, const char *vendor_quirks_to_string(struct vendor_quirk quirks) { static char out[1024]; + char *pos = out; size_t s = 0; + if (quirks.ignore_bss_tm_candidates) + s += snprintf(pos, sizeof(out) - s, "IgnoreBssTmCandidateList"); + + if (quirks.replay_counter_mismatch) + s += snprintf(pos, sizeof(out) - s, "ReplayCounterMismatch"); + if (!s) return NULL; From b342de80ad15497542f7387c93812a02f8ab319b Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:53 -0700 Subject: [PATCH 13/21] handshake: pass object to handshake_util_ap_ie_matches This is to prepare for supporting a vendor quirk, where we'll need the handshake to lookup if the quirk to disable a specific check. --- src/eapol.c | 2 +- src/ft.c | 10 ++++++---- src/handshake.c | 3 ++- src/handshake.h | 3 ++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/eapol.c b/src/eapol.c index 6e37a54a3..ab77746f5 100644 --- a/src/eapol.c +++ b/src/eapol.c @@ -1810,7 +1810,7 @@ static void eapol_handle_ptk_3_of_4(struct eapol_sm *sm, if ((rsne[1] != hs->authenticator_ie[1] || memcmp(rsne + 2, hs->authenticator_ie + 2, rsne[1])) && - !handshake_util_ap_ie_matches(&rsn_info, + !handshake_util_ap_ie_matches(hs, &rsn_info, hs->authenticator_ie, hs->wpa_ie)) goto error_ie_different; diff --git a/src/ft.c b/src/ft.c index d8bee74cc..0d6be4d41 100644 --- a/src/ft.c +++ b/src/ft.c @@ -223,7 +223,8 @@ static bool ft_parse_associate_resp_frame(const uint8_t *frame, size_t frame_len return true; } -static bool ft_verify_rsne(const uint8_t *rsne, const uint8_t *pmk_r0_name, +static bool ft_verify_rsne(struct handshake_state *hs, + const uint8_t *rsne, const uint8_t *pmk_r0_name, const uint8_t *authenticator_ie) { /* @@ -253,7 +254,7 @@ static bool ft_verify_rsne(const uint8_t *rsne, const uint8_t *pmk_r0_name, memcmp(msg2_rsne.pmkids, pmk_r0_name, 16)) return false; - if (!handshake_util_ap_ie_matches(&msg2_rsne, authenticator_ie, false)) + if (!handshake_util_ap_ie_matches(hs, &msg2_rsne, authenticator_ie, false)) return false; return true; @@ -301,7 +302,8 @@ static int parse_ies(struct handshake_state *hs, is_rsn = hs->supplicant_ie != NULL; if (is_rsn) { - if (!ft_verify_rsne(rsne, hs->pmk_r0_name, authenticator_ie)) + if (!ft_verify_rsne(hs, rsne, hs->pmk_r0_name, + authenticator_ie)) goto ft_error; } else if (rsne) goto ft_error; @@ -480,7 +482,7 @@ int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame, size_t frame_len) memcmp(msg4_rsne.pmkids, hs->pmk_r1_name, 16)) return -EBADMSG; - if (!handshake_util_ap_ie_matches(&msg4_rsne, + if (!handshake_util_ap_ie_matches(hs, &msg4_rsne, hs->authenticator_ie, false)) return -EBADMSG; diff --git a/src/handshake.c b/src/handshake.c index c469e6fa2..92edac308 100644 --- a/src/handshake.c +++ b/src/handshake.c @@ -877,7 +877,8 @@ void handshake_state_set_igtk(struct handshake_state *s, const uint8_t *key, * results vs the RSN/WPA IE obtained as part of the 4-way handshake. If they * don't match, the EAPoL packet must be silently discarded. */ -bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info, +bool handshake_util_ap_ie_matches(struct handshake_state *s, + const struct ie_rsn_info *msg_info, const uint8_t *scan_ie, bool is_wpa) { struct ie_rsn_info scan_info; diff --git a/src/handshake.h b/src/handshake.h index c6e3c10b8..b8891490b 100644 --- a/src/handshake.h +++ b/src/handshake.h @@ -312,7 +312,8 @@ bool handshake_state_set_pmksa(struct handshake_state *s, struct pmksa *pmksa); void handshake_state_cache_pmksa(struct handshake_state *s); bool handshake_state_remove_pmksa(struct handshake_state *s); -bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info, +bool handshake_util_ap_ie_matches(struct handshake_state *s, + const struct ie_rsn_info *msg_info, const uint8_t *scan_ie, bool is_wpa); const uint8_t *handshake_util_find_kde(enum handshake_kde selector, From 9a8db47f8ba6795411e137291239d0bda9bfab3b Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:54 -0700 Subject: [PATCH 14/21] handshake: add vendor quirks into handshake object --- src/handshake.c | 6 ++++++ src/handshake.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/handshake.c b/src/handshake.c index 92edac308..02dfe2d80 100644 --- a/src/handshake.c +++ b/src/handshake.c @@ -368,6 +368,12 @@ void handshake_state_set_vendor_ies(struct handshake_state *s, } } +void handshake_state_set_vendor_quirks(struct handshake_state *s, + struct vendor_quirk quirks) +{ + s->vendor_quirks = quirks; +} + void handshake_state_set_kh_ids(struct handshake_state *s, const uint8_t *r0khid, size_t r0khid_len, const uint8_t *r1khid) diff --git a/src/handshake.h b/src/handshake.h index b8891490b..9ddeecd6e 100644 --- a/src/handshake.h +++ b/src/handshake.h @@ -26,6 +26,8 @@ #include #include +#include "src/vendor_quirks.h" + struct handshake_state; enum crypto_cipher; struct eapol_frame; @@ -107,6 +109,7 @@ struct handshake_state { uint8_t *authenticator_fte; uint8_t *supplicant_fte; uint8_t *vendor_ies; + struct vendor_quirk vendor_quirks; size_t vendor_ies_len; enum ie_rsn_cipher_suite pairwise_cipher; enum ie_rsn_cipher_suite group_cipher; @@ -237,6 +240,9 @@ void handshake_state_set_vendor_ies(struct handshake_state *s, const struct iovec *iov, size_t n_iovs); +void handshake_state_set_vendor_quirks(struct handshake_state *s, + struct vendor_quirk quirks); + void handshake_state_set_kh_ids(struct handshake_state *s, const uint8_t *r0khid, size_t r0khid_len, const uint8_t *r1khid); From 34af1d8329a684342969624d6ca2bf05e0cbaf74 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:55 -0700 Subject: [PATCH 15/21] scan: store vendor quirks in scan_bss As each vendor IE is parsed lookup if there are any quirks associated with it, and store these in a bit mask. --- src/scan.c | 6 ++++++ src/scan.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/scan.c b/src/scan.c index d9f27c83b..46ea79efe 100644 --- a/src/scan.c +++ b/src/scan.c @@ -51,6 +51,7 @@ #include "src/mpdu.h" #include "src/band.h" #include "src/scan.h" +#include "src/vendor_quirks.h" /* User configurable options */ static double RANK_2G_FACTOR; @@ -1221,6 +1222,11 @@ static void scan_parse_vendor_specific(struct scan_bss *bss, const void *data, uint16_t cost_flags; bool dgaf_disable; + if (L_WARN_ON(len < 3)) + return; + + vendor_quirks_append_for_oui(data, &bss->vendor_quirks); + if (!bss->wpa && is_ie_wpa_ie(data, len)) { bss->wpa = l_memdup(data - 2, len + 2); return; diff --git a/src/scan.h b/src/scan.h index 4c1ebc21d..ae6a3a79a 100644 --- a/src/scan.h +++ b/src/scan.h @@ -21,6 +21,7 @@ */ #include "src/defs.h" +#include "src/vendor_quirks.h" struct scan_freq_set; struct ie_rsn_info; @@ -79,6 +80,7 @@ struct scan_bss { uint8_t *wfd; /* Concatenated WFD IEs */ ssize_t wfd_size; /* Size of Concatenated WFD IEs */ int8_t snr; + struct vendor_quirk vendor_quirks; bool mde_present : 1; bool cc_present : 1; bool cap_rm_neighbor_report : 1; From 0362adb3930c311c19a6eea7440b99d58f1949a8 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:56 -0700 Subject: [PATCH 16/21] station: set vendor quirks into handshake object --- src/station.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/station.c b/src/station.c index e2273153c..66395a5bd 100644 --- a/src/station.c +++ b/src/station.c @@ -1446,6 +1446,8 @@ static struct handshake_state *station_handshake_setup(struct station *station, vendor_ies = network_info_get_extra_ies(info, bss, &iov_elems); handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems); + handshake_state_set_vendor_quirks(hs, bss->vendor_quirks); + /* * It can't hurt to try the FILS IP Address Assignment independent of * which auth-proto is actually used. From 86cc4dcd3a267c3f56e7d20ee48ae10306c2a517 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:57 -0700 Subject: [PATCH 17/21] handshake: use vendor quirk to disable check of replay counters This has been a long standing issue on Aruba APs where the scan IEs differ from the IEs received during FT. For compatibility we have been carrying a patch to disable the replay counter check but this isn't something that was ever acceptable for upstream. Now with the addition of vendor quirks this check can be disabled only for the OUI of Aruba APs. Reported-by: Michael Johnson Co-authored-by: Michael Johnson < --- src/handshake.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/handshake.c b/src/handshake.c index 02dfe2d80..ef1a82203 100644 --- a/src/handshake.c +++ b/src/handshake.c @@ -914,11 +914,15 @@ bool handshake_util_ap_ie_matches(struct handshake_state *s, if (msg_info->no_pairwise != scan_info.no_pairwise) return false; - if (msg_info->ptksa_replay_counter != scan_info.ptksa_replay_counter) - return false; + if (!(s->vendor_quirks.replay_counter_mismatch)) { + if (msg_info->ptksa_replay_counter != + scan_info.ptksa_replay_counter) + return false; - if (msg_info->gtksa_replay_counter != scan_info.gtksa_replay_counter) - return false; + if (msg_info->gtksa_replay_counter != + scan_info.gtksa_replay_counter) + return false; + } if (msg_info->mfpr != scan_info.mfpr) return false; From 203739013ca460d108f557a94d5f4b77d4c830f3 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:58 -0700 Subject: [PATCH 18/21] station: get neighbor report on BSS TM request If a BSS is requesting IWD roam elsewhere but does not include a preferred candidate list try getting a neighbor report before doing a full scan. If the limited scan based on the candidate list comes up empty this would previously result in IWD giving up on the AP roam entirely. This patch also improves that behavior slightly by doing a full scan afterwards as a last ditch effort. If no BSS's are found after that, IWD will give up on the AP roam. --- src/station.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/station.c b/src/station.c index 66395a5bd..3d8eb36ba 100644 --- a/src/station.c +++ b/src/station.c @@ -2440,8 +2440,16 @@ static void station_roam_failed(struct station *station) * We were told by the AP to roam, but failed. Try ourselves or * wait for the AP to tell us to roam again */ - if (station->ap_directed_roaming) + if (station->ap_directed_roaming) { + /* + * The candidate list from the AP (or neighbor report) found + * no BSS's. Force a full scan + */ + if (!station->roam_scan_full) + goto full_scan; + goto delayed_retry; + } /* * If we tried a limited scan, failed and the signal is still low, @@ -2453,6 +2461,7 @@ static void station_roam_failed(struct station *station) * the scan here, so that the destroy callback is not called * after the return of this function */ +full_scan: scan_cancel(netdev_get_wdev_id(station->netdev), station->roam_scan_id); @@ -3045,6 +3054,7 @@ static int station_roam_scan(struct station *station, if (!freq_set) { station->roam_scan_full = true; params.freqs = allowed; + station_debug_event(station, "full-roam-scan"); } else scan_freq_set_constrain(freq_set, allowed); @@ -3378,7 +3388,15 @@ static void station_ap_directed_roam(struct station *station, station_neighbor_report_cb(station->netdev, 0, body + pos, body_len - pos, station); } else { - l_debug("roam: AP did not include a preferred candidate list"); + if (station->connected_bss->cap_rm_neighbor_report) { + if (!netdev_neighbor_report_req(station->netdev, + station_neighbor_report_cb)) + return; + + l_warn("failed to request neighbor report!"); + } + + l_debug("full scan after BSS transition request"); if (station_roam_scan(station, NULL) < 0) station_roam_failed(station); } From 237a7eadb49f1e969162b16290dd3bd9f7b308a9 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:54:59 -0700 Subject: [PATCH 19/21] station: check vendor quirk for BSS TM request candidate list If the AP vendor has known issues with the preferred candidate list ignore it and jump directly to requesting a neighbor report. --- src/station.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/station.c b/src/station.c index 3d8eb36ba..4a29c9c1a 100644 --- a/src/station.c +++ b/src/station.c @@ -64,6 +64,7 @@ #include "src/eap-tls-common.h" #include "src/storage.h" #include "src/pmksa.h" +#include "src/vendor_quirks.h" #define STATION_RECENT_NETWORK_LIMIT 5 #define STATION_RECENT_FREQS_LIMIT 5 @@ -3266,6 +3267,8 @@ static void station_ap_directed_roam(struct station *station, uint16_t dtimer; uint8_t valid_interval; bool can_roam = !station_cannot_roam(station); + bool ignore_candidates = + station->connected_bss->vendor_quirks.ignore_bss_tm_candidates; l_debug("ifindex: %u", netdev_get_ifindex(station->netdev)); @@ -3383,7 +3386,8 @@ static void station_ap_directed_roam(struct station *station, l_timeout_remove(station->roam_trigger_timeout); station->roam_trigger_timeout = NULL; - if (req_mode & WNM_REQUEST_MODE_PREFERRED_CANDIDATE_LIST) { + if ((req_mode & WNM_REQUEST_MODE_PREFERRED_CANDIDATE_LIST) && + !ignore_candidates) { l_debug("roam: AP sent a preferred candidate list"); station_neighbor_report_cb(station->netdev, 0, body + pos, body_len - pos, station); From 5f660b0e7651d81e7404e088783600f95cdb1e0b Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:55:00 -0700 Subject: [PATCH 20/21] auto-t: add AP roam test for bad neighbor reports/candidate lists --- .../testAPRoam/bad_neighbor_report_test.py | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 autotests/testAPRoam/bad_neighbor_report_test.py diff --git a/autotests/testAPRoam/bad_neighbor_report_test.py b/autotests/testAPRoam/bad_neighbor_report_test.py new file mode 100644 index 000000000..c1ec7e261 --- /dev/null +++ b/autotests/testAPRoam/bad_neighbor_report_test.py @@ -0,0 +1,156 @@ +#!/usr/bin/python3 + +import unittest +import sys + +sys.path.append('../util') +import iwd +from iwd import IWD +from iwd import NetworkType + +from hostapd import HostapdCLI + +class Test(unittest.TestCase): + def initial_connection(self): + ordered_network = self.device.get_ordered_network('TestAPRoam') + + self.assertEqual(ordered_network.type, NetworkType.psk) + + condition = 'not obj.connected' + self.wd.wait_for_object_condition(ordered_network.network_object, condition) + + self.device.connect_bssid(self.bss_hostapd[0].bssid) + + condition = 'obj.state == DeviceState.connected' + self.wd.wait_for_object_condition(self.device, condition) + + self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED') + + self.assertFalse(self.bss_hostapd[1].list_sta()) + + def test_full_scan(self): + """ + Tests that IWD first tries a limited scan, then a full scan after + an AP directed roam. After the full scan yields no results IWD + should stop trying to roam. + """ + self.initial_connection() + + # Disable other APs, so the scans come up empty + self.bss_hostapd[1].disable() + self.bss_hostapd[2].disable() + + # Send a bad candidate list with the BSS TM request which contains a + # channel with no AP operating on it. + self.bss_hostapd[0].send_bss_transition( + self.device.address, + [(self.bss_hostapd[1].bssid, "8f0000005105060603000000")] + ) + self.device.wait_for_event("roam-scan-triggered") + self.device.wait_for_event("no-roam-candidates") + # IWD should then trigger a full scan + self.device.wait_for_event("full-roam-scan") + self.device.wait_for_event("no-roam-candidates", timeout=30) + + # IWD should not trigger a roam again after the above 2 failures. + with self.assertRaises(TimeoutError): + self.device.wait_for_event("roam-scan-triggered", timeout=60) + + def test_bad_candidate_list(self): + """ + Tests behavior when the AP sends a candidate list but the scan + finds no BSS's. IWD should fall back to a full scan after. + """ + self.initial_connection() + + # Send a bad candidate list with the BSS TM request which contains a + # channel with no AP operating on it. + self.bss_hostapd[0].send_bss_transition( + self.device.address, + [(self.bss_hostapd[1].bssid, "8f0000005105060603000000")] + ) + self.device.wait_for_event("roam-scan-triggered") + self.device.wait_for_event("no-roam-candidates") + # IWD should then trigger a full scan + self.device.wait_for_event("full-roam-scan") + self.device.wait_for_event("roaming", timeout=30) + self.device.wait_for_event("connected") + + def test_bad_neighbor_report(self): + """ + Tests behavior when the AP sends no candidate list. IWD should + request a neighbor report. If the limited scan yields no BSS's IWD + should fall back to a full scan. + """ + + # Set a bad neighbor (channel that no AP is on) to force the limited + # roam scan to fail + self.bss_hostapd[0].set_neighbor( + self.bss_hostapd[1].bssid, + "TestAPRoam", + '%s8f000000%s%s060603000000' % (self.bss_hostapd[1].bssid.replace(':', ''), "51", "0b") + ) + + self.initial_connection() + + self.bss_hostapd[0].send_bss_transition(self.device.address, []) + self.device.wait_for_event("roam-scan-triggered") + # The AP will have sent a neighbor report with a single BSS but on + # channel 11 which no AP is on. This should result in a limited scan + # picking up no candidates. + self.device.wait_for_event("no-roam-candidates", timeout=30) + # IWD should then trigger a full scan + self.device.wait_for_event("full-roam-scan") + self.device.wait_for_event("roaming", timeout=30) + self.device.wait_for_event("connected") + + def test_ignore_candidate_list_quirk(self): + """ + Tests that IWD ignores the candidate list sent by the AP since its + OUI indicates it should be ignored. + """ + + # Set the OUI so the candidate list should be ignored + for hapd in self.bss_hostapd: + hapd.set_value('vendor_elements', 'dd0400180a01') + + self.initial_connection() + + # Send with a candidate list (should be ignored) + self.bss_hostapd[0].send_bss_transition( + self.device.address, + [(self.bss_hostapd[1].bssid, "8f0000005105060603000000")] + ) + # IWD should ignore the list and trigger a full scan since we have not + # set any neighbors + self.device.wait_for_event("full-roam-scan") + self.device.wait_for_event("roaming", timeout=30) + self.device.wait_for_event("connected") + + def setUp(self): + self.wd = IWD(True) + + devices = self.wd.list_devices(1) + self.device = devices[0] + + def tearDown(self): + self.wd = None + self.device = None + + for hapd in self.bss_hostapd: + hapd.reload() + + @classmethod + def setUpClass(cls): + IWD.copy_to_storage('TestAPRoam.psk') + + cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'), + HostapdCLI(config='ssid2.conf'), + HostapdCLI(config='ssid3.conf') ] + + @classmethod + def tearDownClass(cls): + IWD.clear_storage() + +if __name__ == '__main__': + unittest.main(exit=True) From cab4f2a042ef3c3344a9b45457376847b5c2afc5 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 27 Aug 2025 05:55:01 -0700 Subject: [PATCH 21/21] station: print vendor quirks (if any) when connecting/roaming This makes it clear the BSS being selected for a connection/roam has any quirks associated with its OUI(s) and that IWD may behave differently based on these. --- src/station.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/station.c b/src/station.c index 4a29c9c1a..f8069d897 100644 --- a/src/station.c +++ b/src/station.c @@ -2750,11 +2750,15 @@ static bool station_try_next_transition(struct station *station, enum security security = network_get_security(connected); struct handshake_state *new_hs; struct ie_rsn_info cur_rsne, target_rsne; + const char *vendor_quirks = vendor_quirks_to_string(bss->vendor_quirks); iwd_notice(IWD_NOTICE_ROAM_INFO, "bss: "MAC", signal: %d, load: %d/255", MAC_STR(bss->addr), bss->signal_strength / 100, bss->utilization); + if (vendor_quirks) + l_debug("vendor quirks for "MAC": %s", + MAC_STR(bss->addr), vendor_quirks); /* Reset AP roam flag, at this point the roaming behaves the same */ station->ap_directed_roaming = false; @@ -3913,6 +3917,7 @@ int __station_connect_network(struct station *station, struct network *network, { struct handshake_state *hs; int r; + const char *vendor_quirks = vendor_quirks_to_string(bss->vendor_quirks); /* * If we already have a handshake_state ref this is due to a retry, @@ -3947,6 +3952,10 @@ int __station_connect_network(struct station *station, struct network *network, bss->signal_strength / 100, bss->utilization); + if (vendor_quirks) + l_debug("vendor quirks for "MAC": %s", + MAC_STR(bss->addr), vendor_quirks); + station->connected_bss = bss; station->connected_network = network; station->hs = handshake_state_ref(hs);