From 3d009b2f8b13fc888cd042a5add9916a3ce1d6ee Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 16 Mar 2022 10:39:08 -0700 Subject: [PATCH 01/25] 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 93bb0350e1500bf97f0f9308f9177c283062e966 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 24 Jun 2022 15:27:03 -0700 Subject: [PATCH 02/25] 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 999b92ce602616406684c50bf23e028f7d071e43 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 7 Sep 2022 14:51:41 -0700 Subject: [PATCH 03/25] 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 bd468f6f6a98698f1805983c04fa5d8b845cf8ff Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 14 Sep 2022 15:35:30 -0700 Subject: [PATCH 04/25] 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 7b5519d0b7a1cc97e5d9e9c0997a23d4a3cd16fe Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 14 Oct 2022 08:58:15 -0700 Subject: [PATCH 05/25] 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 74ee8c9f6bfcd9bbd36f898e3c482a009c051aa9 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 14 Oct 2022 10:18:25 -0700 Subject: [PATCH 06/25] 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 8c808abf0c5ce15dedd124c56208d012da1dbe37 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 7 Nov 2024 06:12:51 -0800 Subject: [PATCH 07/25] 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 6ac7d26b46133fc433424229110e44509646876c Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 13 Feb 2025 08:18:29 -0800 Subject: [PATCH 08/25] 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 ef6f103959047605b3e0f113b1bae18a8da55243 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 18 Apr 2025 12:03:43 -0700 Subject: [PATCH 09/25] 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 1426941f3df7f46e1031cc62b853a463220fc72d Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Mon, 18 Aug 2025 07:55:14 -0700 Subject: [PATCH 10/25] 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 9c1e7ea9042c8d7d8c7e993351f71d745e9efab5 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:04 -0700 Subject: [PATCH 11/25] wiphy: add driver quirk for the colocated scan flag Some drivers do not handle the colocated scan flag very well and this results in BSS's not being seen in scans. This of course results in very poor behavior. This has been seen on ath11k specifically but after some conversations [1] on the linux-wireless mailing list others have reported issues with iwlwifi acting similarly. Since there are many hardware variants that use both ath11k and iwlwifi this new quirk isn't being forced to those drivers, but let users configure IWD to disable the flag if needed. [1] https://lore.kernel.org/linux-wireless/d1e75a08-047d-7947-d51a-2e486efead77@candelatech.com/ --- src/wiphy.c | 23 +++++++++++++++++------ src/wiphy.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/wiphy.c b/src/wiphy.c index fb544fe6a..c7f2805e0 100644 --- a/src/wiphy.c +++ b/src/wiphy.c @@ -75,6 +75,8 @@ enum driver_flag { OWE_DISABLE = 0x8, MULTICAST_RX_DISABLE = 0x10, SAE_DISABLE = 0x20, + /* Disables use of the NL80211_SCAN_FLAG_COLOCATED_6GHZ flag in scans */ + COLOCATED_SCAN_DISABLE = 0x40, }; struct driver_flag_name { @@ -103,12 +105,13 @@ static const struct driver_info driver_infos[] = { }; static const struct driver_flag_name driver_flag_names[] = { - { "DefaultInterface", DEFAULT_IF }, - { "ForcePae", FORCE_PAE }, - { "PowerSaveDisable", POWER_SAVE_DISABLE }, - { "OweDisable", OWE_DISABLE }, - { "MulticastRxDisable", MULTICAST_RX_DISABLE }, - { "SaeDisable", SAE_DISABLE }, + { "DefaultInterface", DEFAULT_IF }, + { "ForcePae", FORCE_PAE }, + { "PowerSaveDisable", POWER_SAVE_DISABLE }, + { "OweDisable", OWE_DISABLE }, + { "MulticastRxDisable", MULTICAST_RX_DISABLE }, + { "SaeDisable", SAE_DISABLE }, + { "ColocatedScanDisable", COLOCATED_SCAN_DISABLE }, }; struct wiphy { @@ -963,6 +966,11 @@ bool wiphy_supports_multicast_rx(const struct wiphy *wiphy) !(wiphy->driver_flags & MULTICAST_RX_DISABLE); } +bool wiphy_supports_colocated_flag(const struct wiphy *wiphy) +{ + return !(wiphy->driver_flags & COLOCATED_SCAN_DISABLE); +} + const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy, enum band_freq band, size_t *size) @@ -1382,6 +1390,9 @@ static void wiphy_print_basic_info(struct wiphy *wiphy) if (wiphy->driver_flags & SAE_DISABLE) flags = l_strv_append(flags, "SaeDisable"); + if (wiphy->driver_flags & COLOCATED_SCAN_DISABLE) + flags = l_strv_append(flags, "ColocatedScanDisable"); + joined = l_strjoinv(flags, ' '); l_info("\tDriver Flags: %s", joined); diff --git a/src/wiphy.h b/src/wiphy.h index 9fcbdcd2d..19d79405c 100644 --- a/src/wiphy.h +++ b/src/wiphy.h @@ -144,6 +144,7 @@ bool wiphy_country_is_unknown(struct wiphy *wiphy); bool wiphy_supports_uapsd(const struct wiphy *wiphy); bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy); bool wiphy_supports_multicast_rx(const struct wiphy *wiphy); +bool wiphy_supports_colocated_flag(const struct wiphy *wiphy); const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy, enum band_freq band, From 4bf28932c67ae1845e2c6b585354d78288e8ae3a Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:05 -0700 Subject: [PATCH 12/25] wiphy: add comments around the driver quirks --- src/wiphy.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/wiphy.c b/src/wiphy.c index c7f2805e0..b6774f69d 100644 --- a/src/wiphy.c +++ b/src/wiphy.c @@ -69,11 +69,23 @@ static uint32_t work_ids; static unsigned int wiphy_dump_id; enum driver_flag { + /* Force the use of the default interface created by the kernel */ DEFAULT_IF = 0x1, + /* + * Force the use of the PAE socket rather than control port, even if + * control port is supported + */ FORCE_PAE = 0x2, + /* Disable power save on the adapter during initialization */ POWER_SAVE_DISABLE = 0x4, + /* Don't use OWE when connecting to open networks */ OWE_DISABLE = 0x8, + /* Disables multicast RX frame registration */ MULTICAST_RX_DISABLE = 0x10, + /* + * Don't use SAE (WPA3) when connecting to hybrid networks. This will + * prevent IWD from connecting to WPA3-only networks + */ SAE_DISABLE = 0x20, /* Disables use of the NL80211_SCAN_FLAG_COLOCATED_6GHZ flag in scans */ COLOCATED_SCAN_DISABLE = 0x40, From c7dfa1405e1354a66334ab139a8b355d13c2515d Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:06 -0700 Subject: [PATCH 13/25] scan: check support before using colocated flag --- src/scan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/scan.c b/src/scan.c index dfd667bbb..d9f27c83b 100644 --- a/src/scan.c +++ b/src/scan.c @@ -414,7 +414,8 @@ static struct l_genl_msg *scan_build_cmd(struct scan_context *sc, if (params->ap_scan) flags |= NL80211_SCAN_FLAG_AP; - flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ; + if (wiphy_supports_colocated_flag(sc->wiphy)) + flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ; if (flags) l_genl_msg_append_attr(msg, NL80211_ATTR_SCAN_FLAGS, 4, &flags); From 7bdbd6ada0d6a7c8e16ee30bff86f2f99c9a5500 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:07 -0700 Subject: [PATCH 14/25] monitor: add Cisco Meraki as a printable vendor --- monitor/nlmon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/monitor/nlmon.c b/monitor/nlmon.c index 7924f6f28..65437dc81 100644 --- a/monitor/nlmon.c +++ b/monitor/nlmon.c @@ -404,6 +404,7 @@ static const struct { { { 0x00, 0x50, 0xf2 }, "Microsoft" }, { { 0x00, 0x90, 0x4c }, "Epigram" }, { { 0x50, 0x6f, 0x9a }, "Wi-Fi Alliance" }, + { { 0x00, 0x18, 0x0a }, "Cisco Meraki" }, { } }; From 7524e68275b500910a544b2d99cdc5ced1a4e777 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:08 -0700 Subject: [PATCH 15/25] vendor_quirks: initial skeleton This module will provide a database for known issues or quirks with wireless vendors. For now the list of quirks is limited to 32 as that is the size returned in the bit mask. This could be extended to 64 in the future if needed, but of course the goal is to never reach that level. The vendor_quirks() API is intended to be called from scan.c when parsing vendor attributes. This will lookup any quirks associated with the OUI provided and a mask of quirks will be returned. This can be repeated against all the vendor OUI's seen in the scan. The result is then a bitmask containing all quirks for that BSS. This can then be referenced later during various operations in IWD. --- Makefile.am | 2 ++ src/vendor_quirks.c | 53 +++++++++++++++++++++++++++++++++++++++++++++ src/vendor_quirks.h | 25 +++++++++++++++++++++ 3 files changed, 80 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..ccfcb4448 --- /dev/null +++ b/src/vendor_quirks.c @@ -0,0 +1,53 @@ +/* + * + * 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]; + uint32_t quirks; +} quirk_db[] = { + { } +}; + +uint32_t vendor_quirks(const uint8_t *oui) +{ + size_t i; + uint32_t ret = 0; + + for (i = 0; i < L_ARRAY_SIZE(quirk_db); i++) { + if (memcmp(quirk_db[i].oui, oui, 3)) + continue; + + ret |= quirk_db[i].quirks; + } + + return ret; +} diff --git a/src/vendor_quirks.h b/src/vendor_quirks.h new file mode 100644 index 000000000..758073b30 --- /dev/null +++ b/src/vendor_quirks.h @@ -0,0 +1,25 @@ +/* + * + * 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 + * + */ + +#include + +uint32_t vendor_quirks(const uint8_t *oui); From f558eeefe540c69440a18989ee85b1f3f5e2d172 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:09 -0700 Subject: [PATCH 16/25] vendor_quirks: add two new vendor quirks VENDOR_QUIRK_BAD_BSS_TM_CANDIDATE_LIST: 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. VENDOR_QUIRK_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 | 5 ++++- src/vendor_quirks.h | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/vendor_quirks.c b/src/vendor_quirks.c index ccfcb4448..e005ba3b5 100644 --- a/src/vendor_quirks.c +++ b/src/vendor_quirks.c @@ -34,7 +34,10 @@ static const struct { uint8_t oui[3]; uint32_t quirks; } quirk_db[] = { - { } + /* Cisco Meraki */ + { { 0x00, 0x18, 0x0a }, VENDOR_QUIRK_BAD_BSS_TM_CANDIDATE_LIST }, + /* Hewlitt Packard, owns Aruba */ + { { 0x00, 0x0b, 0x86 }, VENDOR_QUIRK_REPLAY_COUNTER_MISMATCH }, }; uint32_t vendor_quirks(const uint8_t *oui) diff --git a/src/vendor_quirks.h b/src/vendor_quirks.h index 758073b30..6c587d452 100644 --- a/src/vendor_quirks.h +++ b/src/vendor_quirks.h @@ -22,4 +22,19 @@ #include +enum vendor_quirk { + /* + * The neighbor list in a BSS Transition Management request from an AP + * contains a very sparse BSS list which generally leads to poor roaming + * decisions. + */ + VENDOR_QUIRK_BAD_BSS_TM_CANDIDATE_LIST = 1 << 0, + /* + * The PTK/GTK replay counter differs between a scan and FT + * authentication. This is not allowable in the spec, but seen with + * certain vendors. + */ + VENDOR_QUIRK_REPLAY_COUNTER_MISMATCH = 1 << 1, +}; + uint32_t vendor_quirks(const uint8_t *oui); From 707d2377cce8463a5fb10bf02ba37653e849ca31 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:10 -0700 Subject: [PATCH 17/25] 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 951484009907f34d82a6b1aaf12b32d6e1efc9e6 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:11 -0700 Subject: [PATCH 18/25] handshake: add vendor quirks into handshake object --- src/handshake.c | 6 ++++++ src/handshake.h | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/handshake.c b/src/handshake.c index 92edac308..02f0e4361 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, + uint32_t quirks_mask) +{ + s->vendor_quirks = quirks_mask; +} + 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..df0f83153 100644 --- a/src/handshake.h +++ b/src/handshake.h @@ -107,6 +107,7 @@ struct handshake_state { uint8_t *authenticator_fte; uint8_t *supplicant_fte; uint8_t *vendor_ies; + uint32_t vendor_quirks; size_t vendor_ies_len; enum ie_rsn_cipher_suite pairwise_cipher; enum ie_rsn_cipher_suite group_cipher; @@ -237,6 +238,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, + uint32_t quirks_mask); + void handshake_state_set_kh_ids(struct handshake_state *s, const uint8_t *r0khid, size_t r0khid_len, const uint8_t *r1khid); From 571d01b7891f5b5c42dae5be16f01e28ea749bdf Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:12 -0700 Subject: [PATCH 19/25] 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 | 1 + 2 files changed, 7 insertions(+) diff --git a/src/scan.c b/src/scan.c index d9f27c83b..b87c46216 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; + + bss->vendor_quirks |= vendor_quirks(data); + 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..b2a63505f 100644 --- a/src/scan.h +++ b/src/scan.h @@ -79,6 +79,7 @@ struct scan_bss { uint8_t *wfd; /* Concatenated WFD IEs */ ssize_t wfd_size; /* Size of Concatenated WFD IEs */ int8_t snr; + uint32_t vendor_quirks; bool mde_present : 1; bool cc_present : 1; bool cap_rm_neighbor_report : 1; From 1c32dcf2aadbd3b20d775724691f48a27e0ef053 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:13 -0700 Subject: [PATCH 20/25] 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 a4c3e7d1a..339ee73c8 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 8392549ae27ee44c5305a1a67e0d96aa203f802d Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:14 -0700 Subject: [PATCH 21/25] 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 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/handshake.c b/src/handshake.c index 02f0e4361..6db897bf9 100644 --- a/src/handshake.c +++ b/src/handshake.c @@ -44,6 +44,7 @@ #include "src/erp.h" #include "src/band.h" #include "src/pmksa.h" +#include "src/vendor_quirks.h" static inline unsigned int n_ecc_groups(void) { @@ -914,11 +915,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 & VENDOR_QUIRK_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 994bcbc8798055b6e02a442bf47915617cac8f7b Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:15 -0700 Subject: [PATCH 22/25] 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 339ee73c8..4919f89ee 100644 --- a/src/station.c +++ b/src/station.c @@ -2435,8 +2435,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, @@ -2448,6 +2456,8 @@ 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: + station_debug_event(station, "full-roam-scan"); scan_cancel(netdev_get_wdev_id(station->netdev), station->roam_scan_id); @@ -3373,7 +3383,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 8e0e4c5538e317958febdf52e258e1743b7394e7 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:16 -0700 Subject: [PATCH 23/25] 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 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/station.c b/src/station.c index 4919f89ee..3424964d8 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 @@ -3378,7 +3379,9 @@ 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) && + !(station->connected_bss->vendor_quirks & + VENDOR_QUIRK_BAD_BSS_TM_CANDIDATE_LIST)) { l_debug("roam: AP sent a preferred candidate list"); station_neighbor_report_cb(station->netdev, 0, body + pos, body_len - pos, station); From 53aed35a2cb8f935b5b0626ee3533059d9ef5c6f Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:17 -0700 Subject: [PATCH 24/25] station: clear roam_freqs on delayed roam If there were no BSS candidates found after trying to roam make sure the old roam_freqs list gets cleared so IWD doesn't end up scanning potentially old frequencies on the next retry. --- src/station.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/station.c b/src/station.c index 3424964d8..968aafb08 100644 --- a/src/station.c +++ b/src/station.c @@ -2407,6 +2407,11 @@ static void station_roam_retry(struct station *station) station->roam_scan_full = false; station->ap_directed_roaming = false; + if (station->roam_freqs) { + scan_freq_set_free(station->roam_freqs); + station->roam_freqs = NULL; + } + if (station->signal_low) station_roam_timeout_rearm(station, roam_retry_interval); } From 27dbb552ed77f1fea44e110ddace07274c0d74f8 Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Fri, 22 Aug 2025 12:51:18 -0700 Subject: [PATCH 25/25] auto-t: add AP roam test for bad neighbor reports/candidate lists --- .../testAPRoam/bad_neighbor_report_test.py | 133 ++++++++++++++++++ 1 file changed, 133 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..4bf3b63b7 --- /dev/null +++ b/autotests/testAPRoam/bad_neighbor_report_test.py @@ -0,0 +1,133 @@ +#!/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 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)