diff --git a/.CodeQL.yml b/.CodeQL.yml index 1455700093..1ccd4b8022 100644 --- a/.CodeQL.yml +++ b/.CodeQL.yml @@ -2,13 +2,14 @@ # https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/codeql/troubleshooting/bugs/generated-library-code # (Access restricted to Microsoft employees only.) -# The following paths are explicitly classified which indicates they should no +# The following paths are explicitly classified which indicates they should not # be analyzed by CodeQL and generate alerts. path_classifiers: docs: - docs generated: + - build - src/cs - src/generated submodules: - - subodules + - submodules diff --git a/.azure/OneBranch.Publish.yml b/.azure/OneBranch.Publish.yml index eae884a1d1..f5dc0e9eee 100644 --- a/.azure/OneBranch.Publish.yml +++ b/.azure/OneBranch.Publish.yml @@ -78,8 +78,9 @@ parameters: type: object default: - name: RPM - destionations: + destinations: - microsoft-fedora40-prod-yum # 40 6.8 + - microsoft-fedora41-prod-yum # 41 6.11 - name: DEB destinations: - microsoft-ubuntu-noble-prod-apt # 24.04 6.8 diff --git a/.github/workflows/build-darwin-framework.yml b/.github/workflows/build-darwin-framework.yml index 186ed64055..be92ab1f68 100644 --- a/.github/workflows/build-darwin-framework.yml +++ b/.github/workflows/build-darwin-framework.yml @@ -25,3 +25,4 @@ jobs: uses: ./.github/workflows/build-reuse-darwin-framework.yml with: config: 'Release' + repo: ${{ github.repository }} diff --git a/.github/workflows/build-reuse-darwin-framework.yml b/.github/workflows/build-reuse-darwin-framework.yml index 627168e411..0da51e5c93 100644 --- a/.github/workflows/build-reuse-darwin-framework.yml +++ b/.github/workflows/build-reuse-darwin-framework.yml @@ -7,6 +7,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -42,30 +46,31 @@ jobs: ref: ${{ inputs.ref }} config: ${{ inputs.config }} plat: ${{ matrix.plat }} - os: macos-12 + os: macos-13 arch: ${{ matrix.arch }} tls: ${{ inputs.tls }} static: ${{ inputs.static }} + repo: ${{ inputs.repo }} build-darwin-universal: name: Build Universal Binaries needs: [build-darwin] - runs-on: macos-12 + runs-on: macos-13 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Download Build Artifacts (x64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: ${{ inputs.config }}-macos-macos-12-x64-${{ inputs.tls }}${{ inputs.static }} + name: ${{ inputs.config }}-macos-macos-13-x64-${{ inputs.tls }}${{ inputs.static }} path: artifacts - name: Download Build Artifacts (arm64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: ${{ inputs.config }}-macos-macos-12-arm64-${{ inputs.tls }}${{ inputs.static }} + name: ${{ inputs.config }}-macos-macos-13-arm64-${{ inputs.tls }}${{ inputs.static }} path: artifacts - name: Build Package shell: pwsh @@ -73,7 +78,7 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 with: - name: ${{ inputs.config }}-macos-macos-12-universal-${{ inputs.tls }}${{ inputs.static }} + name: ${{ inputs.config }}-macos-macos-13-universal-${{ inputs.tls }}${{ inputs.static }} path: artifacts build-darwin-framework: @@ -87,17 +92,17 @@ jobs: { plat: "ios", arch: "arm64" }, { plat: "macos", arch: "universal" }, ] - runs-on: macos-12 + runs-on: macos-13 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Download Build Artifacts (x64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: ${{ inputs.config }}-${{ matrix.vec.plat }}-macos-12-${{ matrix.vec.arch }}-${{ inputs.tls }}${{ inputs.static }} + name: ${{ inputs.config }}-${{ matrix.vec.plat }}-macos-13-${{ matrix.vec.arch }}-${{ inputs.tls }}${{ inputs.static }} path: artifacts - name: Build Framework shell: pwsh @@ -105,33 +110,33 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 with: - name: Framework-${{ inputs.config }}-${{ matrix.vec.plat }}-macos-12-${{ matrix.vec.arch }}-${{ inputs.tls }}${{ inputs.static }} + name: Framework-${{ inputs.config }}-${{ matrix.vec.plat }}-macos-13-${{ matrix.vec.arch }}-${{ inputs.tls }}${{ inputs.static }} path: artifacts build-darwin-xcframework: name: Build Darwin XCFramework needs: [build-darwin-framework] - runs-on: macos-12 + runs-on: macos-13 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Download Build Artifacts (iOS x64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: Framework-${{ inputs.config }}-ios-macos-12-x64-openssl + name: Framework-${{ inputs.config }}-ios-macos-13-x64-openssl path: artifacts - name: Download Build Artifacts (iOS arm64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: Framework-${{ inputs.config }}-ios-macos-12-arm64-openssl + name: Framework-${{ inputs.config }}-ios-macos-13-arm64-openssl path: artifacts - name: Download Build Artifacts (MacOS Universal) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: - name: Framework-${{ inputs.config }}-macos-macos-12-universal-openssl + name: Framework-${{ inputs.config }}-macos-macos-13-universal-openssl path: artifacts - name: Build XCFramework shell: pwsh diff --git a/.github/workflows/build-reuse-unix.yml b/.github/workflows/build-reuse-unix.yml index 26b513bb77..518ba82bf3 100644 --- a/.github/workflows/build-reuse-unix.yml +++ b/.github/workflows/build-reuse-unix.yml @@ -9,6 +9,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -33,7 +37,7 @@ on: # - ubuntu-20.04 # - ubuntu-22.04 # - ubuntu-24.04 - # - macos-12 + # - macos-13 arch: required: false default: 'x64' @@ -89,9 +93,9 @@ jobs: image: ${{ (inputs.plat == 'linux' && format('ghcr.io/microsoft/msquic/linux-build-xcomp:{0}-cross', inputs.os)) || '' }} steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Set ownership if: inputs.plat == 'linux' diff --git a/.github/workflows/build-reuse-win.yml b/.github/workflows/build-reuse-win.yml index 4e09d717d4..41f614cbbd 100644 --- a/.github/workflows/build-reuse-win.yml +++ b/.github/workflows/build-reuse-win.yml @@ -9,6 +9,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -69,12 +73,12 @@ jobs: runs-on: ${{ inputs.os }} steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Install Perl - uses: shogo82148/actions-setup-perl@9c1eca9952ccc07f9ca4a2097b63df93d9d138e9 + uses: shogo82148/actions-setup-perl@98dfedee230bcf1ee68d5b021931fc8d63f2016e with: perl-version: '5.34' - name: Install NASM @@ -86,6 +90,13 @@ jobs: if: inputs.build == '-Test' shell: pwsh run: scripts/build.ps1 -Config ${{ inputs.config }} -Platform ${{ inputs.plat }} -Arch ${{ inputs.arch }} -Tls ${{ inputs.tls }} -DisablePerf -DynamicCRT ${{ inputs.sanitize }} + - name: Build External Platform Test + if: inputs.build == '-Test' + shell: pwsh + run: | + cmake --install build\${{ inputs.plat }}\${{ inputs.arch }}_${{ inputs.tls }} --config ${{ inputs.config }} + cmake src/platform/unittest/external -G "Visual Studio 17 2022" -A ${{ inputs.arch }} -B build_external "-DCMAKE_INSTALL_PREFIX:PATH=C:/Program Files/msquic" + cmake --build build_external --config ${{ inputs.config }} - name: Build For Perf if: inputs.build == '-Perf' shell: pwsh diff --git a/.github/workflows/build-reuse-winkernel.yml b/.github/workflows/build-reuse-winkernel.yml index ef4ebc17d5..b6212bc4a2 100644 --- a/.github/workflows/build-reuse-winkernel.yml +++ b/.github/workflows/build-reuse-winkernel.yml @@ -9,6 +9,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -58,9 +62,9 @@ jobs: runs-on: ${{ inputs.os }} steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Prepare Machine shell: pwsh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12489fe75f..8d1ba58ae2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,6 +53,7 @@ jobs: arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} static: ${{ matrix.static }} + repo: ${{ github.repository }} build-windows-kernel: name: WinKernel @@ -72,6 +73,7 @@ jobs: os: ${{ matrix.os }} arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} + repo: ${{ github.repository }} build-ubuntu-cross-compile: name: UbuntuArm @@ -93,6 +95,7 @@ jobs: arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} static: ${{ matrix.static }} + repo: ${{ github.repository }} build-ubuntu: name: Ubuntu @@ -177,6 +180,7 @@ jobs: clang: ${{ matrix.clang }} codecheck: ${{ matrix.codecheck }} xdp: ${{ matrix.xdp }} + repo: ${{ github.repository }} build-darwin: name: MacOs @@ -186,7 +190,7 @@ jobs: matrix: config: ['Debug', 'Release'] plat: [macos, ios] - os: ['macos-12'] + os: ['macos-13'] arch: [x64, arm64] tls: [openssl, openssl3] static: ['', '-Static'] @@ -198,6 +202,7 @@ jobs: arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} static: ${{ matrix.static }} + repo: ${{ github.repository }} build-nuget: name: Build Nuget Package @@ -213,21 +218,21 @@ jobs: runs-on: windows-2022 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Download Build Artifacts - uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 + uses: dawidd6/action-download-artifact@80620a5d27ce0ae443b965134db88467fc607b43 with: name: Release-${{ matrix.vec.plat }}-windows-2022-x86-${{ matrix.vec.tls }} path: artifacts if_no_artifact_found: ignore - name: Download Build Artifacts - uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 + uses: dawidd6/action-download-artifact@80620a5d27ce0ae443b965134db88467fc607b43 with: name: Release-${{ matrix.vec.plat }}-windows-2022-x64-${{ matrix.vec.tls }} path: artifacts if_no_artifact_found: ignore - name: Download Build Artifacts - uses: dawidd6/action-download-artifact@bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 + uses: dawidd6/action-download-artifact@80620a5d27ce0ae443b965134db88467fc607b43 with: name: Release-${{ matrix.vec.plat }}-windows-2022-arm64-${{ matrix.vec.tls }} path: artifacts diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index cf2132acdd..fec34ec7e7 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -16,22 +16,36 @@ jobs: cargo: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-xlarge] + features: ["", "--features static", "--features schannel", "--features schannel,static"] + exclude: + - os: ubuntu-latest + features: "--features schannel" + - os: ubuntu-latest + features: "--features schannel,static" + - os: macos-latest + features: "--features schannel" + - os: macos-latest + features: "--features schannel,static" + - os: macos-latest-xlarge + features: "--features schannel" + - os: macos-latest-xlarge + features: "--features schannel,static" runs-on: ${{ matrix.os }} name: Cargo steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Prepare Machine run: scripts/prepare-machine.ps1 -Tls openssl -ForBuild shell: pwsh - name: Install Perl if: runner.os == 'Windows' - uses: shogo82148/actions-setup-perl@9c1eca9952ccc07f9ca4a2097b63df93d9d138e9 + uses: shogo82148/actions-setup-perl@98dfedee230bcf1ee68d5b021931fc8d63f2016e with: perl-version: '5.34' - name: Install NASM @@ -40,9 +54,13 @@ jobs: - name: Install Cargo if: runner.os == 'Linux' run: curl https://sh.rustup.rs -sSf | sh -s -- -y + - name: Cargo fmt + run: cargo fmt --all -- --check + - name: Cargo clippy + run: cargo clippy --all-targets -- -D warnings - name: Cargo build - run: cargo build --all + run: cargo build --all ${{ matrix.features }} - name: Cargo test - run: cargo test --all + run: cargo test --all ${{ matrix.features }} - name: Cargo Publish (dry run) run: cargo publish --dry-run --allow-dirty diff --git a/.github/workflows/check-clog.yml b/.github/workflows/check-clog.yml index f7dec6410c..85b8b99fb8 100644 --- a/.github/workflows/check-clog.yml +++ b/.github/workflows/check-clog.yml @@ -20,11 +20,11 @@ jobs: runs-on: windows-latest steps: - name: Setup .NET - uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee + uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 with: dotnet-version: 6.0.x - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Prepare Machine run: scripts/prepare-machine.ps1 shell: pwsh diff --git a/.github/workflows/check-dotnet.yml b/.github/workflows/check-dotnet.yml index 77144cf8a8..fcb104c3e0 100644 --- a/.github/workflows/check-dotnet.yml +++ b/.github/workflows/check-dotnet.yml @@ -20,11 +20,11 @@ jobs: runs-on: windows-latest steps: - name: Setup .NET - uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee + uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 with: dotnet-version: 6.0.x - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Prepare Machine run: scripts/prepare-machine.ps1 shell: pwsh diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 1fc20031fa..a5f8d18e5a 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -37,6 +37,7 @@ jobs: tls: ${{ matrix.vec.tls }} sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} + repo: ${{ github.repository }} bvt-winlatest: name: BVT WinPrerelease @@ -52,7 +53,7 @@ jobs: - "1ES.Pool=1es-msquic-pool" - "1ES.ImageOverride=WinServerPrerelease" steps: - - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - name: Download Build Artifacts @@ -103,7 +104,7 @@ jobs: pr-allocfail: 100 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 @@ -149,7 +150,7 @@ jobs: pr-timeout: 600000 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 @@ -183,7 +184,7 @@ jobs: runs-on: windows-2022 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - name: Prepare Machine @@ -227,13 +228,13 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 name: Download Merged Coverage Report with: name: merged - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 + uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e with: fail_ci_if_error: true files: msquiccoverage.xml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0c46d2308d..67bb9f63c6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,11 +27,11 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 2 submodules: 'recursive' @@ -51,7 +51,7 @@ jobs: cmake --build . --target OpenSSL_Target - name: Initialize CodeQL - uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b + uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae with: languages: cpp config-file: ./.github/codeql/codeql-config.yml @@ -62,4 +62,4 @@ jobs: cmake --build . - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b + uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae diff --git a/.github/workflows/docker-publish-xcomp.yml b/.github/workflows/docker-publish-xcomp.yml index b411c71287..f4cf2e2da2 100644 --- a/.github/workflows/docker-publish-xcomp.yml +++ b/.github/workflows/docker-publish-xcomp.yml @@ -40,12 +40,12 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # Login against a Docker registry except on PR # https://github.com/docker/login-action @@ -61,14 +61,14 @@ jobs: # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta - uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 with: context: .docker/ubuntu-${{ matrix.version }} file: .docker/ubuntu-${{ matrix.version }}/Dockerfile diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 658b24b66d..7d13243e01 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -26,11 +26,11 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: submodules: recursive @@ -48,14 +48,14 @@ jobs: # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta - uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 + uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 with: context: . file: scripts/qns.Dockerfile diff --git a/.github/workflows/dotnet-test.yml b/.github/workflows/dotnet-test.yml index 55b56524c8..0737d43e34 100644 --- a/.github/workflows/dotnet-test.yml +++ b/.github/workflows/dotnet-test.yml @@ -25,6 +25,7 @@ jobs: uses: ./.github/workflows/build-reuse-darwin-framework.yml with: config: 'Debug' + repo: ${{ github.repository }} build-linux: name: Ubuntu @@ -45,6 +46,7 @@ jobs: arch: ${{ matrix.vec.arch }} tls: ${{ matrix.vec.tls }} xdp: ${{ matrix.vec.xdp }} + repo: ${{ github.repository }} build-windows: name: Windows @@ -63,6 +65,7 @@ jobs: os: ${{ matrix.vec.os }} arch: ${{ matrix.vec.arch }} tls: ${{ matrix.vec.tls }} + repo: ${{ github.repository }} dotnet-test: name: DotNet Test @@ -76,12 +79,12 @@ jobs: { plat: "linux", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, { plat: "linux", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, { plat: "linux", os: "ubuntu-24.04", arch: "x64", tls: "openssl3", xdp: "-UseXdp" }, - { plat: "macos", os: "macos-12", arch: "universal", tls: "openssl" }, + { plat: "macos", os: "macos-13", arch: "universal", tls: "openssl" }, ] runs-on: ${{ matrix.vec.os }} steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Prepare Machine shell: pwsh run: scripts/prepare-machine.ps1 -ForTest ${{ matrix.vec.xdp }} @@ -90,9 +93,9 @@ jobs: with: name: Debug-${{ matrix.vec.plat }}-${{ matrix.vec.os }}-${{ matrix.vec.arch }}-${{ matrix.vec.tls }}${{ matrix.vec.xdp }} path: artifacts - - uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee + - uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 with: dotnet-version: 6.0.x - name: Run Lang Interop shell: pwsh - run: scripts/DotNetTest.ps1 -Config Debug -Arch ${{ matrix.vec.arch }} -Tls ${{ matrix.vec.tls }} + run: scripts/DotNetTest.ps1 -Config Debug -Arch ${{ matrix.vec.arch }} -Tls ${{ matrix.vec.tls }} -DomainName "google.com" diff --git a/.github/workflows/mirror-repo.yml b/.github/workflows/mirror-repo.yml index f03096c40e..3138098d13 100644 --- a/.github/workflows/mirror-repo.yml +++ b/.github/workflows/mirror-repo.yml @@ -19,7 +19,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - name: Sync mirror diff --git a/.github/workflows/netperf.yml b/.github/workflows/netperf.yml index 0c4cb26ed6..c9fa0608ae 100644 --- a/.github/workflows/netperf.yml +++ b/.github/workflows/netperf.yml @@ -10,6 +10,7 @@ on: - .gitmodules - scripts/secnetperf.ps1 - scripts/secnetperf-helpers.psm1 + - scripts/quic_callback.ps1 - scripts/prepare-machine.ps1 - scripts/xdp.json - src/bin/** @@ -27,6 +28,7 @@ on: - .gitmodules - scripts/secnetperf.ps1 - scripts/secnetperf-helpers.psm1 + - scripts/quic_callback.ps1 - scripts/prepare-machine.ps1 - scripts/xdp.json - src/bin/** @@ -51,7 +53,7 @@ jobs: runs-on: windows-latest steps: - name: Run NetPerf Workflow - timeout-minutes: 90 + timeout-minutes: 120 shell: pwsh run: | $url = "https://raw.githubusercontent.com/microsoft/netperf/main/run-workflow.ps1" diff --git a/.github/workflows/package-alpine-linux.yml b/.github/workflows/package-alpine-linux.yml index 8e1aaeabd1..e06444ed7b 100644 --- a/.github/workflows/package-alpine-linux.yml +++ b/.github/workflows/package-alpine-linux.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Generate APKBUILD @@ -70,7 +70,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Download Package uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: @@ -79,7 +79,7 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Set up .NET 9.0 - uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee + uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 with: dotnet-version: ${{ matrix.vec.dotnetVersion }} - name: Build .NET QUIC Test Project diff --git a/.github/workflows/package-linux.yml b/.github/workflows/package-linux.yml index 5cc8ed740d..c52e49b1c3 100644 --- a/.github/workflows/package-linux.yml +++ b/.github/workflows/package-linux.yml @@ -123,7 +123,7 @@ jobs: runs-on: ${{ matrix.vec.os }} steps: - name: Checkout Repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Download Package uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 with: @@ -137,7 +137,7 @@ jobs: - name: Set up QEMU uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf - name: Set up .NET 9.0 - uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee + uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 with: dotnet-version: ${{ matrix.vec.dotnetVersion }} - name: Build .NET QUIC Test Project diff --git a/.github/workflows/package-reuse-linux.yml b/.github/workflows/package-reuse-linux.yml index 8d31394f52..893f621824 100644 --- a/.github/workflows/package-reuse-linux.yml +++ b/.github/workflows/package-reuse-linux.yml @@ -75,6 +75,7 @@ jobs: clang: ${{ inputs.clang }} build: ${{ inputs.build }} xdp: ${{ inputs.xdp }} + repo: ${{ github.repository }} package: name: Package @@ -82,7 +83,7 @@ jobs: runs-on: ${{ inputs.os }} steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - name: Prepare Machine diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index 840fc7f52b..dc82265ade 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -25,7 +25,7 @@ jobs: platform: [x86, x64] steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce - name: Build @@ -49,9 +49,9 @@ jobs: configuration: [Release, Debug] steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Setup .NET - uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee + uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 with: dotnet-version: 6.0.x - name: Install Dependencies diff --git a/.github/workflows/publish-docfx.yml b/.github/workflows/publish-docfx.yml index b2de239d7d..ff81ede485 100644 --- a/.github/workflows/publish-docfx.yml +++ b/.github/workflows/publish-docfx.yml @@ -29,9 +29,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Dotnet Setup - uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee + uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 with: dotnet-version: 7.x diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index b58bc400f6..dbd6cfb323 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -23,7 +23,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: persist-credentials: false @@ -51,6 +51,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae with: sarif_file: results.sarif diff --git a/.github/workflows/stress.yml b/.github/workflows/stress.yml index d886e89289..3e850df271 100644 --- a/.github/workflows/stress.yml +++ b/.github/workflows/stress.yml @@ -40,6 +40,7 @@ jobs: tls: ${{ matrix.vec.tls }} sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} + repo: ${{ github.repository }} build-unix: name: Build Unix @@ -47,8 +48,8 @@ jobs: fail-fast: false matrix: vec: [ - { config: "Debug", plat: "macos", os: "macos-12", arch: "x64", tls: "openssl", build: "-Test" }, - { config: "Debug", plat: "macos", os: "macos-12", arch: "x64", tls: "openssl3", build: "-Test" }, + { config: "Debug", plat: "macos", os: "macos-13", arch: "x64", tls: "openssl", build: "-Test" }, + { config: "Debug", plat: "macos", os: "macos-13", arch: "x64", tls: "openssl3", build: "-Test" }, { config: "Debug", plat: "linux", os: "ubuntu-20.04", arch: "x64", tls: "openssl", sanitize: "-Sanitize", build: "-Test" }, { config: "Debug", plat: "linux", os: "ubuntu-20.04", arch: "x64", tls: "openssl3", sanitize: "-Sanitize", build: "-Test" }, { config: "Debug", plat: "linux", os: "ubuntu-22.04", arch: "x64", tls: "openssl3", sanitize: "-Sanitize", build: "-Test" }, @@ -66,6 +67,7 @@ jobs: sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} xdp: ${{ matrix.vec.xdp }} + repo: ${{ github.repository }} stress: name: Stress @@ -79,8 +81,8 @@ jobs: { config: "Debug", plat: "linux", os: "ubuntu-22.04", arch: "x64", tls: "openssl3", sanitize: "-Sanitize", build: "-Test" }, { config: "Debug", plat: "linux", os: "ubuntu-24.04", arch: "x64", tls: "openssl3", sanitize: "-Sanitize", build: "-Test" }, { config: "Debug", plat: "linux", os: "ubuntu-24.04", arch: "x64", tls: "openssl3", build: "-Test", xdp: "-UseXdp" }, - { config: "Debug", plat: "macos", os: "macos-12", arch: "x64", tls: "openssl", build: "-Test" }, - { config: "Debug", plat: "macos", os: "macos-12", arch: "x64", tls: "openssl3", build: "-Test" }, + { config: "Debug", plat: "macos", os: "macos-13", arch: "x64", tls: "openssl", build: "-Test" }, + { config: "Debug", plat: "macos", os: "macos-13", arch: "x64", tls: "openssl3", build: "-Test" }, { config: "Debug", plat: "windows", os: "windows-2022", arch: "x64", tls: "schannel", sanitize: "-Sanitize", build: "-Test" }, { config: "Debug", plat: "windows", os: "windows-2022", arch: "x64", tls: "schannel", xdp: "-UseXdp", sanitize: "-Sanitize", build: "-Test" }, { config: "Debug", plat: "windows", os: "windows-2022", arch: "x64", tls: "openssl", build: "-Test" }, @@ -99,7 +101,7 @@ jobs: pr-allocfail: 100 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 if: matrix.vec.plat == 'windows' with: @@ -159,7 +161,7 @@ jobs: pr-timeout: 600000 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 if: matrix.vec.plat == 'windows' with: diff --git a/.github/workflows/test-down-level.yml b/.github/workflows/test-down-level.yml index ed2d5d5af5..d110ff1b6e 100644 --- a/.github/workflows/test-down-level.yml +++ b/.github/workflows/test-down-level.yml @@ -31,26 +31,26 @@ jobs: { release: "2.2.6", os: "windows-2022", arch: "x64", tls: "openssl" }, { release: "2.2.6", os: "windows-2022", arch: "x64", tls: "openssl3" }, # v2.3 - { release: "2.3.6", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, - { release: "2.3.6", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, - { release: "2.3.6", os: "windows-2022", arch: "x64", tls: "schannel" }, - { release: "2.3.6", os: "windows-2022", arch: "x64", tls: "openssl" }, - { release: "2.3.6", os: "windows-2022", arch: "x64", tls: "openssl3" }, + { release: "2.3.8", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, + { release: "2.3.8", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, + { release: "2.3.8", os: "windows-2022", arch: "x64", tls: "schannel" }, + { release: "2.3.8", os: "windows-2022", arch: "x64", tls: "openssl" }, + { release: "2.3.8", os: "windows-2022", arch: "x64", tls: "openssl3" }, # v2.4 - { release: "2.4.5", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, - { release: "2.4.5", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, - { release: "2.4.5", os: "windows-2022", arch: "x64", tls: "schannel" }, - { release: "2.4.5", os: "windows-2022", arch: "x64", tls: "openssl" }, - { release: "2.4.5", os: "windows-2022", arch: "x64", tls: "openssl3" }, + { release: "2.4.7", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, + { release: "2.4.7", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, + { release: "2.4.7", os: "windows-2022", arch: "x64", tls: "schannel" }, + { release: "2.4.7", os: "windows-2022", arch: "x64", tls: "openssl" }, + { release: "2.4.7", os: "windows-2022", arch: "x64", tls: "openssl3" }, ] runs-on: ${{ matrix.vec.os }} name: Test steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Install Perl if: runner.os == 'Windows' - uses: shogo82148/actions-setup-perl@9c1eca9952ccc07f9ca4a2097b63df93d9d138e9 + uses: shogo82148/actions-setup-perl@98dfedee230bcf1ee68d5b021931fc8d63f2016e with: perl-version: '5.34' - name: Install NASM diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e67fa3d504..df74a07281 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,6 +43,7 @@ jobs: tls: ${{ matrix.vec.tls }} build: ${{ matrix.vec.build }} ref: ${{ inputs.ref || '' }} + repo: ${{ github.repository }} build-windows: name: Build WinUser @@ -66,6 +67,7 @@ jobs: sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} ref: ${{ inputs.ref || '' }} + repo: ${{ github.repository }} build-unix: name: Build Unix @@ -94,6 +96,7 @@ jobs: build: ${{ matrix.vec.build }} xdp: ${{ matrix.vec.xdp }} ref: ${{ inputs.ref || '' }} + repo: ${{ github.repository }} bvt: name: BVT @@ -127,7 +130,7 @@ jobs: runs-on: ${{ matrix.vec.plat == 'windows' && matrix.vec.os == 'WinServerPrerelease' && fromJson('[''self-hosted'', ''1ES.Pool=1es-msquic-pool'', ''1ES.ImageOverride=WinServerPrerelease'']') || matrix.vec.os }} steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: ref: ${{ inputs.ref || '' }} - name: Download Build Artifacts @@ -193,7 +196,7 @@ jobs: runs-on: ${{ matrix.vec.plat == 'winkernel' && matrix.vec.os == 'WinServerPrerelease' && fromJson('[''self-hosted'', ''1ES.Pool=1es-msquic-pool'', ''1ES.ImageOverride=WinServerPrerelease'']') || matrix.vec.os }} steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: ref: ${{ inputs.ref || '' }} - name: Download Build Artifacts @@ -242,7 +245,7 @@ jobs: - "1ES.ImageOverride=WinServerPrerelease" steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 ref: ${{ inputs.ref || '' }} diff --git a/.github/workflows/wan-perf.yml b/.github/workflows/wan-perf.yml index f2d311b0ba..d51e3b602b 100644 --- a/.github/workflows/wan-perf.yml +++ b/.github/workflows/wan-perf.yml @@ -1,24 +1,24 @@ name: WAN Perf on: - push: - branches: - - main - paths: - - .github/workflows/wan-perf.yml - - src/core/* - - src/platform/* - - src/perf/* - pull_request: - branches: - - main - paths: - - .github/workflows/wan-perf.yml - - src/core/* - - src/platform/* - - src/perf/* - - submodules/openssl/* - + workflow_dispatch: +# push: +# branches: +# - main +# paths: +# - .github/workflows/wan-perf.yml +# - src/core/* +# - src/platform/* +# - src/perf/* +# pull_request: +# branches: +# - main +# paths: +# - .github/workflows/wan-perf.yml +# - src/core/* +# - src/platform/* +# - src/perf/* +# - submodules/openssl/* concurrency: # Cancel any workflow currently in progress for the same PR. # Allow running concurrently with any other commits. @@ -35,7 +35,7 @@ jobs: runs-on: windows-latest steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Prepare Machine shell: pwsh run: scripts/prepare-machine.ps1 -ForBuild -DisableTest @@ -83,7 +83,7 @@ jobs: queueRatio: 5 # Exceeds QueueLimitPackets limit of 100000 steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: Prepare Machine shell: pwsh run: scripts/prepare-machine.ps1 -ForTest -InstallDuoNic @@ -92,11 +92,11 @@ jobs: name: bin path: artifacts/bin - name: Run WAN Perf (QUIC only) - if: ${{ github.event_name == 'pull_request' }} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }} shell: pwsh run: scripts/emulated-performance.ps1 -Debug -Protocol QUIC -LogProfile Performance.Light -NoDateLogDir -NumIterations ${{ env.iterations }} -DurationMs ${{ env.duration }} -Pacing ${{ env.pacing }} -BottleneckMbps ${{ matrix.rate }} -RttMs ${{ matrix.rtt }} -BottleneckQueueRatio ${{ matrix.queueRatio }} -RandomLossDenominator ${{ env.loss }} -RandomReorderDenominator ${{ env.reorder }} -ReorderDelayDeltaMs ${{ env.delay }} -BaseRandomSeed ${{ env.seed }} -CongestionControl ${{ env.congestionControl }} - name: Run WAN Perf (QUIC + TCP) - if: ${{ github.event_name != 'pull_request' }} + if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' }} shell: pwsh run: scripts/emulated-performance.ps1 -Debug -Protocol ('QUIC','TCPTLS') -LogProfile Performance.Light -NoDateLogDir -NumIterations ${{ env.iterations }} -DurationMs ${{ env.duration }} -Pacing ${{ env.pacing }} -BottleneckMbps ${{ matrix.rate }} -RttMs ${{ matrix.rtt }} -BottleneckQueueRatio ${{ matrix.queueRatio }} -RandomLossDenominator ${{ env.loss }} -RandomReorderDenominator ${{ env.reorder }} -ReorderDelayDeltaMs ${{ env.delay }} -BaseRandomSeed ${{ env.seed }} -CongestionControl ${{ env.congestionControl }} - name: Upload Results @@ -116,7 +116,7 @@ jobs: needs: wan-perf steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 diff --git a/CMakeLists.txt b/CMakeLists.txt index f113a371a8..a32b057c25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,9 +67,6 @@ elseif (UNIX) endif() message(STATUS "QUIC Platform: ${CX_PLATFORM}") -set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)") -set(SELF_DIR "$\{SELF_DIR\}") - enable_testing() # Set the default TLS method for each platform. @@ -90,6 +87,7 @@ option(QUIC_STATIC_LINK_CRT "Statically links the C runtime" ON) option(QUIC_STATIC_LINK_PARTIAL_CRT "Statically links the compiler-specific portion of the C runtime" ON) option(QUIC_UWP_BUILD "Build for UWP" OFF) option(QUIC_GAMECORE_BUILD "Build for GameCore" OFF) +option(QUIC_EXTERNAL_TOOLCHAIN "Enable if system libs and include paths are configured by CMake toolchain" OFF) option(QUIC_PGO "Enables profile guided optimizations" OFF) option(QUIC_LINUX_XDP_ENABLED "Enables XDP support" OFF) option(QUIC_SOURCE_LINK "Enables source linking on MSVC" ON) @@ -449,18 +447,30 @@ if(WIN32) # Generate the MsQuicEtw header file. file(MAKE_DIRECTORY ${QUIC_BUILD_DIR}/inc) + if(NOT DEFINED CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OR CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH) + find_program(MC_EXE NAMES mc.exe) + if(MC_EXE MATCHES "NOTFOUND") + # If not found, then VS project generator will set %PATH% env var to WinSDK bin directory + set(MC_EXE "mc.exe" CACHE STRING "mc.exe from %PATH%" FORCE) + endif() + else() + find_program(MC_EXE NAMES mc.exe REQUIRED) + endif() + add_custom_command( OUTPUT ${QUIC_BUILD_DIR}/inc/MsQuicEtw.h OUTPUT ${QUIC_BUILD_DIR}/inc/MsQuicEtw.rc DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/manifest/MsQuicEtw.man - COMMAND mc.exe -um -h ${QUIC_BUILD_DIR}/inc -r ${QUIC_BUILD_DIR}/inc ${CMAKE_CURRENT_SOURCE_DIR}/src/manifest/MsQuicEtw.man) + COMMAND "${MC_EXE}" -um -h ${QUIC_BUILD_DIR}/inc -r ${QUIC_BUILD_DIR}/inc ${CMAKE_CURRENT_SOURCE_DIR}/src/manifest/MsQuicEtw.man) add_custom_target(MsQuicEtw_HeaderBuild DEPENDS ${QUIC_BUILD_DIR}/inc/MsQuicEtw.h) set_property(TARGET MsQuicEtw_HeaderBuild PROPERTY FOLDER "${QUIC_FOLDER_PREFIX}helpers") add_library(MsQuicEtw_Header INTERFACE) - target_include_directories(MsQuicEtw_Header INTERFACE ${QUIC_BUILD_DIR}/inc) + target_include_directories(MsQuicEtw_Header INTERFACE + $ + $) add_dependencies(MsQuicEtw_Header MsQuicEtw_HeaderBuild) add_library(MsQuicEtw_Resource OBJECT ${QUIC_BUILD_DIR}/inc/MsQuicEtw.rc) @@ -478,7 +488,11 @@ if(WIN32) endif() if (QUIC_GAMECORE_BUILD) - list(APPEND QUIC_COMMON_DEFINES WINAPI_FAMILY=WINAPI_FAMILY_GAMES QUIC_GAMECORE_BUILD QUIC_RESTRICTED_BUILD) + list(APPEND QUIC_COMMON_DEFINES QUIC_GAMECORE_BUILD QUIC_RESTRICTED_BUILD) + endif() + + if (QUIC_GAMECORE_BUILD AND NOT QUIC_EXTERNAL_TOOLCHAIN) + list(APPEND QUIC_COMMON_DEFINES WINAPI_FAMILY=WINAPI_FAMILY_GAMES) set(CMAKE_CXX_STANDARD_LIBRARIES "") set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "") set(CMAKE_C_STANDARD_LIBRARIES "") diff --git a/Cargo.toml b/Cargo.toml index 9188ecd5be..892e6f969f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,12 @@ include = [ "/THIRD-PARTY-NOTICES", ] +[features] +default = [] +schannel = [] +static = [] +preview-api = [] + [build-dependencies] cmake = "0.1" @@ -52,3 +58,5 @@ bitfield = "0.17.0" libc = "0.2.0" c-types = "4.0.0" serde = { version = "1.0.117", features = ["derive"] } +ctor = "0.2.9" +socket2 = "0.5.8" diff --git a/docs/API.md b/docs/API.md index 7b270b7fcc..535898cdc4 100644 --- a/docs/API.md +++ b/docs/API.md @@ -67,15 +67,7 @@ The **PATCH** version **only changes** when a servicing fix is made to an existi ## Execution Mode -In general, MsQuic uses a callback model for all asynchronous events up to the app. This includes things like connection state changes, new streams being created, stream data being received, and stream sends completing. All these events are indicated to the app via the callback handler, on a thread owned by MsQuic. - -Generally, MsQuic creates multiple threads to parallelize work, and therefore will make parallel/overlapping upcalls to the application, but not for the same connection. All upcalls to the app for a single connection and all child streams are always delivered serially. This is not to say, though, it will always be on the same thread. MsQuic does support the ability to shuffle connections around to better balance the load. - -Apps are expected to keep any execution time in the callback **to a minimum**. MsQuic does not use separate threads for the protocol execution and upcalls to the app. Therefore, any significant delays on the callback **will delay the protocol**. Any significant time or work needed to be completed by the app must happen on its own thread. - -This doesn't mean the app isn't allowed to do any work in the callback. In fact, many things are expressly designed to be most efficient when the app does them on the callback. For instance, closing a handle to a connection or stream is ideally implemented in the "shutdown complete" indications. - -One important aspect of this design is that all blocking calls invoked on a callback always happen inline (to prevent deadlocks), and will supercede any calls in progress or queued from a separate thread. +MsQuic has a very different execution model than classic BSD-style sockets. Please see [Execution](./Execution.md) for more details on how threads and upcalls are handled inside of MsQuic. ## Settings and Configuration diff --git a/docs/Deployment.md b/docs/Deployment.md index 4d8caed2fb..c8142daa57 100644 --- a/docs/Deployment.md +++ b/docs/Deployment.md @@ -137,6 +137,11 @@ MsQuic uses worker threads internally to execute the QUIC protocol logic. For ea The queue delay threshold can be configured via the `MaxWorkerQueueDelayMs` setting. +## Version Negotiation + +MsQuic supports QUIC protocol versions 1 and 2, and the version negotiation extension. By default, version negotiation is off, but can be enabled at runtime. +Instructions for configuring and deploying QUIC versions are at [Versions](Versions.md). + # Diagnostics For details on how to diagnose any issues with your deployment at the MsQuic layer see [Diagnostics](Diagnostics.md). diff --git a/docs/Execution.md b/docs/Execution.md new file mode 100644 index 0000000000..7d7259e7a1 --- /dev/null +++ b/docs/Execution.md @@ -0,0 +1,111 @@ +Execution +====== + +The MsQuic API uses a different execution model compared to BSD-style sockets and most other networking libraries built on them. +The sections below detail the designs MsQuic uses and the reasons behind these choices. + +## Event Model + +In the MsQuic API, all asynchronous state changes and notifications are indicated directly to the application via a callback. +This includes connection state changes, new streams being created, stream data being received, and stream sends completing. + +```c +typedef struct QUIC_LISTENER_EVENT { + QUIC_LISTENER_EVENT_TYPE Type; + union { + struct { ... } NEW_CONNECTION; + struct { ... } STOP_COMPLETE; + }; +} QUIC_LISTENER_EVENT; + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Function_class_(QUIC_LISTENER_CALLBACK) +QUIC_STATUS +(QUIC_API QUIC_LISTENER_CALLBACK)( + _In_ HQUIC Listener, + _In_opt_ void* Context, + _Inout_ QUIC_LISTENER_EVENT* Event + ); +``` + +Above is an example of a callback delivered to the listener interface. +The application must register a per-object callback handler to manage all events MsQuic may indicate for that object, returning a status to show if it was successfully handled or not. + +This approach differs significantly from sockets and most networking libraries, where the application must make a call (e.g., `send` or `recv`) to determine if something happened. +This design choice was made for several reasons: + +- The MsQuic API **runs in-process**, eliminating the need for a kernel to user mode boundary switch to notify the application layer. This makes the callback-based design more practical compared to sockets. + +- MsQuic, due to the QUIC protocol, has numerous event types. Applications may have hundreds of objects with potential state changes. The callback model allows the application to avoid managing pending calls on each object. + +- Writing correct, scalable code on top of the socket interfaces has proven challenging. By offloading the threading to MsQuic it enables MsQuic to abstract much complexity from applications, making things "just work" out of the box. + +- It simplifies MsQuic's logic by eliminating the need for a queue or cached state to indicate to the application. In the socket model, the networking stack must wait for a top-down call from the application before indicating completion, increasing code size, complexity, and memory usage. + +### Writing Event Handlers + +Event handlers are **essential** for all objects that support them, as much of the MsQuic API operates through these callbacks. +Critical events, such as "shutdown complete" notifications, provide vital information necessary for the application to function correctly. +Without these events, the application cannot determine when it is safe to clean up objects. + +Applications should keep the execution time within callbacks **to a minimum**. +MsQuic does not use separate threads for protocol execution and upcalls to the application. +Therefore, any significant delays in the callback **will delay the protocol**. +Any substantial work required by the application should be performed on its own thread. + +This does not mean the application cannot perform any work in the callback handler. +In fact, many operations are designed to be most efficient when executed within the callback. +For example, closing a handle to a connection or stream is ideally done during the "shutdown complete" indication. + +A crucial aspect of this design is that all blocking API (down) calls invoked within a callback always occur inline (to prevent deadlocks) and will take precedence over any calls in progress or queued from a separate thread. +It's also worth noting that MsQuic will not invoke a recursive callback to the application by default in these cases. +The one exception to this rule is if the application opts in via the `QUIC_STREAM_SHUTDOWN_FLAG_INLINE` flag when calling `StreamShudown` on a callback. + +## Threading + +By default, MsQuic creates its own threads to manage the execution of its logic. +The number and configuration of these threads depend on the settings passed to [RegistrationOpen](api/RegistrationOpen.md) or `QUIC_PARAM_GLOBAL_EXECUTION_CONFIG`. + +Typically, MsQuic creates dedicated threads for each processor, which are hard-affinitized to a specific NUMA node and soft-affinitized (set as 'ideal processor') to a specific processor. +These threads handle both the datapath (i.e., UDP) and QUIC layers. +By default both layers are handled by a single thread (per-processor), but QUIC may be configured to run these layers on separate threads. +By using the same thread MsQuic can achieve lower latency, but by using separate threads it can achieve higher throughput. +MsQuic aligns its processing logic with the rest of the networking stack (including hardware RSS) to ensure that all processing stays on the same NUMA node, and ideally, the same processor. + +The complexity of aligning processing across various threads and processors is why MsQuic manages its own threading by default. +This abstraction simplifies the development process for applications built on top of MsQuic, ensuring that things "just work" for QUIC out of the box. + +Each thread manages the execution of one or more connections. +Connections are distributed across threads based on their RSS alignment, which should evenly distribute traffic based on different UDP tuples. +Each connection and its derived state (i.e., streams) are managed and executed by a single thread at a time, but may move across threads to align with any RSS changes. +This ensures that each connection and its streams are effectively single-threaded, including all upcalls to the application layer. +MsQuic will **never** make upcalls for a single connection or any of its streams in parallel. + +For listeners, the application callback will be called in parallel for new connections, allowing server applications to scale efficiently with the number of processors. + +```mermaid +graph TD + subgraph Kernel + NIC-Queue1[NIC Queue] + NIC-Queue2[NIC Queue] + NIC-Queue1 -->|RSS Receive| UDP1[IP/UDP] + NIC-Queue2 -->|RSS Receive| UDP2[IP/UDP] + end + subgraph MsQuic Process + UDP1 -.-> Processor1 + UDP2 -.-> Processor2 + subgraph Processor1[Processor 0] + Thread1[Thread] + Thread1 -->|Manages| Connection1[Connection 1] + Thread1 -->|Manages| Connection2[Connection 2] + Connection1 -->|Delivers Event| ApplicationCallback1[App Callback] + Connection2 -->|Delivers Event| ApplicationCallback2[App Callback] + end + subgraph Processor2[Processor 1] + Thread2[Thread] + Thread2 -->|Manages| Connection3[Connection 3] + Connection3 -->|Delivers Event| ApplicationCallback3[App Callback] + end + end +``` diff --git a/docs/Release.md b/docs/Release.md index 405fdec08c..b43e1c97c2 100644 --- a/docs/Release.md +++ b/docs/Release.md @@ -106,15 +106,17 @@ When testing the pipeline, please make sure to comment out the PMC cli commands Prerequisites: - Docker +- Powershell +1. Checkout to release tag. (e.g. `git checkout v2.4.7`) 1. Run `generate-alpine-packaging-file.ps1` script on host computer to create `APKBUILD` file for the release. (This script can run on any Linux distro, and this script will create a docker alpine container to calculate hash keys in APKBUILD file) 1. If you don't have account for [AlpineLinux GitLab](https://gitlab.alpinelinux.org). Create an account and [configure your SSH](https://docs.gitlab.com/ee/user/ssh.html). 1. If you didn't fork `aports` repository yet, Fork `https://gitlab.alpinelinux.org/alpine/aports`. 1. Clone `https://gitlab.alpinelinux.org//aports` repository. -1. Navigate to `aports/testing/libmsquic` folder. +1. Navigate to `aports/community/libmsquic` folder. 1. Replace the `APKBUILD` file with newly created `APKBUILD` file. -1. Create a commit using `testing/libmsquic: upgrade to ` (version_number e.g. 2.5.0 or 2.4.4). -1. Create a merge request using `testing/libmsquic: upgrade to ` (version_number e.g. 2.5.0 or 2.4.4). +1. Create a commit using `community/libmsquic: upgrade to ` (version_number e.g. 2.5.0 or 2.4.4). +1. Create a merge request using `community/libmsquic: upgrade to ` (version_number e.g. 2.5.0 or 2.4.4). 1. Owners of the `aports` repository will respond to the PR or merge it in couple of days/hours. For future reference: [Official documentation](https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package) diff --git a/docs/Settings.md b/docs/Settings.md index f6fc75038a..9df659b894 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -74,11 +74,12 @@ While `REG_DWORD` can hold values larger than `uint16_t`, the administrator shou The following settings are available via registry as well as via [QUIC_VERSION_SETTINGS](./Versions.md): -| Setting | Type | Registry Name | Default | Description | -|-----------------------------------|------------|--------------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------------| -| Acceptable Versions List | uint32_t[] | AcceptableVersions | Unset | Sets the list of versions that a given server instance will use if a client sends a first flight using them. | -| Offered Versions List | uint32_t[] | OfferedVersions | Unset | Sets the list of versions that a given server instance will send in a Version Negotiation packet if it receives a first flight from an unknown version. This list will most often be equal to the Acceptable Versions list. | -| Fully-Deployed Versions List | uint32_t[] | FullyDeployedVersions | Unset | Sets the list of QUIC versions that is supported and negotiated by every single QUIC server instance in this deployment. Used to generate the AvailableVersions list in the Version Negotiation Extension Transport Parameter. | +| Setting | Type | Registry Name | Default | Description | +|-----------------------------------|------------|------------------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------------| +| Acceptable Versions List | uint32_t[] | AcceptableVersions | Unset | Sets the list of versions that a given server instance will use if a client sends a first flight using them. | +| Offered Versions List | uint32_t[] | OfferedVersions | Unset | Sets the list of versions that a given server instance will send in a Version Negotiation packet if it receives a first flight from an unknown version. This list will most often be equal to the Acceptable Versions list. | +| Fully-Deployed Versions List | uint32_t[] | FullyDeployedVersions | Unset | Sets the list of QUIC versions that is supported and negotiated by every single QUIC server instance in this deployment. Used to generate the AvailableVersions list in the Version Negotiation Extension Transport Parameter. | +| Version Negotiation Ext. Enabled | uint32_t | VersionNegotiationExtEnabled | 0 (FALSE) | Enables the Version Negotiation Extension. | The `uint32_t[]` type is a `REG_BINARY` blob of the versions list, with each version in little-endian format. @@ -114,6 +115,7 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa | `QUIC_PARAM_GLOBAL_EXECUTION_CONFIG`
9 | QUIC_EXECUTION_CONFIG | Both | Globally configure the execution model used for QUIC. Must be set before opening registration. | | `QUIC_PARAM_GLOBAL_TLS_PROVIDER`
10 | QUIC_TLS_PROVIDER | Get-Only | The TLS provider being used by MsQuic for the TLS handshake. | | `QUIC_PARAM_GLOBAL_STATELESS_RESET_KEY`
11 | uint8_t[] | Set-Only | Globally change the stateless reset key for all subsequent connections. | +| `QUIC_PARAM_GLOBAL_VERSION_NEGOTIATION_ENABLED`
(preview) | uint8_t (BOOLEAN) | Both | Globally enable the version negotiation extension for all client and server connections. | ## Registration Parameters @@ -132,6 +134,7 @@ These parameters are accessed by calling [GetParam](./api/GetParam.md) or [SetPa | `QUIC_PARAM_CONFIGURATION_TICKET_KEYS`
1 | QUIC_TICKET_KEY_CONFIG[] | Set-only | Resumption ticket encryption keys. Server-side only. | | `QUIC_PARAM_CONFIGURATION_VERSION_SETTINGS`
2 | QUIC_VERSIONS_SETTINGS | Both | Change version settings for all connections on the configuration. | | `QUIC_PARAM_CONFIGURATION_SCHANNEL_CREDENTIAL_ATTRIBUTE_W`
3 | QUIC_SCHANNEL_CREDENTIAL_ATTRIBUTE_W | Set-only | Calls `SetCredentialsAttributesW` with the supplied attribute and buffer on the credential handle. Schannel-only. Only valid once the credential has been loaded. | +| `QUIC_PARAM_CONFIGURATION_VERSION_NEG_ENABLED`
(preview) | uint8_t (BOOLEAN) | Both | Enables the version negotiation extension for all client connections on the configuration. | ## Listener Parameters diff --git a/docs/Versions.md b/docs/Versions.md index b7f694f2a0..eb67cea9e3 100644 --- a/docs/Versions.md +++ b/docs/Versions.md @@ -57,7 +57,7 @@ Configuring the QUIC versions on a MsQuic server is similar to configuring them If a server is not in a fleet, or the operator/application does not ever need to change QUIC versions, then all three lists in `QUIC_VERSION_SETTINGS` **MUST** be the same. -If a server is deployed in a fleet, and the server operator wishes to change the supported QUIC versions, the Version Negotiation specification details how that should be done, quoted here: +If a server is deployed in a fleet, and the server operator wishes to change the supported QUIC versions, the [Version Negotiation specification](https://www.rfc-editor.org/rfc/rfc9368.html#section-5) details how that should be done, quoted here: > When adding support for a new version: > * The first step is to progressively add support for the new version to all server instances. This step updates the Acceptable Versions but not the Offered Versions nor the Fully-Deployed Versions. Once all server instances have been updated, operators wait for at least one MSL to allow any in-flight Version Negotiation packets to arrive. > * Then, the second step is to progressively add the new version to Offered Versions on all server instances. Once complete, operators wait for at least another MSL. @@ -70,7 +70,7 @@ If a server is deployed in a fleet, and the server operator wishes to change the **Note that this opens connections to version downgrades (but only for partially-deployed versions) during the update window, since those could be due to clients communicating with both updated and non-updated server instances.** - +### Configuring Versions via code This snippet should execute before the server's `QUIC_CONFIGURATION` is created: ```c QUIC_VERSION_SETTINGS Settings = { 0 }; @@ -92,9 +92,30 @@ MsQuic->SetParam( &Settings); ``` +### Configuring Versions via Windows Registry +MsQuic supports setting the Acceptable Versions, Offered Versions, and Fully-Deployed Versions lists via the Windows registry. These settings are global for all servers and clients on the machine. +The registry settings are overridden by settings specified in the code. +The registry values must be created under the `HKLM\System\CurrentControlSet\Services\MsQuic\Parameters` key. +Each list is stored in the registry as a `REG_BINARY` type, with the version numbers in little-endian (host) order. +The registry value for Acceptable Versions must be named `AcceptableVersions`. +The registry value for Offered Versions must be named `OfferedVersions`. +The registry value for Fully-Deployed Versions must be named `FullyDeployedVersions`. + +Here's a sample .reg file that creates all three lists with QUIC version 2 first and QUIC version 1 after, in little endian order, and enables version negotiation: +```reg +Windows Registry Editor Version 5.00 + +[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MsQuic\Parameters] +"AcceptableVersions"=hex:cf,43,33,6b,01,00,00,00 +"OfferedVersions"=hex:cf,43,33,6b,01,00,00,00 +"FullyDeployedVersions"=hex:cf,43,33,6b,01,00,00,00 +"VersionNegotiationExtEnabled"=dword:00000001 +``` + # QUIC Version Negotiation Extension -The Version Negotiation Extension is on by default in our officially-released binaries. Since the standard is not yet complete, incompatible changes may be made preventing different drafts from working with each other. An application using MsQuic should be cautious about enabling the Version Negotiation Extension in production scenarios until the standard is complete. +The Version Negotiation Extension is off by default in our officially-released binaries, but can be enabled via registry or [Settings](./Settings.md). +The Version Negotiated Extension has been standardized and is present in MsQuic since version 2.3. ## Enabling Version Negotiation Extension on MsQuic Client @@ -103,4 +124,4 @@ This setting **MUST** be set before [`ConnectionStart`](api/ConnectionStart.md) ## Enabling Version Negotiation Extension on MsQuic Server -Enabling the Version Negotiation Extension on server follows the same restrictions as setting the QUIC version on server, i.e. it **MUST** be set globally, using [`SetParam`](api/SetParam.md) before the `QUIC_CONFIGURATION` is opened for the server. It is set automatically when `QUIC_VERSION_SETTINGS` are set. +Enabling the Version Negotiation Extension on server follows the same restrictions as setting the QUIC version on server, i.e. it **MUST** be set globally, using [`SetParam`](api/SetParam.md) before the `QUIC_CONFIGURATION` is opened for the server. It is set automatically when `QUIC_VERSION_SETTINGS` are set, except via registry. diff --git a/docs/api/QUIC_CREDENTIAL_CONFIG.md b/docs/api/QUIC_CREDENTIAL_CONFIG.md index b92dfceeba..0a8ed546fc 100644 --- a/docs/api/QUIC_CREDENTIAL_CONFIG.md +++ b/docs/api/QUIC_CREDENTIAL_CONFIG.md @@ -161,6 +161,10 @@ Obtain the peer certificate using a faster in-process API call. Only available o Enable CA certificate file provided in the `CaCertificateFile` member. +`QUIC_CREDENTIAL_FLAG_DISABLE_AIA` + +The following flag can be set to explicitly disable AIA retrievals. Only valid on Windows. + #### `CertificateHash` Must **only** use with `QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH` type. diff --git a/scripts/DotNetTest.ps1 b/scripts/DotNetTest.ps1 index 46f132503a..8580e2ae22 100644 --- a/scripts/DotNetTest.ps1 +++ b/scripts/DotNetTest.ps1 @@ -19,7 +19,10 @@ param ( [string]$Tls = "", [Parameter(Mandatory = $false)] - [string]$ExtraArtifactDir = "" + [string]$ExtraArtifactDir = "", + + [Parameter(Mandatory = $true)] + [string]$DomainName = "" ) Set-StrictMode -Version 'Latest' @@ -43,4 +46,4 @@ if ($IsWindows) { $RootDir = Split-Path $PSScriptRoot -Parent dotnet build (Join-Path $RootDir src cs) -dotnet run --project (Join-Path $RootDir src cs tool) -- (Join-Path $RootArtifactDir $LibName) +dotnet run --project (Join-Path $RootDir src cs tool) -- $DomainName (Join-Path $RootArtifactDir $LibName) diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 7b676862bb..1869011ce5 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -386,8 +386,8 @@ function CMake-Generate { } if ($Platform -eq "macos") { switch ($Arch) { - "x64" { $Arguments += " -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=""12"""} - "arm64" { $Arguments += " -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=""11.0"""} + "x64" { $Arguments += " -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_DEPLOYMENT_TARGET=""13"""} + "arm64" { $Arguments += " -DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=""13"""} } } if ($Platform -eq "linux") { diff --git a/scripts/build.rs b/scripts/build.rs index 355d263d65..7dbb6d3ec0 100644 --- a/scripts/build.rs +++ b/scripts/build.rs @@ -2,8 +2,8 @@ // Licensed under the MIT License. use cmake::Config; -use std::path::Path; use std::env; +use std::path::Path; fn main() { let path_extra = "lib"; @@ -13,25 +13,54 @@ fn main() { } let target = env::var("TARGET").unwrap(); + let out_dir = env::var("OUT_DIR").unwrap(); + // The output directory for the native MsQuic library. + let quic_output_dir = Path::new(&out_dir).join("lib"); // Builds the native MsQuic and installs it into $OUT_DIR. let mut config = Config::new("."); config .define("QUIC_ENABLE_LOGGING", logging_enabled) - .define("QUIC_TLS", "openssl") - .define("QUIC_OUTPUT_DIR", "../lib"); + .define("QUIC_OUTPUT_DIR", quic_output_dir.to_str().unwrap()); + if cfg!(feature = "schannel") { + config.define("QUIC_TLS", "schannel"); + } else { + config.define("QUIC_TLS", "openssl"); + } + if cfg!(feature = "static") { + config.define("QUIC_BUILD_SHARED", "off"); + } + // macos-latest's cargo automatically specify --target=${ARCH}-apple-macosx14.5 + // which conflicts with -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}. + // Different value than 14.5 will cause the build to fail. + // This hardcoded 14.5 is workaround for this issue. match target.as_str() { "x86_64-apple-darwin" => config .define("CMAKE_OSX_ARCHITECTURES", "x86_64") - .define("CMAKE_OSX_DEPLOYMENT_TARGET", "10.15"), + .define("CMAKE_OSX_DEPLOYMENT_TARGET", "14.5"), "aarch64-apple-darwin" => config .define("CMAKE_OSX_ARCHITECTURES", "arm64") - .define("CMAKE_OSX_DEPLOYMENT_TARGET", "11.0"), - _ => &mut config + .define("CMAKE_OSX_DEPLOYMENT_TARGET", "14.5"), + _ => &mut config, }; let dst = config.build(); let lib_path = Path::join(Path::new(&dst), Path::new(path_extra)); println!("cargo:rustc-link-search=native={}", lib_path.display()); + if cfg!(feature = "static") { + if cfg!(target_os = "linux") { + let numa_lib_path = match target.as_str() { + "x86_64-unknown-linux-gnu" => "/usr/lib/x86_64-linux-gnu", + "aarch64-unknown-linux-gnu" => "/usr/lib/aarch64-linux-gnu", + _ => panic!("Unsupported target: {}", target), + }; + println!("cargo:rustc-link-search=native={}", numa_lib_path); + println!("cargo:rustc-link-lib=static:+whole-archive=numa"); + } else if cfg!(target_os = "macos") { + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=Security"); + } + println!("cargo:rustc-link-lib=static=msquic"); + } } diff --git a/scripts/secnetperf-helpers.psm1 b/scripts/secnetperf-helpers.psm1 index 0ecd7c94b0..0a768477ee 100644 --- a/scripts/secnetperf-helpers.psm1 +++ b/scripts/secnetperf-helpers.psm1 @@ -524,7 +524,7 @@ function Get-LatencyOutput { # Invokes secnetperf with the given arguments for both TCP and QUIC. function Invoke-Secnetperf { - param ($Session, $RemoteName, $RemoteDir, $UserName, $SecNetPerfPath, $LogProfile, $TestId, $ExeArgs, $io, $Filter, $Environment, $RunId, $SyncerSecret) + param ($Session, $RemoteName, $RemoteDir, $UserName, $SecNetPerfPath, $LogProfile, $Scenario, $io, $Filter, $Environment, $RunId, $SyncerSecret) $values = @(@(), @()) $latency = $null @@ -536,26 +536,25 @@ function Invoke-Secnetperf { $tcpSupported = 0 } $metric = "throughput" - if ($exeArgs.Contains("conns:16cpu")) { # TODO: figure out a better way to detect max RPS tests + if ($Scenario.Contains("rps")) { $metric = "rps" - } elseif ($exeArgs.Contains("plat:1")) { + } elseif ($Scenario.Contains("latency")) { $metric = "latency" $latency = @(@(), @()) $extraOutput = Repo-Path "latency.txt" if (!$isWindows) { chmod +rw "$extraOutput" } - } elseif ($exeArgs.Contains("prate:1")) { + } elseif ($Scenario.Contains("hps")) { $metric = "hps" } for ($tcp = 0; $tcp -le $tcpSupported; $tcp++) { # Set up all the parameters and paths for running the test. - $execMode = $ExeArgs.Substring(0, $ExeArgs.IndexOf(" ")) # First arg is the exec mode $clientPath = Repo-Path $SecNetPerfPath - $serverArgs = "$execMode -io:$io" - $clientArgs = "-target:$RemoteName $ExeArgs -tcp:$tcp -trimout -watchdog:25000" + $serverArgs = "-scenario:$Scenario -io:$io" + $clientArgs = "-target:$RemoteName -scenario:$Scenario -io:$io -tcp:$tcp -trimout -watchdog:25000" if ($io -eq "xdp" -or $io -eq "qtip") { $serverArgs += " -pollidle:10000" $clientArgs += " -pollidle:10000" @@ -584,9 +583,9 @@ function Invoke-Secnetperf { $useSudo = (!$isWindows -and $io -eq "xdp") if ($tcp -eq 0) { - $artifactName = "$TestId-quic" + $artifactName = "$Scenario-quic" } else { - $artifactName = "$TestId-tcp" + $artifactName = "$Scenario-tcp" } New-Item -ItemType Directory "artifacts/logs/$artifactName" -ErrorAction Ignore | Out-Null $artifactDir = Repo-Path "artifacts/logs/$artifactName" @@ -715,14 +714,14 @@ function Invoke-Secnetperf { } } -function CheckRegressionResult($values, $testid, $transport, $regressionJson, $envStr) { +function CheckRegressionResult($values, $scenario, $transport, $regressionJson, $envStr) { $sum = 0 foreach ($item in $values) { $sum += $item } $avg = $sum / $values.Length - $Testid = "$testid-$transport" + $Scenario = "$scenario-$transport" $res = @{ Baseline = "N/A" @@ -734,14 +733,14 @@ function CheckRegressionResult($values, $testid, $transport, $regressionJson, $e } try { - $res.Baseline = $regressionJson.$Testid.$envStr.baseline - $res.BestResult = $regressionJson.$Testid.$envStr.BestResult - $res.BestResultCommit = $regressionJson.$Testid.$envStr.BestResultCommit + $res.Baseline = $regressionJson.$Scenario.$envStr.baseline + $res.BestResult = $regressionJson.$Scenario.$envStr.BestResult + $res.BestResultCommit = $regressionJson.$Scenario.$envStr.BestResultCommit $res.CumulativeResult = $avg $res.AggregateFunction = "AVG" if ($avg -lt $res.Baseline) { - Write-GHError "Regression detected in $Testid for $envStr. See summary table for details." + Write-GHError "Regression detected in $Scenario for $envStr. See summary table for details." $res.HasRegression = $true } } catch { @@ -751,7 +750,7 @@ function CheckRegressionResult($values, $testid, $transport, $regressionJson, $e return $res } -function CheckRegressionLat($values, $regressionJson, $testid, $transport, $envStr) { +function CheckRegressionLat($values, $regressionJson, $scenario, $transport, $envStr) { # TODO: Right now, we are not using a watermark based method for regression detection of latency percentile values because we don't know how to determine a "Best Ever" distribution. # (we are just looking at P0, P50, P99 columns, and computing the baseline for each percentile as the mean - 2 * std of the last 20 runs. ) @@ -764,7 +763,7 @@ function CheckRegressionLat($values, $regressionJson, $testid, $transport, $envS } $RpsAvg /= $NumRuns - $Testid = "$testid-$transport" + $Scenario = "$scenario-$transport" $res = @{ Baseline = "N/A" @@ -776,14 +775,14 @@ function CheckRegressionLat($values, $regressionJson, $testid, $transport, $envS } try { - $res.Baseline = $regressionJson.$Testid.$envStr.baseline - $res.BestResult = $regressionJson.$Testid.$envStr.BestResult - $res.BestResultCommit = $regressionJson.$Testid.$envStr.BestResultCommit + $res.Baseline = $regressionJson.$Scenario.$envStr.baseline + $res.BestResult = $regressionJson.$Scenario.$envStr.BestResult + $res.BestResultCommit = $regressionJson.$Scenario.$envStr.BestResultCommit $res.CumulativeResult = $RpsAvg $res.AggregateFunction = "AVG" if ($RpsAvg -lt $res.Baseline) { - Write-GHError "RPS Regression detected in $Testid for $envStr. See summary table for details." + Write-GHError "RPS Regression detected in $Scenario for $envStr. See summary table for details." $res.HasRegression = $true } } catch { diff --git a/scripts/secnetperf.ps1 b/scripts/secnetperf.ps1 index 332e2fe3c4..5abff3b504 100644 --- a/scripts/secnetperf.ps1 +++ b/scripts/secnetperf.ps1 @@ -251,17 +251,11 @@ if ($isWindows) { $kernelVersion = bash -c "uname -r" $json["os_version"] = "$osName $kernelVersion" } -$allTests = [System.Collections.Specialized.OrderedDictionary]::new() -# > All tests: -$allTests["tput-up"] = "-exec:maxtput -up:12s -ptput:1" -$allTests["tput-down"] = "-exec:maxtput -down:12s -ptput:1" -$allTests["hps-conns-100"] = "-exec:maxtput -rconn:1 -share:1 -conns:100 -run:12s -prate:1" -$allTests["rps-up-512-down-4000"] = "-exec:lowlat -rstream:1 -up:512 -down:4000 -run:20s -plat:1" -$allTests["max-rps-up-512-down-4000"] = "-exec:lowlat -conns:16cpu -streams:10 -rstream:1 -up:512 -down:4000 -run:20s -plat:1" +# Test all supported scenarios. +$allScenarios = @("upload", "download", "hps", "rps", "rps-multi", "latency") $hasFailures = $false -$json["run_args"] = $allTests try { @@ -336,9 +330,8 @@ $regressionJson = Get-Content -Raw -Path "watermark_regression.json" | ConvertFr # Run all the test cases. Write-Host "Setup complete! Running all tests" -foreach ($testId in $allTests.Keys) { - $ExeArgs = $allTests[$testId] + " -io:$io" - $Output = Invoke-Secnetperf $Session $RemoteName $RemoteDir $UserName $SecNetPerfPath $LogProfile $testId $ExeArgs $io $filter $environment $RunId $SyncerSecret +foreach ($scenario in $allScenarios) { + $Output = Invoke-Secnetperf $Session $RemoteName $RemoteDir $UserName $SecNetPerfPath $LogProfile $scenario $io $filter $environment $RunId $SyncerSecret $Test = $Output[-1] if ($Test.HasFailures) { $hasFailures = $true } @@ -349,15 +342,15 @@ foreach ($testId in $allTests.Keys) { } else { $transport = "quic" } - $json["$testId-$transport"] = $Test.Values[$tcp] + $json["$scenario-$transport"] = $Test.Values[$tcp] if ($Test.Metric -eq "latency") { - $json["$testId-$transport-lat"] = $Test.Latency[$tcp] - $LatencyRegression = CheckRegressionLat $Test.Values[$tcp] $regressionJson $testId $transport "$os-$arch-$environment-$io-$tls" - $json["$testId-$transport-regression"] = $LatencyRegression + $json["$scenario-$transport-lat"] = $Test.Latency[$tcp] + $LatencyRegression = CheckRegressionLat $Test.Values[$tcp] $regressionJson $scenario $transport "$os-$arch-$environment-$io-$tls" + $json["$scenario-$transport-regression"] = $LatencyRegression } else { - $ResultRegression = CheckRegressionResult $Test.Values[$tcp] $testId $transport $regressionJson "$os-$arch-$environment-$io-$tls" - $json["$testId-$transport-regression"] = $ResultRegression + $ResultRegression = CheckRegressionResult $Test.Values[$tcp] $scenario $transport $regressionJson "$os-$arch-$environment-$io-$tls" + $json["$scenario-$transport-regression"] = $ResultRegression } } } diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt index 16cef4e31e..e69cf51ac5 100644 --- a/src/bin/CMakeLists.txt +++ b/src/bin/CMakeLists.txt @@ -11,7 +11,8 @@ endif() if(BUILD_SHARED_LIBS) add_library(msquic SHARED ${SOURCES}) - target_link_libraries(msquic PRIVATE core platform inc warnings logging base_link main_binary_link_args) + target_include_directories(msquic PUBLIC $) + target_link_libraries(msquic PRIVATE core msquic_platform inc warnings logging base_link main_binary_link_args) set_target_properties(msquic PROPERTIES OUTPUT_NAME ${QUIC_LIBRARY_NAME}) if (NOT WIN32) set_target_properties(msquic PROPERTIES SOVERSION ${QUIC_MAJOR_VERSION} VERSION ${QUIC_FULL_VERSION}) @@ -21,7 +22,7 @@ if(BUILD_SHARED_LIBS) endif() else() add_library(msquic_static STATIC static/empty.c) - target_link_libraries(msquic_static PRIVATE core platform inc logging main_binary_link_args) + target_link_libraries(msquic_static PRIVATE core msquic_platform inc logging main_binary_link_args) target_compile_definitions(msquic_static PUBLIC QUIC_BUILD_STATIC) set_property(TARGET msquic_static PROPERTY FOLDER "${QUIC_FOLDER_PREFIX}libraries") @@ -150,7 +151,7 @@ else() COMMAND libtool -static -o \"${QUIC_STATIC_LIBRARY}\" @${QUIC_DEPS_FILE} DEPENDS ${QUIC_DEPS_FILE} DEPENDS core - DEPENDS platform + DEPENDS msquic_platform DEPENDS msquic_static ) elseif(WIN32) @@ -181,7 +182,7 @@ else() COMMAND ${CMAKE_COMMAND} -E copy "${QUIC_STATIC_LIBRARY}" "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}" DEPENDS ${QUIC_DEPS_FILE} DEPENDS core - DEPENDS platform + DEPENDS msquic_platform DEPENDS msquic_static ) else() @@ -205,7 +206,7 @@ else() COMMAND ${CMAKE_AR} -M < ${QUIC_DEPS_FILE} DEPENDS ${QUIC_DEPS_FILE} DEPENDS core - DEPENDS platform + DEPENDS msquic_platform DEPENDS msquic_static ) endif() @@ -254,19 +255,27 @@ include(GNUInstallDirs) file(GLOB PUBLIC_HEADERS "../inc/*.h" "../inc/*.hpp") +if(QUIC_TLS STREQUAL "openssl" OR QUIC_TLS STREQUAL "openssl3") + list(APPEND OTHER_TARGETS OpenSSL OpenSSLQuic) +endif() + +if(WIN32) + list(APPEND OTHER_TARGETS MsQuicEtw_Header) +endif() + if(BUILD_SHARED_LIBS) - install(TARGETS msquic EXPORT msquic DESTINATION lib) + install(TARGETS msquic msquic_platform inc logging_inc warnings main_binary_link_args ${OTHER_TARGETS} EXPORT msquic DESTINATION lib) else() install(FILES ${QUIC_STATIC_LIBRARY} DESTINATION lib) endif() install(FILES ${PUBLIC_HEADERS} DESTINATION include) -configure_file(msquic-config.cmake.in ${CMAKE_BINARY_DIR}/msquic-config.cmake) +configure_file(msquic-config.cmake.in ${CMAKE_BINARY_DIR}/msquic-config.cmake @ONLY) install(FILES ${CMAKE_BINARY_DIR}/msquic-config.cmake DESTINATION share/msquic) if(BUILD_SHARED_LIBS) - install(EXPORT msquic DESTINATION share/msquic) + install(EXPORT msquic NAMESPACE msquic:: DESTINATION share/msquic) endif() if (MSVC AND NOT QUIC_ENABLE_SANITIZERS AND BUILD_SHARED_LIBS) diff --git a/src/bin/msquic-config-unix.cmake.in b/src/bin/msquic-config-unix.cmake.in deleted file mode 100644 index 9292018b12..0000000000 --- a/src/bin/msquic-config-unix.cmake.in +++ /dev/null @@ -1,5 +0,0 @@ -include(CMakeFindDependencyMacro) -@FILENAME_DEP_REPLACE@ - -include(${SELF_DIR}/msquic.cmake) -include(${SELF_DIR}/msquictraceprovider.cmake) diff --git a/src/bin/msquic-config.cmake.in b/src/bin/msquic-config.cmake.in index dd676243fe..e61e63feab 100644 --- a/src/bin/msquic-config.cmake.in +++ b/src/bin/msquic-config.cmake.in @@ -1,4 +1,9 @@ include(CMakeFindDependencyMacro) -@FILENAME_DEP_REPLACE@ -include(${SELF_DIR}/msquic.cmake) +include("${CMAKE_CURRENT_LIST_DIR}/msquic.cmake") + +foreach(_t IN ITEMS msquic msquic_platform) + if(TARGET msquic::${_t} AND NOT TARGET ${_t}) + add_library(${_t} ALIAS msquic::${_t}) + endif() +endforeach() diff --git a/src/core/ack_tracker.c b/src/core/ack_tracker.c index fe70fa57df..5ba46d3810 100644 --- a/src/core/ack_tracker.c +++ b/src/core/ack_tracker.c @@ -96,6 +96,73 @@ QuicAckTrackerAddPacketNumber( !RangeUpdated; } +// +// This function implements the logic defined in Section 6.2 [draft-ietf-quic-frequence-10] +// to determine if the reordering threshold has been hit. +// +BOOLEAN +QuicAckTrackerDidHitReorderingThreshold( + _In_ QUIC_ACK_TRACKER* Tracker, + _In_ uint8_t ReorderingThreshold + ) +{ + if (ReorderingThreshold == 0 || QuicRangeSize(&Tracker->PacketNumbersToAck) < 2) { + return FALSE; + } + + const uint64_t LargestUnacked = QuicRangeGetMax(&Tracker->PacketNumbersToAck); + const uint64_t SmallestTracked = QuicRangeGet(&Tracker->PacketNumbersToAck, 0)->Low; + + // + // Largest Reported is equal to the largest packet number acknowledged minus the + // Reordering Threshold. If the difference between the largest packet number + // acknowledged and the Reordering Threshold is smaller than the smallest packet + // in the ack tracker, then the largest reported is the smallest packet in the ack + // tracker. + // + + const uint64_t LargestReported = + (Tracker->LargestPacketNumberAcknowledged >= SmallestTracked + ReorderingThreshold) ? + Tracker->LargestPacketNumberAcknowledged - ReorderingThreshold + 1 : + SmallestTracked; + + // + // Loop through all previous ACK ranges (before last) to find the smallest missing + // packet number that is after the largest reported packet number. If the difference + // between that missing number and the largest unack'ed number is more than the + // reordering threshold, then the condition has been met to send an immediate + // acknowledgement. + // + + for (uint32_t Index = QuicRangeSize(&Tracker->PacketNumbersToAck) - 1; Index > 0; --Index) { + const uint64_t RangeStart = QuicRangeGet(&Tracker->PacketNumbersToAck, Index)->Low; + + if (LargestReported >= RangeStart) { + // + // Since we are only looking for packets more than LargestReported, we return + // false here. + // + return FALSE; + } + + // + // Check if largest reported packet is missing. In that case, the smallest missing + // packet becomes the largest reported packet. + // + + uint64_t PreviousSmallestMissing = QuicRangeGetHigh(QuicRangeGet(&Tracker->PacketNumbersToAck, Index - 1)) + 1; + if (LargestReported > PreviousSmallestMissing) { + PreviousSmallestMissing = LargestReported; + } + + if (LargestUnacked - PreviousSmallestMissing >= ReorderingThreshold) { + return TRUE; + } + } + + return FALSE; +} + _IRQL_requires_max_(DISPATCH_LEVEL) void QuicAckTrackerAckPacket( @@ -182,10 +249,10 @@ QuicAckTrackerAckPacket( // 1. The packet included an IMMEDIATE_ACK frame. // 2. ACK delay is disabled (MaxAckDelayMs == 0). // 3. We have received 'PacketTolerance' ACK eliciting packets. - // 4. We received an ACK eliciting packet that doesn't directly follow the - // previously received packet number. So we assume there might have - // been loss and should indicate this info to the peer. This logic is - // disabled if 'IgnoreReordering' is TRUE. + // 4. We have received an ACK eliciting packet that is out of order and the + // gap between the smallest Unreported Missing packet and the Largest + // Unacked is greater than or equal to the Reordering Threshold value. This logic is + // disabled if the Reordering Threshold is 0. // 5. The delayed ACK timer fires after the configured time. // // If we don't queue an immediate ACK and this is the first ACK eliciting @@ -195,12 +262,8 @@ QuicAckTrackerAckPacket( if (AckType == QUIC_ACK_TYPE_ACK_IMMEDIATE || Connection->Settings.MaxAckDelayMs == 0 || (Tracker->AckElicitingPacketsToAcknowledge >= (uint16_t)Connection->PacketTolerance) || - (!Connection->State.IgnoreReordering && - (NewLargestPacketNumber && - QuicRangeSize(&Tracker->PacketNumbersToAck) > 1 && // There are more than two ranges, i.e. a gap somewhere. - QuicRangeGet( - &Tracker->PacketNumbersToAck, - QuicRangeSize(&Tracker->PacketNumbersToAck) - 1)->Count == 1))) { // The gap is right before the last packet number. + (NewLargestPacketNumber && + QuicAckTrackerDidHitReorderingThreshold(Tracker, Connection->ReorderingThreshold))) { // // Send the ACK immediately. // diff --git a/src/core/ack_tracker.h b/src/core/ack_tracker.h index 802c337ce9..041d86a778 100644 --- a/src/core/ack_tracker.h +++ b/src/core/ack_tracker.h @@ -5,6 +5,10 @@ --*/ +#if defined(__cplusplus) +extern "C" { +#endif + typedef struct QUIC_ACK_TRACKER { // @@ -91,6 +95,12 @@ QuicAckTrackerAddPacketNumber( _In_ uint64_t PacketNumber ); +BOOLEAN +QuicAckTrackerDidHitReorderingThreshold( + _In_ QUIC_ACK_TRACKER* Tracker, + _In_ uint8_t ReorderingThreshold + ); + typedef enum QUIC_ACK_TYPE { QUIC_ACK_TYPE_NON_ACK_ELICITING, QUIC_ACK_TYPE_ACK_ELICITING, @@ -146,3 +156,7 @@ QuicAckTrackerHasPacketsToAck( !Tracker->AlreadyWrittenAckFrame && QuicRangeSize(&Tracker->PacketNumbersToAck) != 0; } + +#if defined(__cplusplus) +} +#endif diff --git a/src/core/binding.c b/src/core/binding.c index b0296a0ca5..348e1228d0 100644 --- a/src/core/binding.c +++ b/src/core/binding.c @@ -257,7 +257,7 @@ QuicBindingTraceRundown( CASTED_CLOG_BYTEARRAY(sizeof(DatapathLocalAddr), &DatapathLocalAddr), CASTED_CLOG_BYTEARRAY(sizeof(DatapathRemoteAddr), &DatapathRemoteAddr)); - CxPlatDispatchRwLockAcquireShared(&Binding->RwLock); + CxPlatDispatchRwLockAcquireShared(&Binding->RwLock, PrevIrql); for (CXPLAT_LIST_ENTRY* Link = Binding->Listeners.Flink; Link != &Binding->Listeners; @@ -266,7 +266,7 @@ QuicBindingTraceRundown( CXPLAT_CONTAINING_RECORD(Link, QUIC_LISTENER, Link)); } - CxPlatDispatchRwLockReleaseShared(&Binding->RwLock); + CxPlatDispatchRwLockReleaseShared(&Binding->RwLock, PrevIrql); } _IRQL_requires_max_(DISPATCH_LEVEL) @@ -327,7 +327,7 @@ QuicBindingRegisterListener( const BOOLEAN NewWildCard = NewListener->WildCard; const QUIC_ADDRESS_FAMILY NewFamily = QuicAddrGetFamily(NewAddr); - CxPlatDispatchRwLockAcquireExclusive(&Binding->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Binding->RwLock, PrevIrql); // // For a single binding, listeners are saved in a linked list, sorted by @@ -397,7 +397,7 @@ QuicBindingRegisterListener( } } - CxPlatDispatchRwLockReleaseExclusive(&Binding->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Binding->RwLock, PrevIrql); if (MaximizeLookup && !QuicLookupMaximizePartitioning(&Binding->Lookup)) { @@ -426,7 +426,7 @@ QuicBindingGetListener( BOOLEAN FailedAlpnMatch = FALSE; BOOLEAN FailedAddrMatch = TRUE; - CxPlatDispatchRwLockAcquireShared(&Binding->RwLock); + CxPlatDispatchRwLockAcquireShared(&Binding->RwLock, PrevIrql); for (CXPLAT_LIST_ENTRY* Link = Binding->Listeners.Flink; Link != &Binding->Listeners; @@ -460,7 +460,7 @@ QuicBindingGetListener( Done: - CxPlatDispatchRwLockReleaseShared(&Binding->RwLock); + CxPlatDispatchRwLockReleaseShared(&Binding->RwLock, PrevIrql); if (FailedAddrMatch) { QuicTraceEvent( @@ -487,9 +487,9 @@ QuicBindingUnregisterListener( _In_ QUIC_LISTENER* Listener ) { - CxPlatDispatchRwLockAcquireExclusive(&Binding->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Binding->RwLock, PrevIrql); CxPlatListEntryRemove(&Listener->Link); - CxPlatDispatchRwLockReleaseExclusive(&Binding->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Binding->RwLock, PrevIrql); } _IRQL_requires_max_(PASSIVE_LEVEL) diff --git a/src/core/connection.c b/src/core/connection.c index 7630a5bc79..b59d19ba45 100644 --- a/src/core/connection.c +++ b/src/core/connection.c @@ -118,6 +118,8 @@ QuicConnAlloc( Connection->AckDelayExponent = QUIC_ACK_DELAY_EXPONENT; Connection->PacketTolerance = QUIC_MIN_ACK_SEND_NUMBER; Connection->PeerPacketTolerance = QUIC_MIN_ACK_SEND_NUMBER; + Connection->ReorderingThreshold = QUIC_MIN_REORDERING_THRESHOLD; + Connection->PeerReorderingThreshold = QUIC_MIN_REORDERING_THRESHOLD; Connection->PeerTransportParams.AckDelayExponent = QUIC_TP_ACK_DELAY_EXPONENT_DEFAULT; Connection->ReceiveQueueTail = &Connection->ReceiveQueue; QuicSettingsCopy(&Connection->Settings, &MsQuicLib.Settings); @@ -664,6 +666,7 @@ QuicConnIndicateEvent( _Inout_ QUIC_CONNECTION_EVENT* Event ) { + CXPLAT_PASSIVE_CODE(); QUIC_STATUS Status; if (Connection->ClientCallbackHandler != NULL) { // @@ -5210,12 +5213,12 @@ QuicConnRecvFrames( return FALSE; } - if (Frame.UpdateMaxAckDelay < MS_TO_US(MsQuicLib.TimerResolutionMs)) { + if (Frame.RequestedMaxAckDelay < MS_TO_US(MsQuicLib.TimerResolutionMs)) { QuicTraceEvent( ConnError, "[conn][%p] ERROR, %s.", Connection, - "UpdateMaxAckDelay is less than TimerResolution"); + "RequestedMaxAckDelay is less than TimerResolution"); QuicConnTransportError(Connection, QUIC_ERROR_PROTOCOL_VIOLATION); return FALSE; } @@ -5230,20 +5233,24 @@ QuicConnRecvFrames( } Connection->NextRecvAckFreqSeqNum = Frame.SequenceNumber + 1; - Connection->State.IgnoreReordering = Frame.IgnoreOrder; - if (Frame.UpdateMaxAckDelay == 0) { + if (Frame.RequestedMaxAckDelay == 0) { Connection->Settings.MaxAckDelayMs = 0; - } else if (Frame.UpdateMaxAckDelay < 1000) { + } else if (Frame.RequestedMaxAckDelay < 1000) { Connection->Settings.MaxAckDelayMs = 1; } else { - CXPLAT_DBG_ASSERT(US_TO_MS(Frame.UpdateMaxAckDelay) <= UINT32_MAX); - Connection->Settings.MaxAckDelayMs = (uint32_t)US_TO_MS(Frame.UpdateMaxAckDelay); + CXPLAT_DBG_ASSERT(US_TO_MS(Frame.RequestedMaxAckDelay) <= UINT32_MAX); + Connection->Settings.MaxAckDelayMs = (uint32_t)US_TO_MS(Frame.RequestedMaxAckDelay); } - if (Frame.PacketTolerance < UINT8_MAX) { - Connection->PacketTolerance = (uint8_t)Frame.PacketTolerance; + if (Frame.AckElicitingThreshold < UINT8_MAX) { + Connection->PacketTolerance = (uint8_t)Frame.AckElicitingThreshold; } else { Connection->PacketTolerance = UINT8_MAX; // Cap to 0xFF for space savings. } + if (Frame.ReorderingThreshold < UINT8_MAX) { + Connection->ReorderingThreshold = (uint8_t)Frame.ReorderingThreshold; + } else { + Connection->ReorderingThreshold = UINT8_MAX; // Cap to 0xFF for space savings. + } QuicTraceLogConnInfo( UpdatePacketTolerance, Connection, @@ -5637,6 +5644,9 @@ QuicConnRecvDatagrams( if (!IsDeferred) { Connection->Stats.Recv.TotalBytes += Packet->BufferLength; + if (Connection->Stats.Handshake.HandshakeHopLimitTTL == 0) { + Connection->Stats.Handshake.HandshakeHopLimitTTL = Packet->HopLimitTTL; + } QuicConnLogInFlowStats(Connection); if (!CurrentPath->IsPeerValidated) { @@ -6822,6 +6832,10 @@ QuicConnGetV2Statistics( Stats->SendEcnCongestionCount = Connection->Stats.Send.EcnCongestionCount; } + if (STATISTICS_HAS_FIELD(*StatsLength, HandshakeHopLimitTTL)) { + Stats->HandshakeHopLimitTTL = Connection->Stats.Handshake.HandshakeHopLimitTTL; + } + *StatsLength = CXPLAT_MIN(*StatsLength, sizeof(QUIC_STATISTICS_V2)); return QUIC_STATUS_SUCCESS; diff --git a/src/core/connection.h b/src/core/connection.h index bfff21972a..c754115f52 100644 --- a/src/core/connection.h +++ b/src/core/connection.h @@ -146,12 +146,6 @@ typedef union QUIC_CONNECTION_STATE { // BOOLEAN ResumptionEnabled : 1; - // - // When true,acknowledgment that reordering shouldn't elict an - // immediate acknowledgement. - // - BOOLEAN IgnoreReordering : 1; - // // When true, this indicates that the connection is currently executing // an API call inline (from a reentrant call on a callback). @@ -275,6 +269,7 @@ typedef struct QUIC_CONN_STATS { uint32_t ClientFlight1Bytes; // Sum of TLS payloads uint32_t ServerFlight1Bytes; // Sum of TLS payloads uint32_t ClientFlight2Bytes; // Sum of TLS payloads + uint8_t HandshakeHopLimitTTL; // TTL value in the initial packet of the handshake. } Handshake; struct { @@ -452,6 +447,21 @@ typedef struct QUIC_CONNECTION { // uint8_t PeerPacketTolerance; + // + // The maximum number of packets that can be out of order before an immediate + // acknowledgment (ACK) is triggered. If no specific instructions (ACK_FREQUENCY + // frames) are received from the peer, the receiver will immediately acknowledge + // any out-of-order packets, which means the default value is 1. A value of 0 + // means out-of-order packets do not trigger an immediate ACK. + // + uint8_t ReorderingThreshold; + + // + // The maximum number of packets that the peer can be out of order before an immediate + // acknowledgment (ACK) is triggered. + // + uint8_t PeerReorderingThreshold; + // // The ACK frequency sequence number we are currently using to send. // diff --git a/src/core/crypto_tls.c b/src/core/crypto_tls.c index da1b459030..713405fef7 100644 --- a/src/core/crypto_tls.c +++ b/src/core/crypto_tls.c @@ -63,7 +63,7 @@ typedef enum eSniNameType { #define QUIC_TP_ID_MAX_DATAGRAM_FRAME_SIZE 32 // varint #define QUIC_TP_ID_DISABLE_1RTT_ENCRYPTION 0xBAAD // N/A #define QUIC_TP_ID_VERSION_NEGOTIATION_EXT 0x11 // Blob -#define QUIC_TP_ID_MIN_ACK_DELAY 0xFF03DE1AULL // varint +#define QUIC_TP_ID_MIN_ACK_DELAY 0xFF04DE1BULL // varint #define QUIC_TP_ID_CIBIR_ENCODING 0x1000 // {varint, varint} #define QUIC_TP_ID_GREASE_QUIC_BIT 0x2AB2 // N/A #define QUIC_TP_ID_RELIABLE_RESET_ENABLED 0x17f7586d2cb570 // varint diff --git a/src/core/frame.c b/src/core/frame.c index 7e1a923e11..20b6d91eb5 100644 --- a/src/core/frame.c +++ b/src/core/frame.c @@ -1234,19 +1234,6 @@ QuicDatagramFrameDecode( return TRUE; } -typedef struct QUIC_ACK_FREQUENCY_EXTRAS { - - union { - struct { - uint8_t IgnoreOrder : 1; - uint8_t IgnoreCE : 1; - uint8_t Reserved : 6; - }; - uint8_t Value; - }; - -} QUIC_ACK_FREQUENCY_EXTRAS; - _Success_(return != FALSE) BOOLEAN QuicAckFrequencyFrameEncode( @@ -1259,27 +1246,20 @@ QuicAckFrequencyFrameEncode( uint16_t RequiredLength = QuicVarIntSize(QUIC_FRAME_ACK_FREQUENCY) + QuicVarIntSize(Frame->SequenceNumber) + - QuicVarIntSize(Frame->PacketTolerance) + - QuicVarIntSize(Frame->UpdateMaxAckDelay) + - sizeof(QUIC_ACK_FREQUENCY_EXTRAS); + QuicVarIntSize(Frame->AckElicitingThreshold) + + QuicVarIntSize(Frame->RequestedMaxAckDelay) + + QuicVarIntSize(Frame->ReorderingThreshold); if (BufferLength < *Offset + RequiredLength) { return FALSE; } - CXPLAT_DBG_ASSERT(Frame->IgnoreOrder <= 1); // IgnoreOrder should only be 0 or 1. - CXPLAT_DBG_ASSERT(Frame->IgnoreCE <= 1); // IgnoreCE should only be 0 or 1. - - QUIC_ACK_FREQUENCY_EXTRAS Extras = { .Value = 0 }; - Extras.IgnoreOrder = Frame->IgnoreOrder; - Extras.IgnoreCE = Frame->IgnoreCE; - Buffer = Buffer + *Offset; Buffer = QuicVarIntEncode(QUIC_FRAME_ACK_FREQUENCY, Buffer); Buffer = QuicVarIntEncode(Frame->SequenceNumber, Buffer); - Buffer = QuicVarIntEncode(Frame->PacketTolerance, Buffer); - Buffer = QuicVarIntEncode(Frame->UpdateMaxAckDelay, Buffer); - QuicUint8Encode(Extras.Value, Buffer); + Buffer = QuicVarIntEncode(Frame->AckElicitingThreshold, Buffer); + Buffer = QuicVarIntEncode(Frame->RequestedMaxAckDelay, Buffer); + QuicVarIntEncode(Frame->ReorderingThreshold, Buffer); *Offset += RequiredLength; return TRUE; @@ -1295,15 +1275,12 @@ QuicAckFrequencyFrameDecode( _Out_ QUIC_ACK_FREQUENCY_EX* Frame ) { - QUIC_ACK_FREQUENCY_EXTRAS Extras; if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->SequenceNumber) || - !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->PacketTolerance) || - !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->UpdateMaxAckDelay) || - !QuicUint8tDecode(BufferLength, Buffer, Offset, &Extras.Value)) { + !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->AckElicitingThreshold) || + !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->RequestedMaxAckDelay) || + !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ReorderingThreshold)) { return FALSE; } - Frame->IgnoreOrder = Extras.IgnoreOrder; - Frame->IgnoreCE = Extras.IgnoreCE; return TRUE; } @@ -1963,15 +1940,14 @@ QuicFrameLog( QuicTraceLogVerbose( FrameLogAckFrequency, - "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu PktTolerance:%llu MaxAckDelay:%llu IgnoreOrder:%hhu IgnoreCE:%hhu", + "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu AckElicitThreshold:%llu MaxAckDelay:%llu ReorderThreshold:%llu", PtkConnPre(Connection), PktRxPre(Rx), PacketNumber, Frame.SequenceNumber, - Frame.PacketTolerance, - Frame.UpdateMaxAckDelay, - Frame.IgnoreOrder, - Frame.IgnoreCE); + Frame.AckElicitingThreshold, + Frame.RequestedMaxAckDelay, + Frame.ReorderingThreshold); break; } @@ -2006,7 +1982,7 @@ QuicFrameLog( Frame.Timestamp); break; } - + case QUIC_FRAME_RELIABLE_RESET_STREAM: { QUIC_RELIABLE_RESET_STREAM_EX Frame; if (!QuicReliableResetFrameDecode(PacketLength, Packet, Offset, &Frame)) { diff --git a/src/core/frame.h b/src/core/frame.h index 277177cbe9..f538ecffbc 100644 --- a/src/core/frame.h +++ b/src/core/frame.h @@ -156,7 +156,7 @@ typedef enum QUIC_FRAME_TYPE { QUIC_FRAME_DATAGRAM_1 = 0x31ULL, /* 0x32 to 0xad are unused currently */ QUIC_FRAME_ACK_FREQUENCY = 0xafULL, - QUIC_FRAME_IMMEDIATE_ACK = 0xacULL, + QUIC_FRAME_IMMEDIATE_ACK = 0x1fULL, /* 0xaf to 0x2f4 are unused currently */ QUIC_FRAME_TIMESTAMP = 0x2f5ULL, @@ -844,11 +844,9 @@ QuicDatagramFrameDecode( typedef struct QUIC_ACK_FREQUENCY_EX { QUIC_VAR_INT SequenceNumber; - QUIC_VAR_INT PacketTolerance; - QUIC_VAR_INT UpdateMaxAckDelay; // In microseconds (us) - BOOLEAN IgnoreOrder; - BOOLEAN IgnoreCE; - + QUIC_VAR_INT AckElicitingThreshold; + QUIC_VAR_INT RequestedMaxAckDelay; // In microseconds (us) + QUIC_VAR_INT ReorderingThreshold; } QUIC_ACK_FREQUENCY_EX; _Success_(return != FALSE) diff --git a/src/core/listener.c b/src/core/listener.c index 7c53766618..8466f8302f 100644 --- a/src/core/listener.c +++ b/src/core/listener.c @@ -406,6 +406,7 @@ QuicListenerIndicateEvent( _Inout_ QUIC_LISTENER_EVENT* Event ) { + CXPLAT_PASSIVE_CODE(); CXPLAT_FRE_ASSERT(Listener->ClientCallbackHandler); return Listener->ClientCallbackHandler( diff --git a/src/core/lookup.c b/src/core/lookup.c index 3f96e08192..4449a05afc 100644 --- a/src/core/lookup.c +++ b/src/core/lookup.c @@ -247,7 +247,7 @@ QuicLookupMaximizePartitioning( { BOOLEAN Result = TRUE; - CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); if (!Lookup->MaximizePartitioning) { Result = @@ -263,7 +263,7 @@ QuicLookupMaximizePartitioning( } } - CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); return Result; } @@ -369,14 +369,14 @@ QuicLookupFindConnectionByLocalCidInternal( PartitionIndex %= Lookup->PartitionCount; QUIC_PARTITIONED_HASHTABLE* Table = &Lookup->HASH.Tables[PartitionIndex]; - CxPlatDispatchRwLockAcquireShared(&Table->RwLock); + CxPlatDispatchRwLockAcquireShared(&Table->RwLock, PrevIrql); Connection = QuicHashLookupConnection( &Table->Table, CID, CIDLen, Hash); - CxPlatDispatchRwLockReleaseShared(&Table->RwLock); + CxPlatDispatchRwLockReleaseShared(&Table->RwLock, PrevIrql); } #if QUIC_DEBUG_HASHTABLE_LOOKUP @@ -487,13 +487,13 @@ QuicLookupInsertLocalCid( PartitionIndex %= Lookup->PartitionCount; QUIC_PARTITIONED_HASHTABLE* Table = &Lookup->HASH.Tables[PartitionIndex]; - CxPlatDispatchRwLockAcquireExclusive(&Table->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Table->RwLock, PrevIrql); CxPlatHashtableInsert( &Table->Table, &SourceCid->Entry, Hash, NULL); - CxPlatDispatchRwLockReleaseExclusive(&Table->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Table->RwLock, PrevIrql); } if (UpdateRefCount) { @@ -617,9 +617,9 @@ QuicLookupRemoveLocalCidInt( PartitionIndex &= MsQuicLib.PartitionMask; PartitionIndex %= Lookup->PartitionCount; QUIC_PARTITIONED_HASHTABLE* Table = &Lookup->HASH.Tables[PartitionIndex]; - CxPlatDispatchRwLockAcquireExclusive(&Table->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Table->RwLock, PrevIrql); CxPlatHashtableRemove(&Table->Table, &SourceCid->Entry, NULL); - CxPlatDispatchRwLockReleaseExclusive(&Table->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Table->RwLock, PrevIrql); } } @@ -634,7 +634,7 @@ QuicLookupFindConnectionByLocalCid( { uint32_t Hash = CxPlatHashSimple(CIDLen, CID); - CxPlatDispatchRwLockAcquireShared(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireShared(&Lookup->RwLock, PrevIrql); QUIC_CONNECTION* ExistingConnection = QuicLookupFindConnectionByLocalCidInternal( @@ -647,7 +647,7 @@ QuicLookupFindConnectionByLocalCid( QuicConnAddRef(ExistingConnection, QUIC_CONN_REF_LOOKUP_RESULT); } - CxPlatDispatchRwLockReleaseShared(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseShared(&Lookup->RwLock, PrevIrql); return ExistingConnection; } @@ -664,7 +664,7 @@ QuicLookupFindConnectionByRemoteHash( { uint32_t Hash = QuicPacketHash(RemoteAddress, RemoteCidLength, RemoteCid); - CxPlatDispatchRwLockAcquireShared(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireShared(&Lookup->RwLock, PrevIrql); QUIC_CONNECTION* ExistingConnection; if (Lookup->MaximizePartitioning) { @@ -684,7 +684,7 @@ QuicLookupFindConnectionByRemoteHash( ExistingConnection = NULL; } - CxPlatDispatchRwLockReleaseShared(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseShared(&Lookup->RwLock, PrevIrql); return ExistingConnection; } @@ -699,7 +699,7 @@ QuicLookupFindConnectionByRemoteAddr( QUIC_CONNECTION* ExistingConnection = NULL; UNREFERENCED_PARAMETER(RemoteAddress); // Can't even validate this for single connection lookups right now. - CxPlatDispatchRwLockAcquireShared(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireShared(&Lookup->RwLock, PrevIrql); if (Lookup->PartitionCount == 0) { // @@ -717,7 +717,7 @@ QuicLookupFindConnectionByRemoteAddr( QuicConnAddRef(ExistingConnection, QUIC_CONN_REF_LOOKUP_RESULT); } - CxPlatDispatchRwLockReleaseShared(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseShared(&Lookup->RwLock, PrevIrql); return ExistingConnection; } @@ -734,7 +734,7 @@ QuicLookupAddLocalCid( QUIC_CONNECTION* ExistingConnection; uint32_t Hash = CxPlatHashSimple(SourceCid->CID.Length, SourceCid->CID.Data); - CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); CXPLAT_DBG_ASSERT(!SourceCid->CID.IsInLookupTable); @@ -759,7 +759,7 @@ QuicLookupAddLocalCid( } } - CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); return Result; } @@ -781,7 +781,7 @@ QuicLookupAddRemoteHash( QUIC_CONNECTION* ExistingConnection; uint32_t Hash = QuicPacketHash(RemoteAddress, RemoteCidLength, RemoteCid); - CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); if (Lookup->MaximizePartitioning) { ExistingConnection = @@ -813,7 +813,7 @@ QuicLookupAddRemoteHash( *Collision = NULL; } - CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); return Result; } @@ -826,11 +826,11 @@ QuicLookupRemoveLocalCid( _In_ CXPLAT_SLIST_ENTRY** Entry ) { - CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); QuicLookupRemoveLocalCidInt(Lookup, SourceCid); SourceCid->CID.IsInLookupTable = FALSE; *Entry = (*Entry)->Next; - CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); QuicConnRelease(SourceCid->Connection, QUIC_CONN_REF_LOOKUP_TABLE); } @@ -846,14 +846,14 @@ QuicLookupRemoveRemoteHash( QuicLibraryOnHandshakeConnectionRemoved(); - CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); CXPLAT_DBG_ASSERT(Connection->RemoteHashEntry != NULL); CxPlatHashtableRemove( &Lookup->RemoteHashTable, &RemoteHashEntry->Entry, NULL); Connection->RemoteHashEntry = NULL; - CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); CXPLAT_FREE(RemoteHashEntry, QUIC_POOL_REMOTE_HASH); QuicConnRelease(Connection, QUIC_CONN_REF_LOOKUP_TABLE); @@ -868,7 +868,7 @@ QuicLookupRemoveLocalCids( { uint8_t ReleaseRefCount = 0; - CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&Lookup->RwLock, PrevIrql); while (Connection->SourceCids.Next != NULL) { QUIC_CID_HASH_ENTRY *CID = CXPLAT_CONTAINING_RECORD( @@ -882,7 +882,7 @@ QuicLookupRemoveLocalCids( } CXPLAT_FREE(CID, QUIC_POOL_CIDHASH); } - CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&Lookup->RwLock, PrevIrql); for (uint8_t i = 0; i < ReleaseRefCount; i++) { #pragma prefast(suppress:6001, "SAL doesn't understand ref counts") @@ -900,7 +900,7 @@ QuicLookupMoveLocalConnectionIDs( { CXPLAT_SLIST_ENTRY* Entry = Connection->SourceCids.Next; - CxPlatDispatchRwLockAcquireExclusive(&LookupSrc->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&LookupSrc->RwLock, PrevIrql1); while (Entry != NULL) { QUIC_CID_HASH_ENTRY *CID = CXPLAT_CONTAINING_RECORD( @@ -913,9 +913,9 @@ QuicLookupMoveLocalConnectionIDs( } Entry = Entry->Next; } - CxPlatDispatchRwLockReleaseExclusive(&LookupSrc->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&LookupSrc->RwLock, PrevIrql1); - CxPlatDispatchRwLockAcquireExclusive(&LookupDest->RwLock); + CxPlatDispatchRwLockAcquireExclusive(&LookupDest->RwLock, PrevIrql2); #pragma prefast(suppress:6001, "SAL doesn't understand ref counts") Entry = Connection->SourceCids.Next; while (Entry != NULL) { @@ -936,5 +936,5 @@ QuicLookupMoveLocalConnectionIDs( } Entry = Entry->Next; } - CxPlatDispatchRwLockReleaseExclusive(&LookupDest->RwLock); + CxPlatDispatchRwLockReleaseExclusive(&LookupDest->RwLock, PrevIrql2); } diff --git a/src/core/quicdef.h b/src/core/quicdef.h index 07956b50fb..10bec83f95 100644 --- a/src/core/quicdef.h +++ b/src/core/quicdef.h @@ -103,6 +103,12 @@ typedef struct QUIC_RX_PACKET QUIC_RX_PACKET; // #define QUIC_MIN_ACK_SEND_NUMBER 2 +// +// The value for Reordering threshold when no ACK_FREQUENCY frame is received. +// This means that the receiver will immediately acknowledge any out-of-order packets. +// +#define QUIC_MIN_REORDERING_THRESHOLD 1 + // // The size of the stateless reset token. // diff --git a/src/core/send.c b/src/core/send.c index bbd1d0493b..306801d4bf 100644 --- a/src/core/send.c +++ b/src/core/send.c @@ -894,10 +894,9 @@ QuicSendWriteFrames( QUIC_ACK_FREQUENCY_EX Frame; Frame.SequenceNumber = Connection->SendAckFreqSeqNum; - Frame.PacketTolerance = Connection->PeerPacketTolerance; - Frame.UpdateMaxAckDelay = MS_TO_US(QuicConnGetAckDelay(Connection)); - Frame.IgnoreOrder = FALSE; - Frame.IgnoreCE = FALSE; + Frame.AckElicitingThreshold = Connection->PeerPacketTolerance; + Frame.RequestedMaxAckDelay = MS_TO_US(QuicConnGetAckDelay(Connection)); + Frame.ReorderingThreshold = Connection->PeerReorderingThreshold; if (QuicAckFrequencyFrameEncode( &Frame, diff --git a/src/core/settings.c b/src/core/settings.c index a3942f691d..40dd41245f 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -431,6 +431,9 @@ QuicSettingApply( Destination->IsSet.DatagramReceiveEnabled = TRUE; } if (Source->IsSet.MaxOperationsPerDrain && (!Destination->IsSet.MaxOperationsPerDrain || OverWrite)) { + if (Source->MaxOperationsPerDrain == 0) { + return FALSE; + } Destination->MaxOperationsPerDrain = Source->MaxOperationsPerDrain; Destination->IsSet.MaxOperationsPerDrain = TRUE; } @@ -795,7 +798,7 @@ QuicSettingsLoad( QUIC_SETTING_MAX_OPERATIONS_PER_DRAIN, (uint8_t*)&Value, &ValueLen); - if (Value <= UINT8_MAX) { + if (Value > 0 && Value <= UINT8_MAX) { Settings->MaxOperationsPerDrain = (uint8_t)Value; } } diff --git a/src/core/stream.c b/src/core/stream.c index 8b849f7ab4..a96ca37d7a 100644 --- a/src/core/stream.c +++ b/src/core/stream.c @@ -448,6 +448,7 @@ QuicStreamIndicateEvent( _Inout_ QUIC_STREAM_EVENT* Event ) { + CXPLAT_PASSIVE_CODE(); QUIC_STATUS Status; if (Stream->ClientCallbackHandler != NULL) { // diff --git a/src/core/unittest/CMakeLists.txt b/src/core/unittest/CMakeLists.txt index 4f811b475e..4e4170cf99 100644 --- a/src/core/unittest/CMakeLists.txt +++ b/src/core/unittest/CMakeLists.txt @@ -27,7 +27,7 @@ set_property(TARGET msquiccoretest APPEND PROPERTY BUILD_RPATH "$ORIGIN") target_link_libraries(msquiccoretest msquic) if (BUILD_SHARED_LIBS) - target_link_libraries(msquiccoretest core platform) + target_link_libraries(msquiccoretest core msquic_platform) endif() target_link_libraries(msquiccoretest inc gtest warnings logging base_link) diff --git a/src/core/unittest/FrameTest.cpp b/src/core/unittest/FrameTest.cpp index 7daff1d250..f3cde7e738 100644 --- a/src/core/unittest/FrameTest.cpp +++ b/src/core/unittest/FrameTest.cpp @@ -243,6 +243,64 @@ TEST(FrameTest, ReliableResetStreamFrameEncodeDecode) ASSERT_EQ(Frame.ReliableSize, DecodedFrame.ReliableSize); } +// +// Tests if the reordering threshold has been hit. This function initializes a +// QUIC_ACK_TRACKER and populates it with the provided packet numbers. It then +// calls QuicAckTrackerDidHitReorderingThreshold to determine if the reordering +// threshold has been hit. +// +bool TestReorderingThreshold( + uint8_t ReorderingThreshold, + uint64_t LargestPacketNumberAcknowledged, + const std::vector>& AckTrackerRanges) +{ + QUIC_ACK_TRACKER Tracker; + QuicAckTrackerInitialize(&Tracker); + for (const auto& Range : AckTrackerRanges) { + if (Range.size() == 1) { + QuicRangeAddValue(&Tracker.PacketNumbersToAck, Range[0]); + } else { + for (int packet = Range[0]; packet <= Range[1]; ++packet) { + QuicRangeAddValue(&Tracker.PacketNumbersToAck, packet); + } + } + } + Tracker.LargestPacketNumberAcknowledged = LargestPacketNumberAcknowledged; + BOOLEAN Result = QuicAckTrackerDidHitReorderingThreshold(&Tracker, ReorderingThreshold); + QuicAckTrackerUninitialize(&Tracker); + return Result; +} + +TEST(FrameTest, TestQuicAckTrackerDidHitReorderingThreshold) +{ + ASSERT_FALSE(TestReorderingThreshold(0, 0, {{100}})); + + // Case 1 + ASSERT_FALSE(TestReorderingThreshold(3, 0, {{0}})); + ASSERT_FALSE(TestReorderingThreshold(3, 0, {{0, 1}})); + ASSERT_FALSE(TestReorderingThreshold(3, 0, {{0, 1}, {3}})); + ASSERT_FALSE(TestReorderingThreshold(3, 0, {{0, 1}, {3, 4}})); + ASSERT_TRUE(TestReorderingThreshold(3, 0, {{0, 1}, {3, 5}})); + ASSERT_FALSE(TestReorderingThreshold(3, 5, {{0, 1}, {3, 5}, {8}})); + ASSERT_TRUE(TestReorderingThreshold(3, 5, {{0, 1}, {3, 5}, {8, 9}})); + ASSERT_TRUE(TestReorderingThreshold(3, 9, {{0, 1}, {3, 5}, {8, 10}})); + + // Case 2 + ASSERT_FALSE(TestReorderingThreshold(5, 0, {{0}})); + ASSERT_FALSE(TestReorderingThreshold(5, 0, {{0, 1}})); + ASSERT_FALSE(TestReorderingThreshold(5, 0, {{0, 1}, {3}})); + ASSERT_FALSE(TestReorderingThreshold(5, 0, {{0, 1}, {3}, {5}})); + ASSERT_FALSE(TestReorderingThreshold(5, 0, {{0, 1}, {3}, {5, 6}})); + ASSERT_TRUE(TestReorderingThreshold(5, 0, {{0, 1}, {3}, {5, 7}})); + ASSERT_FALSE(TestReorderingThreshold(5, 7, {{0, 1}, {3}, {5, 8}})); + ASSERT_TRUE(TestReorderingThreshold(5, 7, {{0, 1}, {3}, {5, 9}})); + + // Additional cases to test edge conditions + ASSERT_FALSE(TestReorderingThreshold(5, 0, {{1, 2}, {4}})); + ASSERT_FALSE(TestReorderingThreshold(5, 2, {{1, 2}, {4}})); + ASSERT_TRUE(TestReorderingThreshold(5, 4, {{1, 2}, {4}, {10}})); +} + struct ResetStreamFrameParams { uint8_t Buffer[4]; uint16_t BufferLength = 4; diff --git a/src/core/worker.c b/src/core/worker.c index 24c687933a..6338834413 100644 --- a/src/core/worker.c +++ b/src/core/worker.c @@ -118,8 +118,13 @@ QuicWorkerInitialize( break; } - if (MsQuicLib.ExecutionConfig && MsQuicLib.ExecutionConfig->Flags & QUIC_EXECUTION_CONFIG_FLAG_HIGH_PRIORITY) { - ThreadFlags |= CXPLAT_THREAD_FLAG_HIGH_PRIORITY; + if (MsQuicLib.ExecutionConfig) { + if (MsQuicLib.ExecutionConfig->Flags & QUIC_EXECUTION_CONFIG_FLAG_HIGH_PRIORITY) { + ThreadFlags |= CXPLAT_THREAD_FLAG_HIGH_PRIORITY; + } + if (MsQuicLib.ExecutionConfig->Flags & QUIC_EXECUTION_CONFIG_FLAG_AFFINITIZE) { + ThreadFlags |= CXPLAT_THREAD_FLAG_SET_AFFINITIZE; + } } CXPLAT_THREAD_CONFIG ThreadConfig = { diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs index cf1477ea76..375e6897b0 100644 --- a/src/cs/lib/msquic_generated.cs +++ b/src/cs/lib/msquic_generated.cs @@ -99,6 +99,7 @@ internal enum QUIC_CREDENTIAL_FLAGS REVOCATION_CHECK_CACHE_ONLY = 0x00040000, INPROC_PEER_CERTIFICATE = 0x00080000, SET_CA_CERTIFICATE_FILE = 0x00100000, + DISABLE_AIA = 0x00200000, } [System.Flags] @@ -218,6 +219,7 @@ internal enum QUIC_EXECUTION_CONFIG_FLAGS XDP = 0x0004, NO_IDEAL_PROC = 0x0008, HIGH_PRIORITY = 0x0010, + AFFINITIZE = 0x0020, } internal unsafe partial struct QUIC_EXECUTION_CONFIG @@ -888,6 +890,9 @@ internal uint RESERVED [NativeTypeName("uint32_t")] internal uint SendEcnCongestionCount; + + [NativeTypeName("uint8_t")] + internal byte HandshakeHopLimitTTL; } internal partial struct QUIC_LISTENER_STATISTICS diff --git a/src/cs/tool/Program.cs b/src/cs/tool/Program.cs index 4b4f7f2763..bdd3e0beee 100644 --- a/src/cs/tool/Program.cs +++ b/src/cs/tool/Program.cs @@ -18,12 +18,32 @@ static unsafe void Main(string[] args) { // This code lets us pass in an argument of where to search for the library at. // Very helpful for testing - if (args.Length > 0) + if (args.Length == 0) + { + Console.WriteLine("Usage: MsQuicTool [PathToMsQuic]"); + return; + } + + string DomainName = args[0]; + + if (string.IsNullOrWhiteSpace(DomainName)) + { + Console.WriteLine("DomainName cannot be empty."); + return; + } + + if (DomainName.Length > 253) + { + Console.WriteLine("DomainName is too long."); + return; + } + + if (args.Length > 1) { NativeLibrary.SetDllImportResolver(typeof(MsQuic).Assembly, (libraryName, assembly, searchPath) => { if (libraryName != "msquic") return IntPtr.Zero; - if (NativeLibrary.TryLoad(args[0], out var ptr)) + if (NativeLibrary.TryLoad(args[1], out var ptr)) { return ptr; } @@ -55,7 +75,7 @@ static unsafe void Main(string[] args) MsQuic.ThrowIfFailure(ApiTable->ConfigurationLoadCredential(configuration, &config)); MsQuic.ThrowIfFailure(ApiTable->ConnectionOpen(registration, &NativeCallback, ApiTable, &connection)); sbyte* google = stackalloc sbyte[50]; - int written = Encoding.UTF8.GetBytes("google.com", new Span(google, 50)); + int written = Encoding.UTF8.GetBytes(DomainName, new Span(google, 50)); google[written] = 0; MsQuic.ThrowIfFailure(ApiTable->ConnectionStart(connection, configuration, 0, google, 443)); Thread.Sleep(1000); diff --git a/src/generated/CMakeLists.txt b/src/generated/CMakeLists.txt index 19c49f1bf3..93bd6cc9a2 100644 --- a/src/generated/CMakeLists.txt +++ b/src/generated/CMakeLists.txt @@ -23,9 +23,15 @@ if(QUIC_ENABLE_LOGGING) target_link_libraries(logging PRIVATE inc) elseif(QUIC_LOGGING_TYPE STREQUAL "lttng") - target_include_directories(logging_inc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/common) - target_include_directories(logging_inc INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/linux) - target_include_directories(logging_inc INTERFACE ${LTTNGUST_INCLUDE_DIRS}) + target_include_directories(logging_inc INTERFACE + $ + $) + target_include_directories(logging_inc INTERFACE + $ + $) + target_include_directories(logging_inc INTERFACE + $ + $) FILE(GLOB LOGGING_FILES ${CMAKE_CURRENT_SOURCE_DIR}/linux/*.c) add_library(logging STATIC ${LOGGING_FILES}) diff --git a/src/generated/linux/datapath_winuser.c.clog.h b/src/generated/linux/datapath_winuser.c.clog.h index 7926a2bbe0..d77ffd43af 100644 --- a/src/generated/linux/datapath_winuser.c.clog.h +++ b/src/generated/linux/datapath_winuser.c.clog.h @@ -22,6 +22,10 @@ #define _clog_MACRO_QuicTraceLogVerbose 1 #define QuicTraceLogVerbose(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__))) #endif +#ifndef _clog_MACRO_QuicTraceLogError +#define _clog_MACRO_QuicTraceLogError 1 +#define QuicTraceLogError(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__))) +#endif #ifndef _clog_MACRO_QuicTraceEvent #define _clog_MACRO_QuicTraceEvent 1 #define QuicTraceEvent(a, ...) _clog_CAT(_clog_ARGN_SELECTOR(__VA_ARGS__), _clog_CAT(_,a(#a, __VA_ARGS__))) @@ -251,6 +255,26 @@ tracepoint(CLOG_DATAPATH_WINUSER_C, DatapathTooLarge , arg2, arg3_len, arg3);\ +/*---------------------------------------------------------- +// Decoder Ring for DatapathResolveHostNameFailed +// [%p] Couldn't resolve hostname '%s' to an IP address +// QuicTraceLogError( + DatapathResolveHostNameFailed, + "[%p] Couldn't resolve hostname '%s' to an IP address", + Datapath, + HostName); +// arg2 = arg2 = Datapath = arg2 +// arg3 = arg3 = HostName = arg3 +----------------------------------------------------------*/ +#ifndef _clog_4_ARGS_TRACE_DatapathResolveHostNameFailed +#define _clog_4_ARGS_TRACE_DatapathResolveHostNameFailed(uniqueId, encoded_arg_string, arg2, arg3)\ +tracepoint(CLOG_DATAPATH_WINUSER_C, DatapathResolveHostNameFailed , arg2, arg3);\ + +#endif + + + + /*---------------------------------------------------------- // Decoder Ring for LibraryErrorStatus // [ lib] ERROR, %u, %s. @@ -291,6 +315,24 @@ tracepoint(CLOG_DATAPATH_WINUSER_C, AllocFailure , arg2, arg3);\ +/*---------------------------------------------------------- +// Decoder Ring for LibraryError +// [ lib] ERROR, %s. +// QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "No local unicast addresses found"); +// arg2 = arg2 = "No local unicast addresses found" = arg2 +----------------------------------------------------------*/ +#ifndef _clog_3_ARGS_TRACE_LibraryError +#define _clog_3_ARGS_TRACE_LibraryError(uniqueId, encoded_arg_string, arg2)\ +tracepoint(CLOG_DATAPATH_WINUSER_C, LibraryError , arg2);\ + +#endif + + + + /*---------------------------------------------------------- // Decoder Ring for DatapathErrorStatus // [data][%p] ERROR, %u, %s. diff --git a/src/generated/linux/datapath_winuser.c.clog.h.lttng.h b/src/generated/linux/datapath_winuser.c.clog.h.lttng.h index 09933cc4ad..6dfd96927d 100644 --- a/src/generated/linux/datapath_winuser.c.clog.h.lttng.h +++ b/src/generated/linux/datapath_winuser.c.clog.h.lttng.h @@ -245,6 +245,29 @@ TRACEPOINT_EVENT(CLOG_DATAPATH_WINUSER_C, DatapathTooLarge, +/*---------------------------------------------------------- +// Decoder Ring for DatapathResolveHostNameFailed +// [%p] Couldn't resolve hostname '%s' to an IP address +// QuicTraceLogError( + DatapathResolveHostNameFailed, + "[%p] Couldn't resolve hostname '%s' to an IP address", + Datapath, + HostName); +// arg2 = arg2 = Datapath = arg2 +// arg3 = arg3 = HostName = arg3 +----------------------------------------------------------*/ +TRACEPOINT_EVENT(CLOG_DATAPATH_WINUSER_C, DatapathResolveHostNameFailed, + TP_ARGS( + const void *, arg2, + const char *, arg3), + TP_FIELDS( + ctf_integer_hex(uint64_t, arg2, (uint64_t)arg2) + ctf_string(arg3, arg3) + ) +) + + + /*---------------------------------------------------------- // Decoder Ring for LibraryErrorStatus // [ lib] ERROR, %u, %s. @@ -291,6 +314,25 @@ TRACEPOINT_EVENT(CLOG_DATAPATH_WINUSER_C, AllocFailure, +/*---------------------------------------------------------- +// Decoder Ring for LibraryError +// [ lib] ERROR, %s. +// QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "No local unicast addresses found"); +// arg2 = arg2 = "No local unicast addresses found" = arg2 +----------------------------------------------------------*/ +TRACEPOINT_EVENT(CLOG_DATAPATH_WINUSER_C, LibraryError, + TP_ARGS( + const char *, arg2), + TP_FIELDS( + ctf_string(arg2, arg2) + ) +) + + + /*---------------------------------------------------------- // Decoder Ring for DatapathErrorStatus // [data][%p] ERROR, %u, %s. diff --git a/src/generated/linux/frame.c.clog.h b/src/generated/linux/frame.c.clog.h index 99477a4200..e8c8b30384 100644 --- a/src/generated/linux/frame.c.clog.h +++ b/src/generated/linux/frame.c.clog.h @@ -1173,30 +1173,28 @@ tracepoint(CLOG_FRAME_C, FrameLogAckFrequencyInvalid , arg2, arg3, arg4);\ /*---------------------------------------------------------- // Decoder Ring for FrameLogAckFrequency -// [%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu PktTolerance:%llu MaxAckDelay:%llu IgnoreOrder:%hhu IgnoreCE:%hhu +// [%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu AckElicitThreshold:%llu MaxAckDelay:%llu ReorderThreshold:%llu // QuicTraceLogVerbose( FrameLogAckFrequency, - "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu PktTolerance:%llu MaxAckDelay:%llu IgnoreOrder:%hhu IgnoreCE:%hhu", + "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu AckElicitThreshold:%llu MaxAckDelay:%llu ReorderThreshold:%llu", PtkConnPre(Connection), PktRxPre(Rx), PacketNumber, Frame.SequenceNumber, - Frame.PacketTolerance, - Frame.UpdateMaxAckDelay, - Frame.IgnoreOrder, - Frame.IgnoreCE); + Frame.AckElicitingThreshold, + Frame.RequestedMaxAckDelay, + Frame.ReorderingThreshold); // arg2 = arg2 = PtkConnPre(Connection) = arg2 // arg3 = arg3 = PktRxPre(Rx) = arg3 // arg4 = arg4 = PacketNumber = arg4 // arg5 = arg5 = Frame.SequenceNumber = arg5 -// arg6 = arg6 = Frame.PacketTolerance = arg6 -// arg7 = arg7 = Frame.UpdateMaxAckDelay = arg7 -// arg8 = arg8 = Frame.IgnoreOrder = arg8 -// arg9 = arg9 = Frame.IgnoreCE = arg9 +// arg6 = arg6 = Frame.AckElicitingThreshold = arg6 +// arg7 = arg7 = Frame.RequestedMaxAckDelay = arg7 +// arg8 = arg8 = Frame.ReorderingThreshold = arg8 ----------------------------------------------------------*/ -#ifndef _clog_10_ARGS_TRACE_FrameLogAckFrequency -#define _clog_10_ARGS_TRACE_FrameLogAckFrequency(uniqueId, encoded_arg_string, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)\ -tracepoint(CLOG_FRAME_C, FrameLogAckFrequency , arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);\ +#ifndef _clog_9_ARGS_TRACE_FrameLogAckFrequency +#define _clog_9_ARGS_TRACE_FrameLogAckFrequency(uniqueId, encoded_arg_string, arg2, arg3, arg4, arg5, arg6, arg7, arg8)\ +tracepoint(CLOG_FRAME_C, FrameLogAckFrequency , arg2, arg3, arg4, arg5, arg6, arg7, arg8);\ #endif diff --git a/src/generated/linux/frame.c.clog.h.lttng.h b/src/generated/linux/frame.c.clog.h.lttng.h index 5c25f96c93..df916db905 100644 --- a/src/generated/linux/frame.c.clog.h.lttng.h +++ b/src/generated/linux/frame.c.clog.h.lttng.h @@ -1479,26 +1479,24 @@ TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogAckFrequencyInvalid, /*---------------------------------------------------------- // Decoder Ring for FrameLogAckFrequency -// [%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu PktTolerance:%llu MaxAckDelay:%llu IgnoreOrder:%hhu IgnoreCE:%hhu +// [%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu AckElicitThreshold:%llu MaxAckDelay:%llu ReorderThreshold:%llu // QuicTraceLogVerbose( FrameLogAckFrequency, - "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu PktTolerance:%llu MaxAckDelay:%llu IgnoreOrder:%hhu IgnoreCE:%hhu", + "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu AckElicitThreshold:%llu MaxAckDelay:%llu ReorderThreshold:%llu", PtkConnPre(Connection), PktRxPre(Rx), PacketNumber, Frame.SequenceNumber, - Frame.PacketTolerance, - Frame.UpdateMaxAckDelay, - Frame.IgnoreOrder, - Frame.IgnoreCE); + Frame.AckElicitingThreshold, + Frame.RequestedMaxAckDelay, + Frame.ReorderingThreshold); // arg2 = arg2 = PtkConnPre(Connection) = arg2 // arg3 = arg3 = PktRxPre(Rx) = arg3 // arg4 = arg4 = PacketNumber = arg4 // arg5 = arg5 = Frame.SequenceNumber = arg5 -// arg6 = arg6 = Frame.PacketTolerance = arg6 -// arg7 = arg7 = Frame.UpdateMaxAckDelay = arg7 -// arg8 = arg8 = Frame.IgnoreOrder = arg8 -// arg9 = arg9 = Frame.IgnoreCE = arg9 +// arg6 = arg6 = Frame.AckElicitingThreshold = arg6 +// arg7 = arg7 = Frame.RequestedMaxAckDelay = arg7 +// arg8 = arg8 = Frame.ReorderingThreshold = arg8 ----------------------------------------------------------*/ TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogAckFrequency, TP_ARGS( @@ -1508,8 +1506,7 @@ TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogAckFrequency, unsigned long long, arg5, unsigned long long, arg6, unsigned long long, arg7, - unsigned char, arg8, - unsigned char, arg9), + unsigned long long, arg8), TP_FIELDS( ctf_integer(unsigned char, arg2, arg2) ctf_integer(unsigned char, arg3, arg3) @@ -1517,8 +1514,7 @@ TRACEPOINT_EVENT(CLOG_FRAME_C, FrameLogAckFrequency, ctf_integer(uint64_t, arg5, arg5) ctf_integer(uint64_t, arg6, arg6) ctf_integer(uint64_t, arg7, arg7) - ctf_integer(unsigned char, arg8, arg8) - ctf_integer(unsigned char, arg9, arg9) + ctf_integer(uint64_t, arg8, arg8) ) ) diff --git a/src/inc/CMakeLists.txt b/src/inc/CMakeLists.txt index dde47b8252..48edebd1e0 100644 --- a/src/inc/CMakeLists.txt +++ b/src/inc/CMakeLists.txt @@ -42,10 +42,13 @@ if(WIN32) if(QUIC_UWP_BUILD) target_link_libraries(base_link INTERFACE OneCore ws2_32 ntdll) elseif(QUIC_GAMECORE_BUILD) - target_link_options(inc INTERFACE ${Console_LinkOptions}) - target_compile_options(inc INTERFACE ${Console_ArchOptions}) - target_link_directories(inc INTERFACE ${Console_EndpointLibRoot}) - target_link_libraries(base_link INTERFACE xgameplatform ntdll advapi32) + target_link_libraries(base_link INTERFACE ntdll advapi32) + if(NOT QUIC_EXTERNAL_TOOLCHAIN) + target_link_options(inc INTERFACE ${Console_LinkOptions}) + target_compile_options(inc INTERFACE ${Console_ArchOptions}) + target_link_directories(inc INTERFACE ${Console_EndpointLibRoot}) + target_link_libraries(base_link INTERFACE xgameplatform) + endif() else() target_link_libraries(base_link INTERFACE ws2_32 schannel ntdll bcrypt ncrypt crypt32 iphlpapi advapi32) endif() diff --git a/src/inc/msquic.h b/src/inc/msquic.h index d9c25b8933..825bfb8a6a 100644 --- a/src/inc/msquic.h +++ b/src/inc/msquic.h @@ -146,6 +146,7 @@ typedef enum QUIC_CREDENTIAL_FLAGS { QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY = 0x00040000, // Windows only currently QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE = 0x00080000, // Schannel only QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE = 0x00100000, // OpenSSL only currently + QUIC_CREDENTIAL_FLAG_DISABLE_AIA = 0x00200000, // Schannel only currently } QUIC_CREDENTIAL_FLAGS; DEFINE_ENUM_FLAG_OPERATORS(QUIC_CREDENTIAL_FLAGS) @@ -272,6 +273,7 @@ typedef enum QUIC_EXECUTION_CONFIG_FLAGS { QUIC_EXECUTION_CONFIG_FLAG_XDP = 0x0004, QUIC_EXECUTION_CONFIG_FLAG_NO_IDEAL_PROC = 0x0008, QUIC_EXECUTION_CONFIG_FLAG_HIGH_PRIORITY = 0x0010, + QUIC_EXECUTION_CONFIG_FLAG_AFFINITIZE = 0x0020, #endif } QUIC_EXECUTION_CONFIG_FLAGS; @@ -555,6 +557,8 @@ typedef struct QUIC_STATISTICS_V2 { uint32_t SendEcnCongestionCount; // Number of congestion events caused by ECN. + uint8_t HandshakeHopLimitTTL; // The TTL value in the initial packet of the handshake. + // N.B. New fields must be appended to end } QUIC_STATISTICS_V2; diff --git a/src/inc/msquic.hpp b/src/inc/msquic.hpp index 5b911abbf6..f051e84139 100644 --- a/src/inc/msquic.hpp +++ b/src/inc/msquic.hpp @@ -87,16 +87,6 @@ struct CxPlatLockDispatch { void Acquire() noexcept { CxPlatDispatchLockAcquire(&Handle); } void Release() noexcept { CxPlatDispatchLockRelease(&Handle); } }; - -struct CxPlatRwLockDispatch { - CXPLAT_DISPATCH_RW_LOCK Handle; - CxPlatRwLockDispatch() noexcept { CxPlatDispatchRwLockInitialize(&Handle); } - ~CxPlatRwLockDispatch() noexcept { CxPlatDispatchRwLockUninitialize(&Handle); } - void AcquireShared() noexcept { CxPlatDispatchRwLockAcquireShared(&Handle); } - void AcquireExclusive() noexcept { CxPlatDispatchRwLockAcquireExclusive(&Handle); } - void ReleaseShared() noexcept { CxPlatDispatchRwLockReleaseShared(&Handle); } - void ReleaseExclusive() noexcept { CxPlatDispatchRwLockReleaseExclusive(&Handle); } -}; #pragma warning(pop) struct CxPlatPool { diff --git a/src/inc/msquichelper.h b/src/inc/msquichelper.h index 4572ddbd3e..529c68413d 100644 --- a/src/inc/msquichelper.h +++ b/src/inc/msquichelper.h @@ -304,7 +304,7 @@ IsValue( _In_z_ const char* toTestAgainst ) { - return _strnicmp(name, toTestAgainst, CXPLAT_MIN(strlen(name), strlen(toTestAgainst))) == 0; + return _strnicmp(name, toTestAgainst, strlen(toTestAgainst)) == 0; } inline diff --git a/src/inc/quic_datapath.h b/src/inc/quic_datapath.h index a5dd6a335c..9975f762a1 100644 --- a/src/inc/quic_datapath.h +++ b/src/inc/quic_datapath.h @@ -222,6 +222,11 @@ typedef struct CXPLAT_RECV_DATA { // uint8_t TypeOfService; + // + // TTL Hoplimit field of the IP header of the received packet on handshake. + // + uint8_t HopLimitTTL; + // // Flags. // @@ -438,6 +443,7 @@ CxPlatDataPathUpdateConfig( #define CXPLAT_DATAPATH_FEATURE_PORT_RESERVATIONS 0x0010 #define CXPLAT_DATAPATH_FEATURE_TCP 0x0020 #define CXPLAT_DATAPATH_FEATURE_RAW 0x0040 +#define CXPLAT_DATAPATH_FEATURE_TTL 0x0080 // // Queries the currently supported features of the datapath. diff --git a/src/inc/quic_platform_posix.h b/src/inc/quic_platform_posix.h index afa718ebb1..9adc306306 100644 --- a/src/inc/quic_platform_posix.h +++ b/src/inc/quic_platform_posix.h @@ -417,13 +417,13 @@ typedef CXPLAT_RW_LOCK CXPLAT_DISPATCH_RW_LOCK; #define CxPlatDispatchRwLockUninitialize CxPlatRwLockUninitialize -#define CxPlatDispatchRwLockAcquireShared CxPlatRwLockAcquireShared +#define CxPlatDispatchRwLockAcquireShared(Lock, PrevIrql) CxPlatRwLockAcquireShared(Lock) -#define CxPlatDispatchRwLockAcquireExclusive CxPlatRwLockAcquireExclusive +#define CxPlatDispatchRwLockAcquireExclusive(Lock, PrevIrql) CxPlatRwLockAcquireExclusive(Lock) -#define CxPlatDispatchRwLockReleaseShared CxPlatRwLockReleaseShared +#define CxPlatDispatchRwLockReleaseShared(Lock, PrevIrql) CxPlatRwLockReleaseShared(Lock) -#define CxPlatDispatchRwLockReleaseExclusive CxPlatRwLockReleaseExclusive +#define CxPlatDispatchRwLockReleaseExclusive(Lock, PrevIrql) CxPlatRwLockReleaseExclusive(Lock) // // Represents a QUIC memory pool used for fixed sized allocations. diff --git a/src/inc/quic_platform_winkernel.h b/src/inc/quic_platform_winkernel.h index 3c3c4b9903..3314de5272 100644 --- a/src/inc/quic_platform_winkernel.h +++ b/src/inc/quic_platform_winkernel.h @@ -314,17 +314,14 @@ typedef EX_PUSH_LOCK CXPLAT_RW_LOCK; #define CxPlatRwLockReleaseShared(Lock) ExReleasePushLockShared(Lock); KeLeaveCriticalRegion() #define CxPlatRwLockReleaseExclusive(Lock) ExReleasePushLockExclusive(Lock); KeLeaveCriticalRegion() -typedef struct CXPLAT_DISPATCH_RW_LOCK { - EX_SPIN_LOCK SpinLock; - KIRQL PrevIrql; -} CXPLAT_DISPATCH_RW_LOCK; +typedef EX_SPIN_LOCK CXPLAT_DISPATCH_RW_LOCK; -#define CxPlatDispatchRwLockInitialize(Lock) (Lock)->SpinLock = 0 +#define CxPlatDispatchRwLockInitialize(Lock) *(Lock) = 0 #define CxPlatDispatchRwLockUninitialize(Lock) -#define CxPlatDispatchRwLockAcquireShared(Lock) (Lock)->PrevIrql = ExAcquireSpinLockShared(&(Lock)->SpinLock) -#define CxPlatDispatchRwLockAcquireExclusive(Lock) (Lock)->PrevIrql = ExAcquireSpinLockExclusive(&(Lock)->SpinLock) -#define CxPlatDispatchRwLockReleaseShared(Lock) ExReleaseSpinLockShared(&(Lock)->SpinLock, (Lock)->PrevIrql) -#define CxPlatDispatchRwLockReleaseExclusive(Lock) ExReleaseSpinLockExclusive(&(Lock)->SpinLock, (Lock)->PrevIrql) +#define CxPlatDispatchRwLockAcquireShared(Lock, PrevIrql) KIRQL PrevIrql = ExAcquireSpinLockShared(Lock) +#define CxPlatDispatchRwLockAcquireExclusive(Lock, PrevIrql) KIRQL PrevIrql = ExAcquireSpinLockExclusive(Lock) +#define CxPlatDispatchRwLockReleaseShared(Lock, PrevIrql) ExReleaseSpinLockShared(Lock, PrevIrql) +#define CxPlatDispatchRwLockReleaseExclusive(Lock, PrevIrql) ExReleaseSpinLockExclusive(Lock, PrevIrql) // // Reference Count Interface diff --git a/src/inc/quic_platform_winuser.h b/src/inc/quic_platform_winuser.h index 8295c64f43..52c47080b8 100644 --- a/src/inc/quic_platform_winuser.h +++ b/src/inc/quic_platform_winuser.h @@ -519,10 +519,10 @@ typedef SRWLOCK CXPLAT_DISPATCH_RW_LOCK; #define CxPlatDispatchRwLockInitialize(Lock) InitializeSRWLock(Lock) #define CxPlatDispatchRwLockUninitialize(Lock) -#define CxPlatDispatchRwLockAcquireShared(Lock) AcquireSRWLockShared(Lock) -#define CxPlatDispatchRwLockAcquireExclusive(Lock) AcquireSRWLockExclusive(Lock) -#define CxPlatDispatchRwLockReleaseShared(Lock) ReleaseSRWLockShared(Lock) -#define CxPlatDispatchRwLockReleaseExclusive(Lock) ReleaseSRWLockExclusive(Lock) +#define CxPlatDispatchRwLockAcquireShared(Lock, PrevIrql) AcquireSRWLockShared(Lock) +#define CxPlatDispatchRwLockAcquireExclusive(Lock, PrevIrql) AcquireSRWLockExclusive(Lock) +#define CxPlatDispatchRwLockReleaseShared(Lock, PrevIrql) ReleaseSRWLockShared(Lock) +#define CxPlatDispatchRwLockReleaseExclusive(Lock, PrevIrql) ReleaseSRWLockExclusive(Lock) // // Reference Count Interface diff --git a/src/lib.rs b/src/lib.rs index 4ba2a4629b..9a5e00b1d3 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,20 @@ use c_types::AF_INET; use c_types::AF_INET6; #[allow(unused_imports)] use c_types::AF_UNSPEC; +use c_types::{sa_family_t, sockaddr_in, sockaddr_in6, socklen_t}; use libc::c_void; use serde::{Deserialize, Serialize}; +use socket2::SockAddr; use std::convert::TryInto; use std::fmt; +use std::io; +use std::marker::PhantomData; +use std::mem; +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::option::Option; use std::ptr; +use std::result::Result; +use std::sync::Once; #[macro_use] extern crate bitfield; @@ -32,31 +40,13 @@ pub type BOOLEAN = ::std::os::raw::c_uchar; /// Family of an IP address. pub type AddressFamily = u16; +#[allow(clippy::unnecessary_cast)] pub const ADDRESS_FAMILY_UNSPEC: AddressFamily = c_types::AF_UNSPEC as u16; +#[allow(clippy::unnecessary_cast)] pub const ADDRESS_FAMILY_INET: AddressFamily = c_types::AF_INET as u16; +#[allow(clippy::unnecessary_cast)] pub const ADDRESS_FAMILY_INET6: AddressFamily = c_types::AF_INET6 as u16; -/// IPv4 address payload. -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct sockaddr_in { - pub family: AddressFamily, - pub port: u16, - pub addr: u32, - pub zero: [u8; 8usize], -} - -/// IPv6 address payload. -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct sockaddr_in6 { - pub family: AddressFamily, - pub port: u16, - pub flow_info: u32, - pub addr: [u8; 16usize], - pub scope_id: u32, -} - /// Generic representation of IPv4 or IPv6 addresses. #[repr(C)] #[derive(Copy, Clone)] @@ -66,40 +56,187 @@ pub union Addr { } impl Addr { - /// Create a representation of IPv4 address and perform Network byte order conversion - /// on the port number. - pub fn ipv4(family: u16, port: u16, addr: u32) -> Addr { - Addr { - ipv4: sockaddr_in { - family, - port: port, - addr, - zero: [0, 0, 0, 0, 0, 0, 0, 0], - }, + /// Converts the `Addr` to a `SocketAddr`. + pub fn as_socket(&self) -> Option { + unsafe { + SockAddr::try_init(|addr, len| { + if self.ipv4.sin_family == AF_INET as sa_family_t { + let addr = addr.cast::(); + *addr = self.ipv4; + *len = mem::size_of::() as socklen_t; + Ok(()) + } else if self.ipv4.sin_family == AF_INET6 as sa_family_t { + let addr = addr.cast::(); + *addr = self.ipv6; + *len = mem::size_of::() as socklen_t; + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::Other)) + } + }) } + .map(|((), addr)| addr.as_socket().unwrap()) + .ok() } - /// Create a representation of IPv6 address and perform Network byte order conversion - /// on the port number. - pub fn ipv6( - family: u16, - port: u16, - flow_info: u32, - addr: [u8; 16usize], - scope_id: u32, - ) -> Addr { - Addr { - ipv6: sockaddr_in6 { - family, - port: port, - flow_info, - addr, - scope_id, - }, + /// Get port number from the `Addr`. + pub fn port(&self) -> u16 { + unsafe { u16::from_be(self.ipv4.sin_port) } + } +} + +impl From for Addr { + fn from(addr: SocketAddr) -> Addr { + match addr { + SocketAddr::V4(addr) => addr.into(), + SocketAddr::V6(addr) => addr.into(), } } } +impl From for Addr { + fn from(addr: SocketAddrV4) -> Addr { + // SAFETY: a `Addr` of all zeros is valid. + let mut storage = unsafe { mem::zeroed::() }; + let addr: SockAddr = addr.into(); + let addr = addr.as_ptr().cast::(); + storage.ipv4 = unsafe { *addr }; + storage + } +} + +impl From for Addr { + fn from(addr: SocketAddrV6) -> Addr { + // SAFETY: a `Addr` of all zeros is valid. + let mut storage = unsafe { mem::zeroed::() }; + let addr: SockAddr = addr.into(); + let addr = addr.as_ptr().cast::(); + storage.ipv6 = unsafe { *addr }; + storage + } +} + +#[cfg(target_os = "windows")] +mod status { + pub const QUIC_STATUS_SUCCESS: u32 = 0x0; + pub const QUIC_STATUS_PENDING: u32 = 0x703e5; + pub const QUIC_STATUS_CONTINUE: u32 = 0x704de; + pub const QUIC_STATUS_OUT_OF_MEMORY: u32 = 0x8007000e; + pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 0x80070057; + pub const QUIC_STATUS_INVALID_STATE: u32 = 0x8007139f; + pub const QUIC_STATUS_NOT_SUPPORTED: u32 = 0x80004002; + pub const QUIC_STATUS_NOT_FOUND: u32 = 0x80070490; + pub const QUIC_STATUS_BUFFER_TOO_SMALL: u32 = 0x8007007a; + pub const QUIC_STATUS_HANDSHAKE_FAILURE: u32 = 0x80410000; + pub const QUIC_STATUS_ABORTED: u32 = 0x80004004; + pub const QUIC_STATUS_ADDRESS_IN_USE: u32 = 0x80072740; + pub const QUIC_STATUS_INVALID_ADDRESS: u32 = 0x80072741; + pub const QUIC_STATUS_CONNECTION_TIMEOUT: u32 = 0x80410006; + pub const QUIC_STATUS_CONNECTION_IDLE: u32 = 0x80410005; + pub const QUIC_STATUS_UNREACHABLE: u32 = 0x800704d0; + pub const QUIC_STATUS_INTERNAL_ERROR: u32 = 0x80410003; + pub const QUIC_STATUS_CONNECTION_REFUSED: u32 = 0x800704c9; + pub const QUIC_STATUS_PROTOCOL_ERROR: u32 = 0x80410004; + pub const QUIC_STATUS_VER_NEG_ERROR: u32 = 0x80410001; + pub const QUIC_STATUS_TLS_ERROR: u32 = 0x80072b18; + pub const QUIC_STATUS_USER_CANCELED: u32 = 0x80410002; + pub const QUIC_STATUS_ALPN_NEG_FAILURE: u32 = 0x80410007; + pub const QUIC_STATUS_STREAM_LIMIT_REACHED: u32 = 0x80410008; + pub const QUIC_STATUS_ALPN_IN_USE: u32 = 0x80410009; + pub const QUIC_STATUS_CLOSE_NOTIFY: u32 = 0x80410100; + pub const QUIC_STATUS_BAD_CERTIFICATE: u32 = 0x80410100 | 42; + pub const QUIC_STATUS_UNSUPPORTED_CERTIFICATE: u32 = 0x80410100 | 43; + pub const QUIC_STATUS_REVOKED_CERTIFICATE: u32 = 0x80410100 | 44; + pub const QUIC_STATUS_EXPIRED_CERTIFICATE: u32 = 0x80410100 | 45; + pub const QUIC_STATUS_UNKNOWN_CERTIFICATE: u32 = 0x80410100 | 46; + pub const QUIC_STATUS_REQUIRED_CERTIFICATE: u32 = 0x80410100 | 116; + pub const QUIC_STATUS_CERT_EXPIRED: u32 = 0x800B0101; + pub const QUIC_STATUS_CERT_UNTRUSTED_ROOT: u32 = 0x800B0109; + pub const QUIC_STATUS_CERT_NO_CERT: u32 = 0x8009030E; +} + +#[cfg(target_os = "linux")] +mod status { + pub const QUIC_STATUS_SUCCESS: u32 = 0; + pub const QUIC_STATUS_PENDING: u32 = 0xFFFFFFFE; // -2 + pub const QUIC_STATUS_CONTINUE: u32 = 0xFFFFFFFF; // -1 + pub const QUIC_STATUS_OUT_OF_MEMORY: u32 = 12; + pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 22; + pub const QUIC_STATUS_INVALID_STATE: u32 = 1; + pub const QUIC_STATUS_NOT_SUPPORTED: u32 = 95; + pub const QUIC_STATUS_NOT_FOUND: u32 = 2; + pub const QUIC_STATUS_BUFFER_TOO_SMALL: u32 = 75; + pub const QUIC_STATUS_HANDSHAKE_FAILURE: u32 = 103; + pub const QUIC_STATUS_ABORTED: u32 = 125; + pub const QUIC_STATUS_ADDRESS_IN_USE: u32 = 98; + pub const QUIC_STATUS_INVALID_ADDRESS: u32 = 97; + pub const QUIC_STATUS_CONNECTION_TIMEOUT: u32 = 110; + pub const QUIC_STATUS_CONNECTION_IDLE: u32 = 62; + pub const QUIC_STATUS_INTERNAL_ERROR: u32 = 5; + pub const QUIC_STATUS_CONNECTION_REFUSED: u32 = 111; + pub const QUIC_STATUS_PROTOCOL_ERROR: u32 = 71; + pub const QUIC_STATUS_VER_NEG_ERROR: u32 = 93; + pub const QUIC_STATUS_UNREACHABLE: u32 = 113; + pub const QUIC_STATUS_TLS_ERROR: u32 = 126; + pub const QUIC_STATUS_USER_CANCELED: u32 = 130; + pub const QUIC_STATUS_ALPN_NEG_FAILURE: u32 = 92; + pub const QUIC_STATUS_STREAM_LIMIT_REACHED: u32 = 86; + pub const QUIC_STATUS_ALPN_IN_USE: u32 = 91; + pub const QUIC_STATUS_ADDRESS_NOT_AVAILABLE: u32 = 99; + pub const QUIC_STATUS_CLOSE_NOTIFY: u32 = 0xBEBC300; + pub const QUIC_STATUS_BAD_CERTIFICATE: u32 = 0xBEBC32A; + pub const QUIC_STATUS_UNSUPPORTED_CERTIFICATE: u32 = 0xBEBC32B; + pub const QUIC_STATUS_REVOKED_CERTIFICATE: u32 = 0xBEBC32C; + pub const QUIC_STATUS_EXPIRED_CERTIFICATE: u32 = 0xBEBC32D; + pub const QUIC_STATUS_UNKNOWN_CERTIFICATE: u32 = 0xBEBC32E; + pub const QUIC_STATUS_REQUIRED_CERTIFICATE: u32 = 0xBEBC374; + pub const QUIC_STATUS_CERT_EXPIRED: u32 = 0xBEBC401; + pub const QUIC_STATUS_CERT_UNTRUSTED_ROOT: u32 = 0xBEBC402; + pub const QUIC_STATUS_CERT_NO_CERT: u32 = 0xBEBC403; +} + +#[cfg(target_os = "macos")] +mod status { + pub const QUIC_STATUS_SUCCESS: u32 = 0; + pub const QUIC_STATUS_PENDING: u32 = 0xFFFFFFFE; // -2 + pub const QUIC_STATUS_CONTINUE: u32 = 0xFFFFFFFF; // -1 + pub const QUIC_STATUS_OUT_OF_MEMORY: u32 = 12; + pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 22; + pub const QUIC_STATUS_INVALID_STATE: u32 = 1; + pub const QUIC_STATUS_NOT_SUPPORTED: u32 = 102; + pub const QUIC_STATUS_NOT_FOUND: u32 = 2; + pub const QUIC_STATUS_BUFFER_TOO_SMALL: u32 = 84; + pub const QUIC_STATUS_HANDSHAKE_FAILURE: u32 = 53; + pub const QUIC_STATUS_ABORTED: u32 = 89; + pub const QUIC_STATUS_ADDRESS_IN_USE: u32 = 48; + pub const QUIC_STATUS_INVALID_ADDRESS: u32 = 47; + pub const QUIC_STATUS_CONNECTION_TIMEOUT: u32 = 60; + pub const QUIC_STATUS_CONNECTION_IDLE: u32 = 101; + pub const QUIC_STATUS_INTERNAL_ERROR: u32 = 5; + pub const QUIC_STATUS_CONNECTION_REFUSED: u32 = 61; + pub const QUIC_STATUS_PROTOCOL_ERROR: u32 = 100; + pub const QUIC_STATUS_VER_NEG_ERROR: u32 = 43; + pub const QUIC_STATUS_UNREACHABLE: u32 = 65; + pub const QUIC_STATUS_TLS_ERROR: u32 = 126; + pub const QUIC_STATUS_USER_CANCELED: u32 = 105; + pub const QUIC_STATUS_ALPN_NEG_FAILURE: u32 = 42; + pub const QUIC_STATUS_STREAM_LIMIT_REACHED: u32 = 86; + pub const QUIC_STATUS_ALPN_IN_USE: u32 = 41; + pub const QUIC_STATUS_ADDRESS_NOT_AVAILABLE: u32 = 47; + pub const QUIC_STATUS_CLOSE_NOTIFY: u32 = 0xBEBC300; + pub const QUIC_STATUS_BAD_CERTIFICATE: u32 = 0xBEBC32A; + pub const QUIC_STATUS_UNSUPPORTED_CERTIFICATE: u32 = 0xBEBC32B; + pub const QUIC_STATUS_REVOKED_CERTIFICATE: u32 = 0xBEBC32C; + pub const QUIC_STATUS_EXPIRED_CERTIFICATE: u32 = 0xBEBC32D; + pub const QUIC_STATUS_UNKNOWN_CERTIFICATE: u32 = 0xBEBC32E; + pub const QUIC_STATUS_REQUIRED_CERTIFICATE: u32 = 0xBEBC374; + pub const QUIC_STATUS_CERT_EXPIRED: u32 = 0xBEBC401; + pub const QUIC_STATUS_CERT_UNTRUSTED_ROOT: u32 = 0xBEBC402; + pub const QUIC_STATUS_CERT_NO_CERT: u32 = 0xBEBC403; +} + +pub use status::*; + /// Helper for processing MsQuic return statuses. pub struct Status {} @@ -129,7 +266,7 @@ impl Status { /// The different possible TLS providers used by MsQuic. pub type TlsProvider = u32; pub const TLS_PROVIDER_SCHANNEL: TlsProvider = 0; -pub const TLS_PROVIDER_OPENSSL : TlsProvider = 1; +pub const TLS_PROVIDER_OPENSSL: TlsProvider = 1; /// Configures how to process a registration's workload. pub type ExecutionProfile = u32; @@ -658,7 +795,7 @@ pub struct QuicTlsSecrets { } #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct Settings { pub is_set_flags: u64, pub max_bytes_per_key: u64, @@ -689,6 +826,7 @@ pub struct Settings { pub mtu_operations_per_drain: u8, pub mtu_discovery_missing_probe_count: u8, pub dest_cid_update_idle_timeout_ms: u32, + pub other2_flags: u64, } pub const PARAM_GLOBAL_RETRY_MEMORY_PERCENT: u32 = 0x01000000; @@ -773,19 +911,35 @@ pub const PARAM_STREAM_PRIORITY: u32 = 0x08000003; pub type ListenerEventType = u32; pub const LISTENER_EVENT_NEW_CONNECTION: ListenerEventType = 0; +pub const LISTENER_EVENT_STOP_COMPLETE: ListenerEventType = 1; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ListenerEventNewConnection { pub info: *const NewConnectionInfo, pub connection: Handle, - pub new_negotiated_alpn: *const u8, +} + +bitfield! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + pub struct ListenerEventStopCompleteBitfields(u8); + // The fields default to u8 + pub app_close_in_progress, _: 0, 0; + _reserved, _: 7, 1; +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ListenerEventStopComplete { + pub bit_flags: ListenerEventStopCompleteBitfields, } #[repr(C)] #[derive(Copy, Clone)] pub union ListenerEventPayload { pub new_connection: ListenerEventNewConnection, + pub stop_complete: ListenerEventStopComplete, } #[repr(C)] @@ -837,10 +991,33 @@ pub struct ConnectionEventConnectionShutdownByPeer { pub error_code: u62, } +bitfield! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + pub struct ConnectionEventShutdownCompleteBitfields(BOOLEAN); + // The fields default to BOOLEAN + pub handshake_completed, _: 0, 0; + pub peer_acknowledged_shutdown, _: 1, 1; + pub app_close_in_progress, _: 2, 2; + _reserved, _: 7, 3; +} + #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct ConnectionEventShutdownComplete { - pub _bitfield: BOOLEAN, + pub bit_flags: ConnectionEventShutdownCompleteBitfields, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventLocalAddressChanged { + pub address: *const Addr, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventPeerAddressChanged { + pub address: *const Addr, } #[repr(C)] @@ -850,6 +1027,33 @@ pub struct ConnectionEventPeerStreamStarted { pub flags: StreamOpenFlags, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventStreamsAvailable { + pub bidirectional_count: u16, + pub unidirectional_count: u16, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventPeerNeedsStreams { + pub bidirectional: BOOLEAN, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventIdealProcessorChanged { + pub ideal_processor: u16, + pub partition_index: u16, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventDatagramStateChanged { + pub send_enabled: BOOLEAN, + pub max_send_length: u16, +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ConnectionEventDatagramReceived { @@ -864,6 +1068,13 @@ pub struct ConnectionEventDatagramSendStateChanged { pub state: DatagramSendState, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventResumed { + pub resumption_state_length: u16, + pub resumption_state: *const u8, +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ConnectionEventResumptionTicketReceived { @@ -871,6 +1082,15 @@ pub struct ConnectionEventResumptionTicketReceived { pub resumption_ticket: *const u8, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventPeerCertificateReceived { + pub certificate: *const Certificate, + pub deferred_error_flags: u32, + pub deferred_status: u32, + pub chain: *const CertificateChain, +} + #[repr(C)] #[derive(Copy, Clone)] pub union ConnectionEventPayload { @@ -878,17 +1098,18 @@ pub union ConnectionEventPayload { pub shutdown_initiated_by_transport: ConnectionEventConnectionShutdownByTransport, pub shutdown_initiated_by_peer: ConnectionEventConnectionShutdownByPeer, pub shutdown_complete: ConnectionEventShutdownComplete, - //pub local_address_changed: ConnectionEventLocalAddressChanged, - //pub peer_address_changed: ConnectionEventPeerAddressChanged, + pub local_address_changed: ConnectionEventLocalAddressChanged, + pub peer_address_changed: ConnectionEventPeerAddressChanged, pub peer_stream_started: ConnectionEventPeerStreamStarted, - //pub streams_available: ConnectionEventStreamsAvailable, - //pub ideal_processor_changed: ConnectionEventIdealProcessorChanged, - //pub datagram_state_changed: ConnectionEventDatagramStateChanged, + pub streams_available: ConnectionEventStreamsAvailable, + pub peer_needs_streams: ConnectionEventPeerNeedsStreams, + pub ideal_processor_changed: ConnectionEventIdealProcessorChanged, + pub datagram_state_changed: ConnectionEventDatagramStateChanged, pub datagram_received: ConnectionEventDatagramReceived, pub datagram_send_state_changed: ConnectionEventDatagramSendStateChanged, - //pub resumed: ConnectionEventResumed, + pub resumed: ConnectionEventResumed, pub resumption_ticket_received: ConnectionEventResumptionTicketReceived, - //pub peer_certificated_received: ConnectionEventPeerCertificateReceived, + pub peer_certificated_received: ConnectionEventPeerCertificateReceived, } #[repr(C)] @@ -912,13 +1133,23 @@ pub const STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: StreamEventType = 6; pub const STREAM_EVENT_SHUTDOWN_COMPLETE: StreamEventType = 7; pub const STREAM_EVENT_IDEAL_SEND_BUFFER_SIZE: StreamEventType = 8; pub const STREAM_EVENT_PEER_ACCEPTED: StreamEventType = 9; +pub const STREAM_EVENT_CANCEL_ON_LOSS: StreamEventType = 10; + +bitfield! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + pub struct StreamEventStartCompleteBitfields(u8); + // The fields default to u8 + pub peer_accepted, _: 0, 0; + _reserved, _: 7, 1; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct StreamEventStartComplete { - pub status: u64, + pub status: u32, pub id: u62, - pub bit_flags: u8, + pub bit_flags: StreamEventStartCompleteBitfields, } #[repr(C)] @@ -941,13 +1172,13 @@ pub struct StreamEventSendComplete { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct StreamEventPeerSendAborted { - pub error_code: u64, + pub error_code: u62, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct StreamEventPeerReceiveAborted { - pub error_code: u64, + pub error_code: u62, } #[repr(C)] @@ -956,21 +1187,24 @@ pub struct StreamEventSendShutdownComplete { pub graceful: bool, } - bitfield! { #[repr(C)] #[derive(Clone, Copy)] - struct StreamEventShutdownCompleteBitfields(u8); + pub struct StreamEventShutdownCompleteBitfields(u8); // The fields default to u8 - app_close_in_progress, _: 1, 0; - _reserved, _: 7, 1; + pub app_close_in_progress, _: 0, 0; + pub conn_shutdown_by_app, _: 1, 1; + pub conn_closed_remotely, _: 2, 2; + _reserved, _: 7, 3; } #[repr(C)] #[derive(Copy, Clone)] pub struct StreamEventShutdownComplete { - connection_shutdown: bool, - flags: StreamEventShutdownCompleteBitfields + pub connection_shutdown: bool, + pub bit_flags: StreamEventShutdownCompleteBitfields, + pub connection_error_code: u62, + pub connection_close_status: u32, } #[repr(C)] @@ -979,6 +1213,12 @@ pub struct StreamEventIdealSendBufferSize { pub byte_count: u64, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct StreamEventCancelOnLoss { + pub error_code: u62, +} + #[repr(C)] #[derive(Copy, Clone)] pub union StreamEventPayload { @@ -990,6 +1230,7 @@ pub union StreamEventPayload { pub send_shutdown_complete: StreamEventSendShutdownComplete, pub shutdown_complete: StreamEventShutdownComplete, pub ideal_send_buffer_size: StreamEventIdealSendBufferSize, + pub cancel_on_loss: StreamEventCancelOnLoss, } #[repr(C)] @@ -1095,15 +1336,10 @@ struct ApiTable { flags: SendFlags, client_send_context: *const c_void, ) -> u32, - resumption_ticket_validation_complete: extern "C" fn( - connection: Handle, - result: BOOLEAN, - ) -> u32, - certificate_validation_complete: extern "C" fn( - connection: Handle, - result: BOOLEAN, - tls_alert: TlsAlertCode - ) -> u32, + resumption_ticket_validation_complete: + extern "C" fn(connection: Handle, result: BOOLEAN) -> u32, + certificate_validation_complete: + extern "C" fn(connection: Handle, result: BOOLEAN, tls_alert: TlsAlertCode) -> u32, } #[link(name = "msquic")] @@ -1116,44 +1352,51 @@ extern "C" { // The following starts the "nice" Rust API wrapper on the C interop layer. // -/// Top level entry point for the MsQuic API. -/// -/// Developper must ensure a struct containing MsQuic members such as `Connection` -/// or `Stream` declares `API` last so that the API is dropped last when the containing -/// sruct goes out of scope. +// +// APITABLE will be initialized via MsQuicOpenVersion() when we first initialize Api or Registration. +// +static mut APITABLE: *const ApiTable = ptr::null(); +static START_MSQUIC: Once = Once::new(); + +/// Entry point for some global MsQuic APIs. pub struct Api { - table: *const ApiTable, + marker: PhantomData<()>, } /// The execution context for processing connections on the application's behalf. pub struct Registration { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Registration {} +unsafe impl Send for Registration {} /// Specifies how to configure a connection. pub struct Configuration { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Configuration {} +unsafe impl Send for Configuration {} /// A single QUIC connection. pub struct Connection { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Connection {} +unsafe impl Send for Connection {} /// A single server listener pub struct Listener { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Listener {} +unsafe impl Send for Listener {} /// A single QUIC stream on a parent connection. pub struct Stream { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Stream {} +unsafe impl Send for Stream {} impl From<&str> for Buffer { fn from(data: &str) -> Buffer { @@ -1175,11 +1418,10 @@ impl From<&Vec> for Buffer { impl From<&[u8]> for Buffer { fn from(data: &[u8]) -> Buffer { - let buffer = Buffer { + Buffer { length: data.len() as u32, buffer: data.as_ptr() as *mut u8, - }; - buffer + } } } @@ -1200,38 +1442,8 @@ impl QuicPerformance { } impl Settings { - pub fn new() -> Settings { - Settings { - is_set_flags: 0, - max_bytes_per_key: 0, - handshake_idle_timeout_ms: 0, - idle_timeout_ms: 0, - mtu_discovery_search_complete_timeout_us: 0, - tls_client_max_send_buffer: 0, - tls_server_max_send_buffer: 0, - stream_recv_window_default: 0, - stream_recv_buffer_default: 0, - conn_flow_control_window: 0, - max_worker_queue_delay_us: 0, - max_stateless_operations: 0, - initial_window_packets: 0, - send_idle_timeout_ms: 0, - initiall_rtt_ms: 0, - max_ack_delay_ms: 0, - disconnect_timeout_ms: 0, - keep_alive_interval_ms: 0, - congestion_control_algorithm: 0, - peer_bidi_stream_count: 0, - peer_unidi_stream_count: 0, - max_binding_stateless_operations: 0, - stateless_operation_expiration_ms: 0, - minimum_mtu: 0, - maximum_mtu: 0, - other_flags: 0, - mtu_operations_per_drain: 0, - mtu_discovery_missing_probe_count: 0, - dest_cid_update_idle_timeout_ms: 0, - } + pub fn new() -> Self { + Self::default() } pub fn set_peer_bidi_stream_count(&mut self, value: u16) -> &mut Settings { self.is_set_flags |= 0x40000; @@ -1253,6 +1465,12 @@ impl Settings { self.other_flags |= (value as u8) << 3; self } + #[cfg(feature = "preview-api")] + pub fn set_stream_multi_receive_enabled(&mut self, value: bool) -> &mut Settings { + self.is_set_flags |= 1 << 42; + self.other2_flags |= (value as u64) << 5; + self + } } impl CredentialConfig { @@ -1271,29 +1489,43 @@ impl CredentialConfig { } } +impl Default for Api { + fn default() -> Self { + Self::new() + } +} + impl Api { - pub fn new() -> Api { - let new_table: *const ApiTable = ptr::null(); - let status = unsafe { MsQuicOpenVersion(2, &new_table) }; - if Status::failed(status) { - panic!("MsQuicOpenVersion failure 0x{:x}", status); + pub fn new() -> Self { + // We initialize APITABLE at only once. + unsafe { + START_MSQUIC.call_once(|| { + let table: *const ApiTable = ptr::null(); + let status = MsQuicOpenVersion(2, &table); + if Status::failed(status) { + panic!("Failed to open MsQuic: {}", status); + } + APITABLE = table; + }); + } + Self { + marker: PhantomData, } - Api { table: new_table } } pub fn close_listener(&self, listener: Handle) { unsafe { - ((*self.table).listener_close)(listener); + ((*APITABLE).listener_close)(listener); } } pub fn close_connection(&self, connection: Handle) { unsafe { - ((*self.table).connection_close)(connection); + ((*APITABLE).connection_close)(connection); } } pub fn close_stream(&self, stream: Handle) { unsafe { - ((*self.table).stream_close)(stream); + ((*APITABLE).stream_close)(stream); } } @@ -1303,7 +1535,7 @@ impl Api { }; let perf_length = std::mem::size_of::<[i64; PERF_COUNTER_MAX as usize]>() as u32; unsafe { - ((*self.table).get_param)( + ((*APITABLE).get_param)( std::ptr::null(), PARAM_GLOBAL_PERF_COUNTERS, (&perf_length) as *const u32 as *mut u32, @@ -1319,37 +1551,51 @@ impl Api { handler: *const c_void, context: *const c_void, ) { - unsafe { ((*self.table).set_callback_handler)(handle, handler, context) } + unsafe { ((*APITABLE).set_callback_handler)(handle, handler, context) } } } -impl Drop for Api { - fn drop(&mut self) { - unsafe { MsQuicClose(self.table) }; +#[ctor::dtor] +fn close_msquic() { + unsafe { + if !APITABLE.is_null() { + MsQuicClose(APITABLE); + APITABLE = ptr::null(); + } } } impl Registration { - pub fn new(api: &Api, config: *const RegistrationConfig) -> Registration { + pub fn new(config: *const RegistrationConfig) -> Result { + // We initialize APITABLE at only once. + unsafe { + START_MSQUIC.call_once(|| { + let table: *const ApiTable = ptr::null(); + let status = MsQuicOpenVersion(2, &table); + if Status::failed(status) { + panic!("Failed to open MsQuic: {}", status); + } + APITABLE = table; + }); + } let new_registration: Handle = ptr::null(); - let status = unsafe { ((*api.table).registration_open)(config, &new_registration) }; + let status = unsafe { ((*APITABLE).registration_open)(config, &new_registration) }; if Status::failed(status) { - panic!("RegistrationOpen failure 0x{:x}", status); + return Err(status); } - Registration { - table: api.table, + Ok(Registration { handle: new_registration, - } + }) } pub fn shutdown(&self) { - unsafe { ((*self.table).registration_shutdown)(self.handle) } + unsafe { ((*APITABLE).registration_shutdown)(self.handle) } } } impl Drop for Registration { fn drop(&mut self) { - unsafe { ((*self.table).registration_close)(self.handle) }; + unsafe { ((*APITABLE).registration_close)(self.handle) }; } } @@ -1358,15 +1604,15 @@ impl Configuration { registration: &Registration, alpn: &[Buffer], settings: *const Settings, - ) -> Configuration { + ) -> Result { let context: *const c_void = ptr::null(); let new_configuration: Handle = ptr::null(); let mut settings_size: u32 = 0; - if settings != ptr::null() { + if !settings.is_null() { settings_size = ::std::mem::size_of::() as u32; } let status = unsafe { - ((*registration.table).configuration_open)( + ((*APITABLE).configuration_open)( registration.handle, alpn.as_ptr(), alpn.len() as u32, @@ -1377,42 +1623,44 @@ impl Configuration { ) }; if Status::failed(status) { - panic!("ConfigurationOpen failure 0x{:x}", status); + return Err(status); } - Configuration { - table: registration.table, + Ok(Configuration { handle: new_configuration, - } + }) } - pub fn load_credential(&self, cred_config: &CredentialConfig) { + pub fn load_credential(&self, cred_config: &CredentialConfig) -> Result<(), u32> { let status = - unsafe { ((*self.table).configuration_load_credential)(self.handle, *&cred_config) }; + unsafe { ((*APITABLE).configuration_load_credential)(self.handle, cred_config) }; if Status::failed(status) { - panic!("ConfigurationLoadCredential failure 0x{:x}", status); + return Err(status); } + Ok(()) } } impl Drop for Configuration { fn drop(&mut self) { - unsafe { ((*self.table).configuration_close)(self.handle) }; + unsafe { ((*APITABLE).configuration_close)(self.handle) }; + } +} + +impl Default for Connection { + fn default() -> Self { + Self::new() } } impl Connection { - pub fn new(registration: &Registration) -> Connection { + pub fn new() -> Connection { Connection { - table: registration.table, handle: ptr::null(), } } - pub fn from_parts(handle: Handle, api: &Api) -> Connection { - Connection { - table: api.table, - handle, - } + pub fn from_parts(handle: Handle) -> Connection { + Connection { handle } } pub fn open( @@ -1420,50 +1668,57 @@ impl Connection { registration: &Registration, handler: ConnectionEventHandler, context: *const c_void, - ) { + ) -> Result<(), u32> { let status = unsafe { - ((*self.table).connection_open)(registration.handle, handler, context, &self.handle) + ((*APITABLE).connection_open)(registration.handle, handler, context, &self.handle) }; if Status::failed(status) { - panic!("ConnectionOpen failure 0x{:x}", status); + return Err(status); } + Ok(()) } - pub fn start(&self, configuration: &Configuration, server_name: &str, server_port: u16) { + pub fn start( + &self, + configuration: &Configuration, + server_name: &str, + server_port: u16, + ) -> Result<(), u32> { let server_name_safe = std::ffi::CString::new(server_name).unwrap(); let status = unsafe { - ((*self.table).connection_start)( + ((*APITABLE).connection_start)( self.handle, configuration.handle, 0, - server_name_safe.as_ptr() as *const i8, + server_name_safe.as_ptr(), server_port, ) }; if Status::failed(status) { - panic!("ConnectionStart failure 0x{:x}", status); + return Err(status); } + Ok(()) } pub fn close(&self) { unsafe { - ((*self.table).connection_close)(self.handle); + ((*APITABLE).connection_close)(self.handle); } } pub fn shutdown(&self, flags: ConnectionShutdownFlags, error_code: u62) { unsafe { - ((*self.table).connection_shutdown)(self.handle, flags, error_code); + ((*APITABLE).connection_shutdown)(self.handle, flags, error_code); } } pub fn set_param(&self, param: u32, buffer_length: u32, buffer: *const c_void) -> u32 { - unsafe { ((*self.table).set_param)(self.handle, param, buffer_length, buffer) } + unsafe { ((*APITABLE).set_param)(self.handle, param, buffer_length, buffer) } } pub fn stream_close(&self, stream: Handle) { unsafe { - ((*self.table).stream_close)(stream); + ((*APITABLE).stream_close)(stream); } } @@ -1472,7 +1727,7 @@ impl Connection { [0; std::mem::size_of::()]; let stat_size_mut = std::mem::size_of::(); unsafe { - ((*self.table).get_param)( + ((*APITABLE).get_param)( self.handle, PARAM_CONN_STATISTICS, (&stat_size_mut) as *const usize as *const u32 as *mut u32, @@ -1488,7 +1743,7 @@ impl Connection { [0; std::mem::size_of::()]; let stat_size_mut = std::mem::size_of::(); unsafe { - ((*self.table).get_param)( + ((*APITABLE).get_param)( self.handle, PARAM_CONN_STATISTICS_V2, (&stat_size_mut) as *const usize as *const u32 as *mut u32, @@ -1499,18 +1754,19 @@ impl Connection { unsafe { *(stat_buffer.as_ptr() as *const c_void as *const QuicStatisticsV2) } } - pub fn set_configuration(&self, configuration: &Configuration) { + pub fn set_configuration(&self, configuration: &Configuration) -> Result<(), u32> { let status = unsafe { - ((*self.table).connection_set_configuration)(self.handle, configuration.handle) + ((*APITABLE).connection_set_configuration)(self.handle, configuration.handle) }; if Status::failed(status) { - panic!("ConnectionSetConfiguration failure 0x{:x}", status); + return Err(status); } + Ok(()) } pub fn set_callback_handler(&self, handler: ConnectionEventHandler, context: *const c_void) { unsafe { - ((*self.table).set_callback_handler)(self.handle, handler as *const c_void, context) + ((*APITABLE).set_callback_handler)(self.handle, handler as *const c_void, context) }; } @@ -1521,7 +1777,7 @@ impl Connection { context: *const c_void, ) { unsafe { - ((*self.table).set_callback_handler)(stream_handle, handler as *const c_void, context) + ((*APITABLE).set_callback_handler)(stream_handle, handler as *const c_void, context) }; } @@ -1531,126 +1787,182 @@ impl Connection { buffer_count: u32, flags: SendFlags, client_send_context: *const c_void, - ) { + ) -> Result<(), u32> { let status = unsafe { - ((*self.table).datagram_send)( + ((*APITABLE).datagram_send)( self.handle, - *&buffer, + buffer, buffer_count, flags, client_send_context, ) }; if Status::failed(status) { - panic!("DatagramSend failure 0x{:x}", status); + return Err(status); + } + Ok(()) + } + + pub fn resumption_ticket_validation_complete(&self, result: BOOLEAN) -> Result<(), u32> { + let status = + unsafe { ((*APITABLE).resumption_ticket_validation_complete)(self.handle, result) }; + if Status::failed(status) { + return Err(status); } + Ok(()) } - pub fn resumption_ticket_validation_complete( + pub fn certificate_validation_complete( &self, result: BOOLEAN, - ) { + tls_alert: TlsAlertCode, + ) -> Result<(), u32> { let status = unsafe { - ((*self.table).resumption_ticket_validation_complete)( + ((*APITABLE).certificate_validation_complete)(self.handle, result, tls_alert) + }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } + + pub fn get_local_addr(&self) -> Result { + let mut addr_buffer: [u8; mem::size_of::()] = [0; mem::size_of::()]; + let addr_size_mut = mem::size_of::(); + let status = unsafe { + ((*APITABLE).get_param)( self.handle, - result, + PARAM_CONN_LOCAL_ADDRESS, + (&addr_size_mut) as *const usize as *const u32 as *mut u32, + addr_buffer.as_mut_ptr() as *const c_void, ) }; if Status::failed(status) { - panic!("ticket validation completion failure 0x{:x}", status); + return Err(status); } + Ok(unsafe { *(addr_buffer.as_ptr() as *const c_void as *const Addr) }) } - pub fn certificate_validation_complete( - &self, - result: BOOLEAN, - tls_alert: TlsAlertCode, - ) { + pub fn get_remote_addr(&self) -> Result { + let mut addr_buffer: [u8; mem::size_of::()] = [0; mem::size_of::()]; + let addr_size_mut = mem::size_of::(); let status = unsafe { - ((*self.table).certificate_validation_complete)( + ((*APITABLE).get_param)( self.handle, - result, - tls_alert, + PARAM_CONN_REMOTE_ADDRESS, + (&addr_size_mut) as *const usize as *const u32 as *mut u32, + addr_buffer.as_mut_ptr() as *const c_void, ) }; if Status::failed(status) { - panic!("ticket validation completion failure 0x{:x}", status); + return Err(status); } + Ok(unsafe { *(addr_buffer.as_ptr() as *const c_void as *const Addr) }) } } impl Drop for Connection { fn drop(&mut self) { - unsafe { ((*self.table).connection_close)(self.handle) }; + unsafe { ((*APITABLE).connection_close)(self.handle) }; + } +} + +impl Default for Listener { + fn default() -> Self { + Self::new() } } impl Listener { - pub fn new( + pub fn new() -> Listener { + Listener { + handle: ptr::null(), + } + } + + pub fn open( + &self, registration: &Registration, handler: ListenerEventHandler, context: *const c_void, - ) -> Listener { - let new_listener: Handle = ptr::null(); + ) -> Result<(), u32> { let status = unsafe { - ((*registration.table).listener_open)( - registration.handle, - handler, - context, - &new_listener, + ((*APITABLE).listener_open)(registration.handle, handler, context, &self.handle) + }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } + + pub fn start(&self, alpn: &[Buffer], local_address: Option<&Addr>) -> Result<(), u32> { + let status = unsafe { + ((*APITABLE).listener_start)( + self.handle, + alpn.as_ptr(), + alpn.len() as u32, + local_address + .map(|addr| addr as *const _) + .unwrap_or(ptr::null()), ) }; if Status::failed(status) { - panic!("ListenerOpen failed, {:x}!\n", status); + return Err(status); } + Ok(()) + } - Listener { - table: registration.table, - handle: new_listener, + pub fn stop(&self) { + unsafe { + ((*APITABLE).listener_stop)(self.handle); } } - pub fn start(&self, alpn: &[Buffer], local_address: &Addr) { + pub fn get_local_addr(&self) -> Result { + let mut addr_buffer: [u8; mem::size_of::()] = [0; mem::size_of::()]; + let addr_size_mut = mem::size_of::(); let status = unsafe { - ((*self.table).listener_start)( + ((*APITABLE).get_param)( self.handle, - alpn.as_ptr(), - alpn.len() as u32, - *&local_address, + PARAM_LISTENER_LOCAL_ADDRESS, + (&addr_size_mut) as *const usize as *const u32 as *mut u32, + addr_buffer.as_mut_ptr() as *const c_void, ) }; if Status::failed(status) { - panic!("ListenerStart failed, {:x}!\n", status); + return Err(status); } + Ok(unsafe { *(addr_buffer.as_ptr() as *const c_void as *const Addr) }) } pub fn close(&self) { unsafe { - ((*self.table).listener_close)(self.handle); + ((*APITABLE).listener_close)(self.handle); } } } impl Drop for Listener { fn drop(&mut self) { - unsafe { ((*self.table).listener_close)(self.handle) }; + unsafe { ((*APITABLE).listener_close)(self.handle) }; + } +} + +impl Default for Stream { + fn default() -> Self { + Self::new() } } impl Stream { - pub fn new(context: *const c_void) -> Stream { - let api = unsafe { &*(context as *const Api) }; + pub fn new() -> Stream { Stream { - table: api.table, handle: ptr::null(), } } - pub fn from_parts(handle: Handle, api: &Api) -> Stream { - Stream { - table: api.table, - handle, - } + pub fn from_parts(handle: Handle) -> Stream { + Stream { handle } } pub fn open( @@ -1659,25 +1971,35 @@ impl Stream { flags: StreamOpenFlags, handler: StreamEventHandler, context: *const c_void, - ) { + ) -> Result<(), u32> { let status = unsafe { - ((*self.table).stream_open)(connection.handle, flags, handler, context, &self.handle) + ((*APITABLE).stream_open)(connection.handle, flags, handler, context, &self.handle) }; if Status::failed(status) { - panic!("StreamOpen failure 0x{:x}", status); + return Err(status); + } + Ok(()) + } + + pub fn start(&self, flags: StreamStartFlags) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).stream_start)(self.handle, flags) }; + if Status::failed(status) { + return Err(status); } + Ok(()) } - pub fn start(&self, flags: StreamStartFlags) { - let status = unsafe { ((*self.table).stream_start)(self.handle, flags) }; + pub fn shutdown(&self, flags: StreamShutdownFlags, error_code: u62) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).stream_shutdown)(self.handle, flags, error_code) }; if Status::failed(status) { - panic!("StreamStart failure 0x{:x}", status); + return Err(status); } + Ok(()) } pub fn close(&self) { unsafe { - ((*self.table).stream_close)(self.handle); + ((*APITABLE).stream_close)(self.handle); } } @@ -1687,31 +2009,53 @@ impl Stream { buffer_count: u32, flags: SendFlags, client_send_context: *const c_void, - ) { + ) -> Result<(), u32> { let status = unsafe { - ((*self.table).stream_send)( + ((*APITABLE).stream_send)( self.handle, - *&buffer, + buffer, buffer_count, flags, client_send_context, //(self as *const Stream) as *const c_void, ) }; if Status::failed(status) { - panic!("StreamSend failure 0x{:x}", status); + return Err(status); } + Ok(()) } pub fn set_callback_handler(&self, handler: StreamEventHandler, context: *const c_void) { unsafe { - ((*self.table).set_callback_handler)(self.handle, handler as *const c_void, context) + ((*APITABLE).set_callback_handler)(self.handle, handler as *const c_void, context) }; } + + pub fn get_param( + &self, + param: u32, + buffer_length: *mut u32, + buffer: *const c_void, + ) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).get_param)(self.handle, param, buffer_length, buffer) }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } + + pub fn receive_complete(&self, buffer_length: u64) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).stream_receive_complete)(self.handle, buffer_length) }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } } impl Drop for Stream { fn drop(&mut self) { - unsafe { ((*self.table).stream_close)(self.handle) }; + unsafe { ((*APITABLE).stream_close)(self.handle) }; } } @@ -1727,7 +2071,11 @@ extern "C" fn test_conn_callback( ) -> u32 { let connection = unsafe { &*(context as *const Connection) }; match event.event_type { - CONNECTION_EVENT_CONNECTED => println!("Connected"), + CONNECTION_EVENT_CONNECTED => { + let local_addr = connection.get_local_addr().unwrap().as_socket().unwrap(); + let remote_addr = connection.get_remote_addr().unwrap().as_socket().unwrap(); + println!("Connected({}, {})", local_addr, remote_addr); + } CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT => { println!("Transport shutdown 0x{:x}", unsafe { event.payload.shutdown_initiated_by_transport.status @@ -1782,27 +2130,61 @@ extern "C" fn test_stream_callback( #[test] fn test_module() { - let api = Api::new(); - let registration = Registration::new(&api, ptr::null()); + let res = Registration::new(ptr::null()); + assert!( + res.is_ok(), + "Failed to open registration: 0x{:x}", + res.err().unwrap() + ); + let registration = res.unwrap(); let alpn = [Buffer::from("h3")]; - let configuration = Configuration::new( + let res = Configuration::new( ®istration, &alpn, + #[cfg(feature = "preview-api")] + Settings::new() + .set_peer_bidi_stream_count(100) + .set_peer_unidi_stream_count(3) + .set_stream_multi_receive_enabled(true), + #[cfg(not(feature = "preview-api"))] Settings::new() .set_peer_bidi_stream_count(100) .set_peer_unidi_stream_count(3), ); + assert!( + res.is_ok(), + "Failed to open configuration: 0x{:x}", + res.err().unwrap() + ); + let configuration = res.unwrap(); + let cred_config = CredentialConfig::new_client(); - configuration.load_credential(&cred_config); + let res = configuration.load_credential(&cred_config); + assert!( + res.is_ok(), + "Failed to load credential: 0x{:x}", + res.err().unwrap() + ); - let connection = Connection::new(®istration); - connection.open( + let connection = Connection::new(); + let res = connection.open( ®istration, test_conn_callback, &connection as *const Connection as *const c_void, ); - connection.start(&configuration, "www.cloudflare.com", 443); + assert!( + res.is_ok(), + "Failed to open connection: 0x{:x}", + res.err().unwrap() + ); + + let res = connection.start(&configuration, "www.cloudflare.com", 443); + assert!( + res.is_ok(), + "Failed to start connection: 0x{:x}", + res.err().unwrap() + ); let duration = std::time::Duration::from_millis(1000); std::thread::sleep(duration); diff --git a/src/manifest/clog.sidecar b/src/manifest/clog.sidecar index eacefe88b5..907d055946 100644 --- a/src/manifest/clog.sidecar +++ b/src/manifest/clog.sidecar @@ -4306,7 +4306,7 @@ }, "FrameLogAckFrequency": { "ModuleProperites": {}, - "TraceString": "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu PktTolerance:%llu MaxAckDelay:%llu IgnoreOrder:%hhu IgnoreCE:%hhu", + "TraceString": "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu AckElicitThreshold:%llu MaxAckDelay:%llu ReorderThreshold:%llu", "UniqueId": "FrameLogAckFrequency", "splitArgs": [ { @@ -4334,12 +4334,8 @@ "MacroVariableName": "arg7" }, { - "DefinationEncoding": "hhu", + "DefinationEncoding": "llu", "MacroVariableName": "arg8" - }, - { - "DefinationEncoding": "hhu", - "MacroVariableName": "arg9" } ], "macroName": "QuicTraceLogVerbose" @@ -14709,9 +14705,9 @@ "EncodingString": "[%c][%cX][%llu] ECN [Invalid]" }, { - "UniquenessHash": "7470e68a-8ad6-563a-4957-76dc22e5deeb", + "UniquenessHash": "92b3c109-06f5-8316-5792-593a28f376c3", "TraceID": "FrameLogAckFrequency", - "EncodingString": "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu PktTolerance:%llu MaxAckDelay:%llu IgnoreOrder:%hhu IgnoreCE:%hhu" + "EncodingString": "[%c][%cX][%llu] ACK_FREQUENCY SeqNum:%llu AckElicitThreshold:%llu MaxAckDelay:%llu ReorderThreshold:%llu" }, { "UniquenessHash": "262b3f95-1062-851d-c2d8-c46867da793b", diff --git a/src/perf/bin/CMakeLists.txt b/src/perf/bin/CMakeLists.txt index 788f15bc7b..86fa457c66 100644 --- a/src/perf/bin/CMakeLists.txt +++ b/src/perf/bin/CMakeLists.txt @@ -12,7 +12,7 @@ set_property(TARGET secnetperf PROPERTY FOLDER "${QUIC_FOLDER_PREFIX}perf") target_link_libraries(secnetperf inc warnings perflib msquic) if (BUILD_SHARED_LIBS) - target_link_libraries(secnetperf platform) + target_link_libraries(secnetperf msquic_platform) endif() target_link_libraries(secnetperf logging base_link) diff --git a/src/perf/lib/PerfClient.cpp b/src/perf/lib/PerfClient.cpp index 6cd808fe3a..1b6542be87 100644 --- a/src/perf/lib/PerfClient.cpp +++ b/src/perf/lib/PerfClient.cpp @@ -108,6 +108,52 @@ PerfClient::Init( CountMult[0] = CxPlatProcCount(); + // + // Scenario profile sets new defauls for values below, that may then be + // further overridden by command line arguments. + // + const char* ScenarioStr = GetValue(argc, argv, "scenario"); + if (ScenarioStr != nullptr) { + if (IsValue(ScenarioStr, "upload")) { + Upload = S_TO_US(12); // 12 seconds + Timed = TRUE; + PrintThroughput = TRUE; + } else if (IsValue(ScenarioStr, "download")) { + Download = S_TO_US(12); // 12 seconds + Timed = TRUE; + PrintThroughput = TRUE; + } else if (IsValue(ScenarioStr, "hps")) { + ConnectionCount = 16 * CxPlatProcCount(); + RunTime = S_TO_US(12); // 12 seconds + RepeatConnections = TRUE; + PrintIoRate = TRUE; + } else if (IsValue(ScenarioStr, "rps-multi")) { + Upload = 512; + Download = 4000; + ConnectionCount = 16 * CxPlatProcCount(); + StreamCount = 100; + RunTime = S_TO_US(20); // 20 seconds + RepeatStreams = TRUE; + PrintLatency = TRUE; + } else if (IsValue(ScenarioStr, "rps")) { + Upload = 512; + Download = 4000; + StreamCount = 100; + RunTime = S_TO_US(20); // 20 seconds + RepeatStreams = TRUE; + PrintLatency = TRUE; + } else if (IsValue(ScenarioStr, "latency")) { + Upload = 512; + Download = 4000; + RunTime = S_TO_US(20); // 20 seconds + RepeatStreams = TRUE; + PrintLatency = TRUE; + } else { + WriteOutput("Failed to parse scenario profile[%s]!\n", ScenarioStr); + return QUIC_STATUS_INVALID_PARAMETER; + } + } + // // Remote target/server options // @@ -146,7 +192,6 @@ PerfClient::Init( WorkerCount = CxPlatProcCount(); TryGetVariableUnitValue(argc, argv, "threads", &WorkerCount); TryGetVariableUnitValue(argc, argv, "workers", &WorkerCount); - TryGetValue(argc, argv, "affinitize", &AffinitizeWorkers); #ifdef QUIC_COMPARTMENT_ID TryGetValue(argc, argv, "comp", &CompartmentId); @@ -326,7 +371,7 @@ PerfClient::Start( // Configure and start all the workers. // uint16_t ThreadFlags = - AffinitizeWorkers ? + PerfDefaultAffinitizeThreads ? (uint16_t)CXPLAT_THREAD_FLAG_SET_AFFINITIZE : (uint16_t)CXPLAT_THREAD_FLAG_SET_IDEAL_PROC; if (PerfDefaultHighPriority) { diff --git a/src/perf/lib/PerfClient.h b/src/perf/lib/PerfClient.h index 3522365683..f9fdd9bc2a 100644 --- a/src/perf/lib/PerfClient.h +++ b/src/perf/lib/PerfClient.h @@ -179,7 +179,6 @@ struct PerfClient { uint8_t IncrementTarget {TRUE}; // Local execution parameters uint32_t WorkerCount; - uint8_t AffinitizeWorkers {FALSE}; uint8_t SpecificLocalAddresses {FALSE}; #ifdef QUIC_COMPARTMENT_ID uint16_t CompartmentId {UINT16_MAX}; diff --git a/src/perf/lib/SecNetPerf.h b/src/perf/lib/SecNetPerf.h index 9590503fda..22f1d4f4da 100644 --- a/src/perf/lib/SecNetPerf.h +++ b/src/perf/lib/SecNetPerf.h @@ -53,6 +53,7 @@ extern QUIC_CONGESTION_CONTROL_ALGORITHM PerfDefaultCongestionControl; extern uint8_t PerfDefaultEcnEnabled; extern uint8_t PerfDefaultQeoAllowed; extern uint8_t PerfDefaultHighPriority; +extern uint8_t PerfDefaultAffinitizeThreads; extern CXPLAT_DATAPATH* Datapath; diff --git a/src/perf/lib/SecNetPerfMain.cpp b/src/perf/lib/SecNetPerfMain.cpp index 3ba2f5486f..feb48269ed 100644 --- a/src/perf/lib/SecNetPerfMain.cpp +++ b/src/perf/lib/SecNetPerfMain.cpp @@ -28,10 +28,41 @@ QUIC_CONGESTION_CONTROL_ALGORITHM PerfDefaultCongestionControl = QUIC_CONGESTION uint8_t PerfDefaultEcnEnabled = false; uint8_t PerfDefaultQeoAllowed = false; uint8_t PerfDefaultHighPriority = false; +uint8_t PerfDefaultAffinitizeThreads = false; #ifdef _KERNEL_MODE volatile int BufferCurrent; char Buffer[BufferLength]; +static inline LONG _strtol(const CHAR* nptr, CHAR** endptr, int base) { + UNREFERENCED_PARAMETER(base); + ULONG temp; + RtlCharToInteger(nptr, base, &temp); + if (endptr != NULL) { + const CHAR* ptr = nptr; + while (*ptr >= '0' && *ptr <= '9') { + ptr++; + } + *endptr = (CHAR*)ptr; + } + return (LONG)temp; +} + +static inline ULONG _strtoul(const CHAR* nptr, CHAR** endptr, int base) { + UNREFERENCED_PARAMETER(base); + ULONG temp; + RtlCharToInteger(nptr, base, &temp); + if (endptr != NULL) { + const CHAR* ptr = nptr; + while (*ptr >= '0' && *ptr <= '9') { + ptr++; + } + *endptr = (CHAR*)ptr; + } + return temp; +} +#else +#define _strtol strtol +#define _strtoul strtoul #endif static @@ -77,6 +108,8 @@ PrintHelp( " -platency<0/1> Print latency statistics. (def:0)\n" "\n" " Scenario options:\n" + " -scenario: Scenario profile to use.\n" + " - {upload, download, hps, rps, rps-multi, latency}.\n" " -conns:<####> The number of connections to use. (def:1)\n" " -streams:<####> The number of streams to send on at a time. (def:0)\n" " -upload:<####>[unit] The length of bytes to send on each stream, with an optional (time or length) unit. (def:0)\n" @@ -95,13 +128,16 @@ PrintHelp( " -pollidle: Amount of time to poll while idle before sleeping (default: 0).\n" " -ecn:<0/1> Enables/disables sender-side ECN support. (def:0)\n" " -qeo:<0/1> Allows/disallowes QUIC encryption offload. (def:0)\n" -#ifndef _KERNEL_MODE +#ifdef _KERNEL_MODE " -io: Configures a requested network IO model to be used.\n" " - {iocp, rio, xdp, qtip, wsk, epoll, kqueue}\n" +#else + " -io: Configures a requested network IO model to be used.\n" + " - {xdp}\n" +#endif // _KERNEL_MODE " -cpu: Specify the processor(s) to use.\n" " -cipher: Decimal value of 1 or more QUIC_ALLOWED_CIPHER_SUITE_FLAGS.\n" " -highpri:<0/1> Configures MsQuic to run threads at high priority. (def:0)\n" -#endif // _KERNEL_MODE "\n", PERF_DEFAULT_PORT, PERF_DEFAULT_PORT @@ -141,10 +177,9 @@ QuicMainStart( QUIC_EXECUTION_CONFIG* Config = (QUIC_EXECUTION_CONFIG*)RawConfig; Config->PollingIdleTimeoutUs = 0; // Default to no polling. bool SetConfig = false; - -#ifndef _KERNEL_MODE const char* IoMode = GetValue(argc, argv, "io"); +#ifndef _KERNEL_MODE if (IoMode && IsValue(IoMode, "qtip")) { Config->Flags |= QUIC_EXECUTION_CONFIG_FLAG_QTIP; SetConfig = true; @@ -155,6 +190,8 @@ QuicMainStart( SetConfig = true; } +#endif // _KERNEL_MODE + if (IoMode && IsValue(IoMode, "xdp")) { Config->Flags |= QUIC_EXECUTION_CONFIG_FLAG_XDP; SetConfig = true; @@ -163,7 +200,7 @@ QuicMainStart( const char* CpuStr; if ((CpuStr = GetValue(argc, argv, "cpu")) != nullptr) { SetConfig = true; - if (strtol(CpuStr, nullptr, 10) == -1) { + if (_strtol(CpuStr, nullptr, 10) == -1) { for (uint32_t i = 0; i < CxPlatProcCount() && Config->ProcessorCount < 256; ++i) { Config->ProcessorList[Config->ProcessorCount++] = (uint16_t)i; } @@ -171,7 +208,7 @@ QuicMainStart( do { if (*CpuStr == ',') CpuStr++; Config->ProcessorList[Config->ProcessorCount++] = - (uint16_t)strtoul(CpuStr, (char**)&CpuStr, 10); + (uint16_t)_strtoul(CpuStr, (char**)&CpuStr, 10); } while (*CpuStr && Config->ProcessorCount < 256); } } @@ -181,7 +218,12 @@ QuicMainStart( Config->Flags |= QUIC_EXECUTION_CONFIG_FLAG_HIGH_PRIORITY; SetConfig = true; } -#endif // _KERNEL_MODE + + TryGetValue(argc, argv, "affinitize", &PerfDefaultAffinitizeThreads); + if (PerfDefaultHighPriority) { + Config->Flags |= QUIC_EXECUTION_CONFIG_FLAG_AFFINITIZE; + SetConfig = true; + } if (TryGetValue(argc, argv, "pollidle", &Config->PollingIdleTimeoutUs)) { SetConfig = true; @@ -199,6 +241,25 @@ QuicMainStart( return Status; } + const char* ScenarioStr = GetValue(argc, argv, "scenario"); + if (ScenarioStr != nullptr) { + if (IsValue(ScenarioStr, "upload") || + IsValue(ScenarioStr, "download") || + IsValue(ScenarioStr, "hps")) { + PerfDefaultExecutionProfile = QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT; + TcpDefaultExecutionProfile = TCP_EXECUTION_PROFILE_MAX_THROUGHPUT; + } else if ( + IsValue(ScenarioStr, "rps") || + IsValue(ScenarioStr, "rps-multi") || + IsValue(ScenarioStr, "latency")) { + PerfDefaultExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY; + TcpDefaultExecutionProfile = TCP_EXECUTION_PROFILE_LOW_LATENCY; + } else { + WriteOutput("Failed to parse scenario profile[%s]!\n", ScenarioStr); + return QUIC_STATUS_INVALID_PARAMETER; + } + } + const char* ExecStr = GetValue(argc, argv, "exec"); if (ExecStr != nullptr) { if (IsValue(ExecStr, "lowlat")) { @@ -212,7 +273,8 @@ QuicMainStart( } else if (IsValue(ExecStr, "realtime")) { PerfDefaultExecutionProfile = QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME; } else { - WriteOutput("Failed to parse execution profile[%s], use lowlat as default for QUIC, lowlat as default for TCP.\n", ExecStr); + WriteOutput("Failed to parse execution profile[%s]!\n", ExecStr); + return QUIC_STATUS_INVALID_PARAMETER; } } diff --git a/src/platform/CMakeLists.txt b/src/platform/CMakeLists.txt index eca936787c..997906d0cd 100644 --- a/src/platform/CMakeLists.txt +++ b/src/platform/CMakeLists.txt @@ -59,21 +59,21 @@ else() message(FATAL_ERROR "TLS Provider not configured") endif() -add_library(platform STATIC ${SOURCES}) +add_library(msquic_platform STATIC ${SOURCES}) if("${CX_PLATFORM}" STREQUAL "windows") target_link_libraries( - platform + msquic_platform PUBLIC wbemuuid) - target_link_libraries(platform PUBLIC winmm) + target_link_libraries(msquic_platform PUBLIC winmm) elseif(QUIC_LINUX_XDP_ENABLED) find_library(NL_LIB nl-3) find_library(NL_ROUTE_LIB nl-route-3) find_library(XDP_LIB libxdp.so) find_library(BPF_LIB libbpf.so) - target_include_directories(platform PRIVATE /usr/include/xdp) - target_include_directories(platform PRIVATE /usr/include/bpf) + target_include_directories(msquic_platform PRIVATE /usr/include/xdp) + target_include_directories(msquic_platform PRIVATE /usr/include/bpf) set(XDP_PROG_INCLUDE_DIR "-I/usr/include/bpf") # building XDP program @@ -88,7 +88,7 @@ elseif(QUIC_LINUX_XDP_ENABLED) DEPENDS ${PROJECT_SOURCE_DIR}/src/platform/datapath_raw_xdp_linux_kern.c ) add_custom_target(xdp_program DEPENDS ${QUIC_OUTPUT_DIR}/datapath_raw_xdp_kern.o) - add_dependencies(platform xdp_program) + add_dependencies(msquic_platform xdp_program) if (NOT BUILD_SHARED_LIBS) find_library(ELF_LIB elf) # for static @@ -103,34 +103,37 @@ elseif(QUIC_LINUX_XDP_ENABLED) string(REPLACE ".so" ".a" BPF_LIB ${BPF_LIB}) endif() - target_link_libraries(platform PUBLIC ${XDP_LIB} ${BPF_LIB} ${NL_LIB} ${NL_ROUTE_LIB} ${ELF_LIB} ${Z_LIB} ${ZSTD_LIB}) + target_link_libraries(msquic_platform PUBLIC ${XDP_LIB} ${BPF_LIB} ${NL_LIB} ${NL_ROUTE_LIB} ${ELF_LIB} ${Z_LIB} ${ZSTD_LIB}) endif() -target_link_libraries(platform PUBLIC inc) -target_link_libraries(platform PRIVATE warnings main_binary_link_args) +target_link_libraries(msquic_platform PUBLIC inc) +target_link_libraries(msquic_platform PRIVATE warnings main_binary_link_args) -set_property(TARGET platform PROPERTY FOLDER "${QUIC_FOLDER_PREFIX}libraries") +set_property(TARGET msquic_platform PROPERTY FOLDER "${QUIC_FOLDER_PREFIX}libraries") if ("${CX_PLATFORM}" STREQUAL "windows") target_include_directories( - platform + msquic_platform PRIVATE ${EXTRA_PLATFORM_INCLUDE_DIRECTORIES} ${PROJECT_SOURCE_DIR}/submodules/xdp-for-windows/published/external) elseif(QUIC_LINUX_XDP_ENABLED) include_directories(/usr/include/libnl3) - target_include_directories(platform PRIVATE ${EXTRA_PLATFORM_INCLUDE_DIRECTORIES}) + target_include_directories(msquic_platform PRIVATE ${EXTRA_PLATFORM_INCLUDE_DIRECTORIES}) endif() if (MSVC AND (QUIC_TLS STREQUAL "openssl" OR QUIC_TLS STREQUAL "schannel") AND NOT QUIC_ENABLE_SANITIZERS) - target_compile_options(platform PRIVATE /analyze) + target_compile_options(msquic_platform PRIVATE /analyze) endif() if(QUIC_TLS STREQUAL "openssl" OR QUIC_TLS STREQUAL "openssl3") - target_link_libraries(platform PUBLIC OpenSSL) + target_link_libraries(msquic_platform PUBLIC OpenSSL) if (CX_PLATFORM STREQUAL "darwin") - target_link_libraries(platform PUBLIC "-framework CoreFoundation" "-framework Security") + target_link_libraries(msquic_platform PUBLIC "-framework CoreFoundation" "-framework Security") endif() elseif(QUIC_TLS STREQUAL "schannel") - target_link_libraries(platform PUBLIC secur32 onecore) + target_link_libraries(msquic_platform PUBLIC secur32) + if (NOT QUIC_GAMECORE_BUILD) + target_link_libraries(msquic_platform PUBLIC onecore) + endif() endif() diff --git a/src/platform/certificates_capi.c b/src/platform/certificates_capi.c index 9fb6fa0b43..8c9a21b151 100644 --- a/src/platform/certificates_capi.c +++ b/src/platform/certificates_capi.c @@ -90,6 +90,9 @@ CxPlatCertVerifyRawCertificate( if (CredFlags & QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY) { CertFlags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; } + if (CredFlags & QUIC_CREDENTIAL_FLAG_DISABLE_AIA) { + CertFlags |= CERT_CHAIN_DISABLE_AIA; + } Result = CxPlatCertValidateChain( diff --git a/src/platform/datapath_epoll.c b/src/platform/datapath_epoll.c index b02152376e..8c95ae8fac 100644 --- a/src/platform/datapath_epoll.c +++ b/src/platform/datapath_epoll.c @@ -122,16 +122,6 @@ typedef struct CXPLAT_SEND_DATA { // QUIC_BUFFER ClientBuffer; - // - // The total buffer size for iovecs. - // - uint32_t TotalSize; - - // - // The send segmentation size the app asked for. - // - uint16_t SegmentSize; - // // Total number of packet buffers allocated (and iovecs used if !GSO). // @@ -199,8 +189,9 @@ typedef struct CXPLAT_SEND_DATA { } CXPLAT_SEND_DATA; typedef struct CXPLAT_RECV_MSG_CONTROL_BUFFER { - char Data[CMSG_SPACE(sizeof(struct in6_pktinfo)) + - 2 * CMSG_SPACE(sizeof(int))]; + char Data[CMSG_SPACE(sizeof(struct in6_pktinfo)) + // IP_PKTINFO + 2 * CMSG_SPACE(sizeof(int)) // TOS + + CMSG_SPACE(sizeof(int))]; // IP_TTL } CXPLAT_RECV_MSG_CONTROL_BUFFER; #ifdef DEBUG @@ -344,6 +335,10 @@ CxPlatDataPathCalculateFeatureSupport( } Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TCP; + // + // TTL should always be available / enabled on Linux. + // + Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TTL; } void @@ -853,6 +848,52 @@ CxPlatSocketContextInitialize( goto Exit; } + // + // TTL should always be available / enabled on Linux. + // + + // + // On Linux, IP_HOPLIMIT does not exist. So we will use IP_RECVTTL, IPV6_RECVHOPLIMIT instead. + // + Option = TRUE; + Result = + setsockopt( + SocketContext->SocketFd, + IPPROTO_IP, + IP_RECVTTL, + (const void*)&Option, + sizeof(Option)); + if (Result == SOCKET_ERROR) { + Status = errno; + QuicTraceEvent( + DatapathErrorStatus, + "[data][%p] ERROR, %u, %s.", + Binding, + Status, + "setsockopt(IP_RECVTTL) failed"); + goto Exit; + } + + Option = TRUE; + Result = + setsockopt( + SocketContext->SocketFd, + IPPROTO_IPV6, + IPV6_RECVHOPLIMIT, + (const void*)&Option, + sizeof(Option)); + if (Result == SOCKET_ERROR) { + Status = errno; + QuicTraceEvent( + DatapathErrorStatus, + "[data][%p] ERROR, %u, %s.", + Binding, + Status, + "setsockopt(IPV6_RECVHOPLIMIT) failed"); + goto Exit; + } + + #ifdef UDP_GRO if (SocketContext->DatapathPartition->Datapath->Features & CXPLAT_DATAPATH_FEATURE_RECV_COALESCING) { Option = TRUE; @@ -1782,8 +1823,9 @@ CxPlatSocketContextRecvComplete( BytesTransferred += RecvMsgHdr[CurrentMessage].msg_len; uint8_t TOS = 0; + int HopLimitTTL = 0; uint16_t SegmentLength = 0; - BOOLEAN FoundLocalAddr = FALSE, FoundTOS = FALSE; + BOOLEAN FoundLocalAddr = FALSE, FoundTOS = FALSE, FoundTTL = FALSE; QUIC_ADDR* LocalAddr = &IoBlock->Route.LocalAddress; QUIC_ADDR* RemoteAddr = &IoBlock->Route.RemoteAddress; CxPlatConvertFromMappedV6(RemoteAddr, RemoteAddr); @@ -1808,6 +1850,11 @@ CxPlatSocketContextRecvComplete( CXPLAT_DBG_ASSERT_CMSG(CMsg, uint8_t); TOS = *(uint8_t*)CMSG_DATA(CMsg); FoundTOS = TRUE; + } else if (CMsg->cmsg_type == IPV6_HOPLIMIT) { + HopLimitTTL = *CMSG_DATA(CMsg); + CXPLAT_DBG_ASSERT(HopLimitTTL < 256); + CXPLAT_DBG_ASSERT(HopLimitTTL > 0); + FoundTTL = TRUE; } else { CXPLAT_DBG_ASSERT(FALSE); } @@ -1816,6 +1863,11 @@ CxPlatSocketContextRecvComplete( CXPLAT_DBG_ASSERT_CMSG(CMsg, uint8_t); TOS = *(uint8_t*)CMSG_DATA(CMsg); FoundTOS = TRUE; + } else if (CMsg->cmsg_type == IP_TTL) { + HopLimitTTL = *CMSG_DATA(CMsg); + CXPLAT_DBG_ASSERT(HopLimitTTL < 256); + CXPLAT_DBG_ASSERT(HopLimitTTL > 0); + FoundTTL = TRUE; } else { CXPLAT_DBG_ASSERT(FALSE); } @@ -1833,6 +1885,10 @@ CxPlatSocketContextRecvComplete( CXPLAT_FRE_ASSERT(FoundLocalAddr); CXPLAT_FRE_ASSERT(FoundTOS); + // + // TTL should always be available/enabled on Linux. + // + CXPLAT_FRE_ASSERT(FoundTTL); QuicTraceEvent( DatapathRecv, @@ -1872,8 +1928,9 @@ CxPlatSocketContextRecvComplete( } RecvData->PartitionIndex = SocketContext->DatapathPartition->PartitionIndex; RecvData->TypeOfService = TOS; + RecvData->HopLimitTTL = (uint8_t)HopLimitTTL; RecvData->Allocated = TRUE; - RecvData->Route->DatapathType = RecvData->DatapathType = CXPLAT_DATAPATH_TYPE_USER; + RecvData->Route->DatapathType = RecvData->DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; RecvData->QueuedOnConnection = FALSE; RecvData->Reserved = FALSE; @@ -2121,7 +2178,7 @@ CxPlatSocketReceiveTcpData( Data->PartitionIndex = SocketContext->DatapathPartition->PartitionIndex; Data->TypeOfService = 0; Data->Allocated = TRUE; - Data->Route->DatapathType = Data->DatapathType = CXPLAT_DATAPATH_TYPE_USER; + Data->Route->DatapathType = Data->DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; Data->QueuedOnConnection = FALSE; IoBlock->RefCount++; IoBlock = NULL; @@ -2213,7 +2270,7 @@ SendDataAlloc( !!(Socket->Datapath->Features & CXPLAT_DATAPATH_FEATURE_SEND_SEGMENTATION); SendData->Iovs[0].iov_len = 0; SendData->Iovs[0].iov_base = SendData->Buffer; - SendData->DatapathType = Config->Route->DatapathType = CXPLAT_DATAPATH_TYPE_USER; + SendData->DatapathType = Config->Route->DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; } return SendData; @@ -2257,6 +2314,7 @@ CxPlatSendDataFinalizeSendBuffer( IoVec->iov_base = SendData->ClientBuffer.Buffer; IoVec->iov_len = SendData->ClientBuffer.Length; if (SendData->TotalSize + SendData->SegmentSize > sizeof(SendData->Buffer) || + SendData->TotalSize + SendData->ClientBuffer.Length > sizeof(SendData->Buffer) || SendData->BufferCount == SendData->SocketContext->DatapathPartition->Datapath->SendIoVecCount) { SendData->ClientBuffer.Buffer = NULL; } else { @@ -2447,7 +2505,7 @@ CxPlatSendDataPopulateAncillaryData( } #ifdef UDP_SEGMENT - if (SendData->SegmentationSupported && SendData->SegmentSize > 0) { + if (SendData->SegmentationSupported && SendData->SegmentSize > 0 && Mhdr->msg_iov->iov_len > SendData->SegmentSize) { Mhdr->msg_controllen += CMSG_SPACE(sizeof(uint16_t)); CMsg = CXPLAT_CMSG_NXTHDR(CMsg); CMsg->cmsg_level = SOL_UDP; diff --git a/src/platform/datapath_kqueue.c b/src/platform/datapath_kqueue.c index 12aabccb68..87efcf0428 100644 --- a/src/platform/datapath_kqueue.c +++ b/src/platform/datapath_kqueue.c @@ -83,6 +83,8 @@ typedef struct DATAPATH_RX_IO_BLOCK { // typedef struct CXPLAT_SEND_DATA { + CXPLAT_SEND_DATA_COMMON; + // // The proc context owning this send context. // @@ -108,16 +110,6 @@ typedef struct CXPLAT_SEND_DATA { // CXPLAT_LIST_ENTRY PendingSendLinkage; - // - // The total buffer size for Buffers. - // - uint32_t TotalSize; - - // - // The type of ECN markings needed for send. - // - CXPLAT_ECN_TYPE ECN; - // // Total number of Buffers currently in use. // @@ -143,11 +135,6 @@ typedef struct CXPLAT_SEND_DATA { // struct iovec Iovs[CXPLAT_MAX_BATCH_SEND]; - // - // The send segmentation size; zero if segmentation is not performed. - // - uint16_t SegmentSize; - } CXPLAT_SEND_DATA; typedef struct CXPLAT_DATAPATH_PARTITION CXPLAT_DATAPATH_PARTITION; @@ -577,6 +564,9 @@ CxPlatDataPathGetSupportedFeatures( _In_ CXPLAT_DATAPATH* Datapath ) { + // + // Intentionally not enabling Feature_TTL on MacOS for now. + // return Datapath->Features; } @@ -1127,6 +1117,7 @@ CxPlatSocketContextRecvComplete( RecvPacket->Route->Queue = SocketContext; RecvPacket->TypeOfService = 0; + RecvPacket->HopLimitTTL = 0; // TODO: We are not supporting this on MacOS (yet) unless there's a business need. struct cmsghdr *CMsg; for (CMsg = CMSG_FIRSTHDR(&SocketContext->RecvMsgHdr); diff --git a/src/platform/datapath_raw.c b/src/platform/datapath_raw.c index f9b827eafd..f74785e7c6 100644 --- a/src/platform/datapath_raw.c +++ b/src/platform/datapath_raw.c @@ -150,7 +150,10 @@ RawDataPathGetSupportedFeatures( ) { UNREFERENCED_PARAMETER(Datapath); - return CXPLAT_DATAPATH_FEATURE_RAW; + // + // TTL should always be available / enabled for XDP. + // + return CXPLAT_DATAPATH_FEATURE_RAW | CXPLAT_DATAPATH_FEATURE_TTL; } _IRQL_requires_max_(DISPATCH_LEVEL) @@ -189,7 +192,7 @@ RawSocketDelete( { CxPlatDpRawPlumbRulesOnSocket(Socket, FALSE); CxPlatRemoveSocket(&Socket->RawDatapath->SocketPool, Socket); - CxPlatRundownReleaseAndWait(&Socket->Rundown); + CxPlatRundownReleaseAndWait(&Socket->RawRundown); if (Socket->PausedTcpSend) { CxPlatDpRawTxFree(Socket->PausedTcpSend); } @@ -269,7 +272,7 @@ CxPlatDpRawRxEthernet( CxPlatDpRawRxFree(PacketChain); } - CxPlatRundownRelease(&Socket->Rundown); + CxPlatRundownRelease(&Socket->RawRundown); } else { CxPlatDpRawRxFree(PacketChain); } diff --git a/src/platform/datapath_raw.h b/src/platform/datapath_raw.h index c23d63d449..f54adc03a0 100644 --- a/src/platform/datapath_raw.h +++ b/src/platform/datapath_raw.h @@ -260,7 +260,7 @@ CxPlatDpRawTxEnqueue( typedef struct CXPLAT_SOCKET_RAW { CXPLAT_HASHTABLE_ENTRY Entry; - CXPLAT_RUNDOWN_REF Rundown; + CXPLAT_RUNDOWN_REF RawRundown; CXPLAT_DATAPATH_RAW* RawDatapath; SOCKET AuxSocket; BOOLEAN Wildcard; // Using a wildcard local address. Optimization @@ -392,43 +392,6 @@ CxPlatFramingWriteHeaders( #pragma pack(push) #pragma pack(1) -typedef struct ETHERNET_HEADER { - uint8_t Destination[6]; - uint8_t Source[6]; - uint16_t Type; - uint8_t Data[0]; -} ETHERNET_HEADER; - -typedef struct IPV4_HEADER { - uint8_t VersionAndHeaderLength; - union { - uint8_t TypeOfServiceAndEcnField; - struct { - uint8_t EcnField : 2; - uint8_t TypeOfService : 6; - }; - }; - uint16_t TotalLength; - uint16_t Identification; - uint16_t FlagsAndFragmentOffset; - uint8_t TimeToLive; - uint8_t Protocol; - uint16_t HeaderChecksum; - uint8_t Source[4]; - uint8_t Destination[4]; - uint8_t Data[0]; -} IPV4_HEADER; - -typedef struct IPV6_HEADER { - uint32_t VersionClassEcnFlow; - uint16_t PayloadLength; - uint8_t NextHeader; - uint8_t HopLimit; - uint8_t Source[16]; - uint8_t Destination[16]; - uint8_t Data[0]; -} IPV6_HEADER; - typedef struct IPV6_EXTENSION { uint8_t NextHeader; uint8_t Length; @@ -474,11 +437,52 @@ typedef struct TCP_HEADER { #define TH_CWR 0x80 #define IPV4_VERSION 4 -#define IPV6_VERSION 6 #define IPV4_VERSION_BYTE (IPV4_VERSION << 4) -#define IPV4_DEFAULT_VERHLEN ((IPV4_VERSION_BYTE) | (sizeof(IPV4_HEADER) / sizeof(uint32_t))) #define IP_DEFAULT_HOP_LIMIT 128 +#ifndef _KERNEL_MODE +typedef struct ETHERNET_HEADER { + uint8_t Destination[6]; + uint8_t Source[6]; + uint16_t Type; + uint8_t Data[0]; +} ETHERNET_HEADER; + +typedef struct IPV4_HEADER { + uint8_t VersionAndHeaderLength; + union { + uint8_t TypeOfServiceAndEcnField; + struct { + uint8_t EcnField : 2; + uint8_t TypeOfService : 6; + }; + }; + uint16_t TotalLength; + uint16_t Identification; + uint16_t FlagsAndFragmentOffset; + uint8_t TimeToLive; + uint8_t Protocol; + uint16_t HeaderChecksum; + uint8_t Source[4]; + uint8_t Destination[4]; + uint8_t Data[0]; +} IPV4_HEADER; + +typedef struct IPV6_HEADER { + uint32_t VersionClassEcnFlow; + uint16_t PayloadLength; + uint8_t NextHeader; + uint8_t HopLimit; + uint8_t Source[16]; + uint8_t Destination[16]; + uint8_t Data[0]; +} IPV6_HEADER; + +#define IPV6_VERSION 6 +#define IPV4_DEFAULT_VERHLEN ((IPV4_VERSION_BYTE) | (sizeof(IPV4_HEADER) / sizeof(uint32_t))) + #define ETHERNET_TYPE_IPV4 0x0008 #define ETHERNET_TYPE_IPV6 0xdd86 + +#endif diff --git a/src/platform/datapath_raw_linux.c b/src/platform/datapath_raw_linux.c index 06c712c6cc..02fbf05dd4 100644 --- a/src/platform/datapath_raw_linux.c +++ b/src/platform/datapath_raw_linux.c @@ -112,7 +112,7 @@ RawSocketCreateUdp( { QUIC_STATUS Status = QUIC_STATUS_SUCCESS; - CxPlatRundownInitialize(&NewSocket->Rundown); + CxPlatRundownInitialize(&NewSocket->RawRundown); NewSocket->RawDatapath = Raw; NewSocket->CibirIdLength = Config->CibirIdLength; NewSocket->CibirIdOffsetSrc = Config->CibirIdOffsetSrc; @@ -169,7 +169,7 @@ RawSocketCreateUdp( if (QUIC_FAILED(Status)) { if (NewSocket != NULL) { - CxPlatRundownUninitialize(&NewSocket->Rundown); + CxPlatRundownUninitialize(&NewSocket->RawRundown); CxPlatZeroMemory(NewSocket, sizeof(CXPLAT_SOCKET_RAW) - sizeof(CXPLAT_SOCKET)); NewSocket = NULL; } diff --git a/src/platform/datapath_raw_socket.c b/src/platform/datapath_raw_socket.c index e0a31c07da..48f448e92d 100644 --- a/src/platform/datapath_raw_socket.c +++ b/src/platform/datapath_raw_socket.c @@ -62,7 +62,7 @@ CxPlatGetSocket( while (Entry != NULL) { CXPLAT_SOCKET_RAW* Temp = CXPLAT_CONTAINING_RECORD(Entry, CXPLAT_SOCKET_RAW, Entry); if (CxPlatSocketCompare(Temp, LocalAddress, RemoteAddress)) { - if (CxPlatRundownAcquire(&Temp->Rundown)) { + if (CxPlatRundownAcquire(&Temp->RawRundown)) { Socket = Temp; } break; @@ -298,6 +298,7 @@ CxPlatDpRawParseIPv4( } Packet->TypeOfService = IP->EcnField; + Packet->HopLimitTTL = IP->TimeToLive; Packet->Route->RemoteAddress.Ipv4.sin_family = AF_INET; CxPlatCopyMemory(&Packet->Route->RemoteAddress.Ipv4.sin_addr, IP->Source, sizeof(IP->Source)); Packet->Route->LocalAddress.Ipv4.sin_family = AF_INET; @@ -366,6 +367,7 @@ CxPlatDpRawParseIPv6( VersionClassEcnFlow.Value = CxPlatByteSwapUint32(IP->VersionClassEcnFlow); Packet->TypeOfService = (uint8_t)VersionClassEcnFlow.EcnField; + Packet->HopLimitTTL = IP->HopLimit; Packet->Route->RemoteAddress.Ipv6.sin6_family = AF_INET6; CxPlatCopyMemory(&Packet->Route->RemoteAddress.Ipv6.sin6_addr, IP->Source, sizeof(IP->Source)); Packet->Route->LocalAddress.Ipv6.sin6_family = AF_INET6; diff --git a/src/platform/datapath_raw_win.c b/src/platform/datapath_raw_win.c index c5aa8b18bb..d3f01926cd 100644 --- a/src/platform/datapath_raw_win.c +++ b/src/platform/datapath_raw_win.c @@ -113,7 +113,7 @@ RawSocketCreateUdp( CXPLAT_DBG_ASSERT(Socket != NULL); QUIC_STATUS Status = QUIC_STATUS_SUCCESS; - CxPlatRundownInitialize(&Socket->Rundown); + CxPlatRundownInitialize(&Socket->RawRundown); Socket->RawDatapath = Raw; Socket->CibirIdLength = Config->CibirIdLength; Socket->CibirIdOffsetSrc = Config->CibirIdOffsetSrc; @@ -167,7 +167,7 @@ RawSocketCreateUdp( if (QUIC_FAILED(Status)) { if (Socket != NULL) { - CxPlatRundownUninitialize(&Socket->Rundown); + CxPlatRundownUninitialize(&Socket->RawRundown); CxPlatZeroMemory(Socket, sizeof(CXPLAT_SOCKET_RAW) - sizeof(CXPLAT_SOCKET)); Socket = NULL; } diff --git a/src/platform/datapath_raw_xdp_win.c b/src/platform/datapath_raw_xdp_win.c index ff1697fec2..370b53a529 100644 --- a/src/platform/datapath_raw_xdp_win.c +++ b/src/platform/datapath_raw_xdp_win.c @@ -578,10 +578,11 @@ CxPlatDpRawInterfaceInitialize( if (!SetFileCompletionNotificationModes( (HANDLE)Queue->RxXsk, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE)) { + Status = QUIC_STATUS_INTERNAL_ERROR; QuicTraceEvent( LibraryErrorStatus, "[ lib] ERROR, %u, %s.", - Status, + GetLastError(), "SetFileCompletionNotificationModes"); goto Error; } @@ -700,10 +701,11 @@ CxPlatDpRawInterfaceInitialize( if (!SetFileCompletionNotificationModes( (HANDLE)Queue->TxXsk, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE)) { + Status = QUIC_STATUS_INTERNAL_ERROR; QuicTraceEvent( LibraryErrorStatus, "[ lib] ERROR, %u, %s.", - Status, + GetLastError(), "SetFileCompletionNotificationModes"); goto Error; } @@ -936,6 +938,7 @@ CxPlatDpRawInitialize( ) { XDP_DATAPATH* Xdp = (XDP_DATAPATH*)Datapath; + PMIB_IF_TABLE2 pIfTable = NULL; QUIC_STATUS Status; if (WorkerPool == NULL) { @@ -971,7 +974,6 @@ CxPlatDpRawInitialize( Xdp, Xdp->PartitionCount); - PMIB_IF_TABLE2 pIfTable; if (GetIfTable2(&pIfTable) != NO_ERROR) { Status = QUIC_STATUS_INTERNAL_ERROR; goto Error; @@ -1086,7 +1088,6 @@ CxPlatDpRawInitialize( "CxPlatThreadCreate"); goto Error; } - FreeMibTable(pIfTable); if (CxPlatListIsEmpty(&Xdp->Interfaces)) { QuicTraceEvent( @@ -1152,6 +1153,9 @@ CxPlatDpRawInitialize( Status = QUIC_STATUS_SUCCESS; Error: + if (pIfTable != NULL) { + FreeMibTable(pIfTable); + } if (QUIC_FAILED(Status)) { while (!CxPlatListIsEmpty(&Xdp->Interfaces)) { diff --git a/src/platform/datapath_win.c b/src/platform/datapath_win.c index 6cab97b861..6512dc98ab 100644 --- a/src/platform/datapath_win.c +++ b/src/platform/datapath_win.c @@ -15,334 +15,6 @@ #include "datapath_winuser.c.clog.h" #endif -_IRQL_requires_max_(PASSIVE_LEVEL) -_Success_(QUIC_SUCCEEDED(return)) -QUIC_STATUS -CxPlatDataPathGetLocalAddresses( - _In_ CXPLAT_DATAPATH* Datapath, - _Outptr_ _At_(*Addresses, __drv_allocatesMem(Mem)) - CXPLAT_ADAPTER_ADDRESS** Addresses, - _Out_ uint32_t* AddressesCount - ) -{ - const ULONG Flags = - GAA_FLAG_INCLUDE_ALL_INTERFACES | - GAA_FLAG_SKIP_ANYCAST | - GAA_FLAG_SKIP_MULTICAST | - GAA_FLAG_SKIP_DNS_SERVER | - GAA_FLAG_SKIP_FRIENDLY_NAME | - GAA_FLAG_SKIP_DNS_INFO; - - UNREFERENCED_PARAMETER(Datapath); - - ULONG AdapterAddressesSize = 0; - PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL; - uint32_t Index = 0; - - QUIC_STATUS Status = QUIC_STATUS_SUCCESS; - ULONG Error; - do { - Error = - GetAdaptersAddresses( - AF_UNSPEC, - Flags, - NULL, - AdapterAddresses, - &AdapterAddressesSize); - if (Error == ERROR_BUFFER_OVERFLOW) { - if (AdapterAddresses) { - CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); - } - AdapterAddresses = CXPLAT_ALLOC_NONPAGED(AdapterAddressesSize, QUIC_POOL_DATAPATH_ADDRESSES); - if (!AdapterAddresses) { - Error = ERROR_NOT_ENOUGH_MEMORY; - QuicTraceEvent( - AllocFailure, - "Allocation of '%s' failed. (%llu bytes)", - "PIP_ADAPTER_ADDRESSES", - AdapterAddressesSize); - } - } - } while (Error == ERROR_BUFFER_OVERFLOW); - - if (Error != ERROR_SUCCESS) { - QuicTraceEvent( - LibraryErrorStatus, - "[ lib] ERROR, %u, %s.", - Error, - "GetAdaptersAddresses"); - Status = HRESULT_FROM_WIN32(Error); - goto Exit; - } - - for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { - for (PIP_ADAPTER_UNICAST_ADDRESS_LH Iter2 = Iter->FirstUnicastAddress; Iter2 != NULL; Iter2 = Iter2->Next) { - Index++; - } - } - - if (Index == 0) { - QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "No local unicast addresses found"); - Status = QUIC_STATUS_NOT_FOUND; - goto Exit; - } - - *Addresses = CXPLAT_ALLOC_NONPAGED(Index * sizeof(CXPLAT_ADAPTER_ADDRESS), QUIC_POOL_DATAPATH_ADDRESSES); - if (*Addresses == NULL) { - Status = QUIC_STATUS_OUT_OF_MEMORY; - QuicTraceEvent( - AllocFailure, - "Allocation of '%s' failed. (%llu bytes)", - "Addresses", - Index * sizeof(CXPLAT_ADAPTER_ADDRESS)); - goto Exit; - } - - CxPlatZeroMemory(*Addresses, Index * sizeof(CXPLAT_ADAPTER_ADDRESS)); - *AddressesCount = Index; - Index = 0; - - for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { - for (PIP_ADAPTER_UNICAST_ADDRESS_LH Iter2 = Iter->FirstUnicastAddress; Iter2 != NULL; Iter2 = Iter2->Next) { - CxPlatCopyMemory( - &(*Addresses)[Index].Address, - Iter2->Address.lpSockaddr, - sizeof(QUIC_ADDR)); - (*Addresses)[Index].InterfaceIndex = - Iter2->Address.lpSockaddr->sa_family == AF_INET ? - (uint32_t)Iter->IfIndex : (uint32_t)Iter->Ipv6IfIndex; - (*Addresses)[Index].InterfaceType = (uint16_t)Iter->IfType; - (*Addresses)[Index].OperationStatus = (CXPLAT_OPERATION_STATUS)Iter->OperStatus; - Index++; - } - } - -Exit: - - if (AdapterAddresses) { - CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); - } - - return Status; -} - -_IRQL_requires_max_(PASSIVE_LEVEL) -_Success_(QUIC_SUCCEEDED(return)) -QUIC_STATUS -CxPlatDataPathGetGatewayAddresses( - _In_ CXPLAT_DATAPATH* Datapath, - _Outptr_ _At_(*GatewayAddresses, __drv_allocatesMem(Mem)) - QUIC_ADDR** GatewayAddresses, - _Out_ uint32_t* GatewayAddressesCount - ) -{ - const ULONG Flags = - GAA_FLAG_INCLUDE_GATEWAYS | - GAA_FLAG_INCLUDE_ALL_INTERFACES | - GAA_FLAG_SKIP_DNS_SERVER | - GAA_FLAG_SKIP_MULTICAST; - - UNREFERENCED_PARAMETER(Datapath); - - ULONG AdapterAddressesSize = 0; - PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL; - uint32_t Index = 0; - - QUIC_STATUS Status = QUIC_STATUS_SUCCESS; - ULONG Error; - do { - Error = - GetAdaptersAddresses( - AF_UNSPEC, - Flags, - NULL, - AdapterAddresses, - &AdapterAddressesSize); - if (Error == ERROR_BUFFER_OVERFLOW) { - if (AdapterAddresses) { - CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); - } - AdapterAddresses = CXPLAT_ALLOC_NONPAGED(AdapterAddressesSize, QUIC_POOL_DATAPATH_ADDRESSES); - if (!AdapterAddresses) { - Error = ERROR_NOT_ENOUGH_MEMORY; - QuicTraceEvent( - AllocFailure, - "Allocation of '%s' failed. (%llu bytes)", - "PIP_ADAPTER_ADDRESSES", - AdapterAddressesSize); - } - } - } while (Error == ERROR_BUFFER_OVERFLOW); - - if (Error != ERROR_SUCCESS) { - QuicTraceEvent( - LibraryErrorStatus, - "[ lib] ERROR, %u, %s.", - Error, - "GetAdaptersAddresses"); - Status = HRESULT_FROM_WIN32(Error); - goto Exit; - } - - for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { - for (PIP_ADAPTER_GATEWAY_ADDRESS_LH Iter2 = Iter->FirstGatewayAddress; Iter2 != NULL; Iter2 = Iter2->Next) { - Index++; - } - } - - if (Index == 0) { - QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "No gateway server addresses found"); - Status = QUIC_STATUS_NOT_FOUND; - goto Exit; - } - - *GatewayAddresses = CXPLAT_ALLOC_NONPAGED(Index * sizeof(QUIC_ADDR), QUIC_POOL_DATAPATH_ADDRESSES); - if (*GatewayAddresses == NULL) { - Status = QUIC_STATUS_OUT_OF_MEMORY; - QuicTraceEvent( - AllocFailure, - "Allocation of '%s' failed. (%llu bytes)", - "GatewayAddresses", - Index * sizeof(QUIC_ADDR)); - goto Exit; - } - - CxPlatZeroMemory(*GatewayAddresses, Index * sizeof(QUIC_ADDR)); - *GatewayAddressesCount = Index; - Index = 0; - - for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { - for (PIP_ADAPTER_GATEWAY_ADDRESS_LH Iter2 = Iter->FirstGatewayAddress; Iter2 != NULL; Iter2 = Iter2->Next) { - CxPlatCopyMemory( - &(*GatewayAddresses)[Index], - Iter2->Address.lpSockaddr, - sizeof(QUIC_ADDR)); - Index++; - } - } - -Exit: - - if (AdapterAddresses) { - CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); - } - - return Status; -} - -// private func -void -CxPlatDataPathPopulateTargetAddress( - _In_ ADDRESS_FAMILY Family, - _In_ ADDRINFOW *Ai, - _Out_ SOCKADDR_INET* Address - ) -{ - if (Ai->ai_addr->sa_family == QUIC_ADDRESS_FAMILY_INET6) { - // - // Is this a mapped ipv4 one? - // - PSOCKADDR_IN6 SockAddr6 = (PSOCKADDR_IN6)Ai->ai_addr; - - if (Family == QUIC_ADDRESS_FAMILY_UNSPEC && IN6ADDR_ISV4MAPPED(SockAddr6)) - { - PSOCKADDR_IN SockAddr4 = &Address->Ipv4; - // - // Get the ipv4 address from the mapped address. - // - SockAddr4->sin_family = QUIC_ADDRESS_FAMILY_INET; - SockAddr4->sin_addr = - *(IN_ADDR UNALIGNED *) - IN6_GET_ADDR_V4MAPPED(&SockAddr6->sin6_addr); - SockAddr4->sin_port = SockAddr6->sin6_port; - return; - } - } - - CxPlatCopyMemory(Address, Ai->ai_addr, Ai->ai_addrlen); -} - -_IRQL_requires_max_(PASSIVE_LEVEL) -QUIC_STATUS -CxPlatDataPathResolveAddress( - _In_ CXPLAT_DATAPATH* Datapath, - _In_z_ const char* HostName, - _Inout_ QUIC_ADDR* Address - ) -{ - QUIC_STATUS Status; - PWSTR HostNameW = NULL; - ADDRINFOW Hints = { 0 }; - ADDRINFOW *Ai; - - Status = - CxPlatUtf8ToWideChar( - HostName, - QUIC_POOL_PLATFORM_TMP_ALLOC, - &HostNameW); - if (QUIC_FAILED(Status)) { - QuicTraceEvent( - LibraryErrorStatus, - "[ lib] ERROR, %u, %s.", - Status, - "Convert HostName to unicode"); - goto Exit; - } - - // - // Prepopulate hint with input family. It might be unspecified. - // - Hints.ai_family = Address->si_family; - - // - // Try numeric name first. - // - Hints.ai_flags = AI_NUMERICHOST; - if (GetAddrInfoW(HostNameW, NULL, &Hints, &Ai) == 0) { - CxPlatDataPathPopulateTargetAddress((ADDRESS_FAMILY)Hints.ai_family, Ai, Address); - FreeAddrInfoW(Ai); - Status = QUIC_STATUS_SUCCESS; - goto Exit; - } - - // - // Try canonical host name. - // - Hints.ai_flags = AI_CANONNAME; - if (GetAddrInfoW(HostNameW, NULL, &Hints, &Ai) == 0) { - CxPlatDataPathPopulateTargetAddress((ADDRESS_FAMILY)Hints.ai_family, Ai, Address); - FreeAddrInfoW(Ai); - Status = QUIC_STATUS_SUCCESS; - goto Exit; - } - - QuicTraceEvent( - LibraryError, - "[ lib] ERROR, %s.", - "Resolving hostname to IP"); - QuicTraceLogError( - DatapathResolveHostNameFailed, - "[%p] Couldn't resolve hostname '%s' to an IP address", - Datapath, - HostName); - Status = HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND); - -Exit: - - if (HostNameW != NULL) { - CXPLAT_FREE(HostNameW, QUIC_POOL_PLATFORM_TMP_ALLOC); - } - - return Status; -} - - _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS CxPlatSocketUpdateQeo( diff --git a/src/platform/datapath_winkernel.c b/src/platform/datapath_winkernel.c index 111b72890e..a2f7391153 100644 --- a/src/platform/datapath_winkernel.c +++ b/src/platform/datapath_winkernel.c @@ -10,6 +10,7 @@ --*/ #include "platform_internal.h" + #ifdef QUIC_CLOG #include "datapath_winkernel.c.clog.h" #endif @@ -198,6 +199,7 @@ typedef struct CXPLAT_DATAPATH_SEND_BUFFER { // Send context. // typedef struct CXPLAT_SEND_DATA { + CXPLAT_SEND_DATA_COMMON; CXPLAT_SOCKET* Binding; @@ -224,26 +226,11 @@ typedef struct CXPLAT_SEND_DATA { // CXPLAT_DATAPATH_SEND_BUFFER* TailBuf; - // - // The total buffer size for WsaBuffers. - // - uint32_t TotalSize; - - // - // The type of ECN markings needed for send. - // - CXPLAT_ECN_TYPE ECN; - // // The number of WSK buffers allocated. // UINT8 WskBufferCount; - // - // The send segmentation size; zero if segmentation is not performed. - // - UINT16 SegmentSize; - // // The QUIC_BUFFER returned to the client for segmented sends. // @@ -273,158 +260,6 @@ CxPlatDataPathSocketReceive( _In_opt_ PWSK_DATAGRAM_INDICATION DataIndication ); -typedef struct _WSK_DATAGRAM_SOCKET { - const WSK_PROVIDER_DATAGRAM_DISPATCH* Dispatch; -} WSK_DATAGRAM_SOCKET, * PWSK_DATAGRAM_SOCKET; - -// -// Per-port state. -// -typedef struct CXPLAT_SOCKET { - - // - // Flag indicates the binding has a default remote destination. - // - BOOLEAN Connected : 1; - - // - // Flag indicates the binding is being used for PCP. - // - BOOLEAN PcpBinding : 1; - - // - // Parent datapath. - // - CXPLAT_DATAPATH* Datapath; - - // - // UDP socket used for sending/receiving datagrams. - // - union { - PWSK_SOCKET Socket; - PWSK_DATAGRAM_SOCKET DgrmSocket; - }; - - // - // Event used to wait for completion of socket functions. - // - CXPLAT_EVENT WskCompletionEvent; - - // - // The local address and UDP port. - // - SOCKADDR_INET LocalAddress; - - // - // The remote address and UDP port. - // - SOCKADDR_INET RemoteAddress; - - // - // The local interface's MTU. - // - UINT16 Mtu; - - // - // Client context pointer. - // - void *ClientContext; - - // - // IRP used for socket functions. - // - union { - IRP Irp; - UCHAR IrpBuffer[sizeof(IRP) + sizeof(IO_STACK_LOCATION)]; - }; - - CXPLAT_RUNDOWN_REF Rundown[0]; // Per-proc - -} CXPLAT_SOCKET; - -// -// Represents the per-processor state of the datapath context. -// -typedef struct CXPLAT_DATAPATH_PROC_CONTEXT { - - // - // Pool of send contexts to be shared by all sockets on this core. - // - CXPLAT_POOL SendDataPool; - - // - // Pool of send buffers to be shared by all sockets on this core. - // - CXPLAT_POOL SendBufferPool; - - // - // Pool of large segmented send buffers to be shared by all sockets on this - // core. - // - CXPLAT_POOL LargeSendBufferPool; - - // - // Pool of receive datagram contexts and buffers to be shared by all sockets - // on this core. Index 0 is regular, Index 1 is URO. - // - // - CXPLAT_POOL RecvDatagramPools[2]; - - // - // Pool of receive data buffers. Index 0 is 4096, Index 1 is 65536. - // - CXPLAT_POOL RecvBufferPools[2]; - - int64_t OutstandingPendingBytes; - -} CXPLAT_DATAPATH_PROC_CONTEXT; - -// -// Structure that maintains all the internal state for the -// CxPlatDataPath interface. -// -typedef struct CXPLAT_DATAPATH { - - // - // Set of supported features. - // - uint32_t Features; - - // - // The registration with WinSock Kernel. - // - WSK_REGISTRATION WskRegistration; - WSK_PROVIDER_NPI WskProviderNpi; - WSK_CLIENT_DATAGRAM_DISPATCH WskDispatch; - - // - // The UDP callback function pointers. - // - CXPLAT_UDP_DATAPATH_CALLBACKS UdpHandlers; - - // - // The size of the buffer to allocate for client's receive context structure. - // - uint32_t ClientRecvDataLength; - - // - // The size of each receive datagram array element, including client context, - // internal context, and padding. - // - uint32_t DatagramStride; - - // - // The number of processors. - // - uint32_t ProcCount; - - // - // Per-processor completion contexts. - // - CXPLAT_DATAPATH_PROC_CONTEXT ProcContexts[0]; - -} CXPLAT_DATAPATH; - _IRQL_requires_same_ _Function_class_(ALLOCATE_FUNCTION_EX) PVOID @@ -765,6 +600,25 @@ CxPlatDataPathQuerySockoptSupport( } while (FALSE); + do { + RTL_OSVERSIONINFOW osInfo; + RtlZeroMemory(&osInfo, sizeof(osInfo)); + osInfo.dwOSVersionInfoSize = sizeof(osInfo); + NTSTATUS status = RtlGetVersion(&osInfo); + if (NT_SUCCESS(status)) { + DWORD BuildNumber = osInfo.dwBuildNumber; + // + // Some USO/URO bug blocks TTL feature support on Windows Server 2022. + // + if (BuildNumber == 20348) { + break; + } + } else { + break; + } + Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TTL; + } while (FALSE); + Error: if (UdpSocket != NULL) { @@ -789,7 +643,7 @@ CxPlatDataPathQuerySockoptSupport( _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS -CxPlatDataPathInitialize( +DataPathInitialize( _In_ uint32_t ClientRecvDataLength, _In_opt_ const CXPLAT_UDP_DATAPATH_CALLBACKS* UdpCallbacks, _In_opt_ const CXPLAT_TCP_DATAPATH_CALLBACKS* TcpCallbacks, @@ -1008,7 +862,7 @@ CxPlatDataPathInitialize( _IRQL_requires_max_(PASSIVE_LEVEL) void -CxPlatDataPathUninitialize( +DataPathUninitialize( _In_ CXPLAT_DATAPATH* Datapath ) { @@ -1032,7 +886,7 @@ CxPlatDataPathUninitialize( _IRQL_requires_max_(PASSIVE_LEVEL) void -CxPlatDataPathUpdateConfig( +DataPathUpdateConfig( _In_ CXPLAT_DATAPATH* Datapath, _In_ QUIC_EXECUTION_CONFIG* Config ) @@ -1043,7 +897,7 @@ CxPlatDataPathUpdateConfig( _IRQL_requires_max_(DISPATCH_LEVEL) uint32_t -CxPlatDataPathGetSupportedFeatures( +DataPathGetSupportedFeatures( _In_ CXPLAT_DATAPATH* Datapath ) { @@ -1052,12 +906,10 @@ CxPlatDataPathGetSupportedFeatures( _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN -CxPlatDataPathIsPaddingPreferred( - _In_ CXPLAT_DATAPATH* Datapath, - _In_ CXPLAT_SEND_DATA* SendData +DataPathIsPaddingPreferred( + _In_ CXPLAT_DATAPATH* Datapath ) { - UNREFERENCED_PARAMETER(SendData); return !!(Datapath->Features & CXPLAT_DATAPATH_FEATURE_SEND_SEGMENTATION); } @@ -1384,7 +1236,7 @@ CxPlatDataPathSetControlSocket( _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS -CxPlatSocketCreateUdp( +SocketCreateUdp( _In_ CXPLAT_DATAPATH* Datapath, _In_ const CXPLAT_UDP_CONFIG* Config, _Out_ CXPLAT_SOCKET** NewBinding @@ -1672,6 +1524,46 @@ CxPlatSocketCreateUdp( goto Error; } + if (Datapath->Features & CXPLAT_DATAPATH_FEATURE_TTL) { + Option = TRUE; + Status = + CxPlatDataPathSetControlSocket( + Binding, + WskSetOption, + IP_HOPLIMIT, + IPPROTO_IP, + sizeof(Option), + &Option); + if (QUIC_FAILED(Status)) { + QuicTraceEvent( + DatapathErrorStatus, + "[data][%p] ERROR, %u, %s.", + Binding, + Status, + "Set IP_HOPLIMIT"); + goto Error; + } + + Option = TRUE; + Status = + CxPlatDataPathSetControlSocket( + Binding, + WskSetOption, + IPV6_HOPLIMIT, + IPPROTO_IPV6, + sizeof(Option), + &Option); + if (QUIC_FAILED(Status)) { + QuicTraceEvent( + DatapathErrorStatus, + "[data][%p] ERROR, %u, %s.", + Binding, + Status, + "Set IPV6_HOPLIMIT"); + goto Error; + } + } + if (Datapath->Features & CXPLAT_DATAPATH_FEATURE_RECV_COALESCING) { Option = MAX_URO_PAYLOAD_LENGTH; Status = @@ -1868,7 +1760,7 @@ CxPlatSocketCreateUdp( _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS -CxPlatSocketCreateTcp( +SocketCreateTcp( _In_ CXPLAT_DATAPATH* Datapath, _In_opt_ const QUIC_ADDR* LocalAddress, _In_ const QUIC_ADDR* RemoteAddress, @@ -1886,7 +1778,7 @@ CxPlatSocketCreateTcp( _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS -CxPlatSocketCreateTcpListener( +SocketCreateTcpListener( _In_ CXPLAT_DATAPATH* Datapath, _In_opt_ const QUIC_ADDR* LocalAddress, _In_opt_ void* CallbackContext, @@ -1954,7 +1846,7 @@ CxPlatDataPathCloseSocketIoCompletion( _IRQL_requires_max_(PASSIVE_LEVEL) void -CxPlatSocketDelete( +SocketDelete( _In_ CXPLAT_SOCKET* Binding ) { @@ -2002,21 +1894,6 @@ CxPlatSocketDelete( CxPlatSocketDeleteComplete(Binding); } -_IRQL_requires_max_(PASSIVE_LEVEL) -QUIC_STATUS -CxPlatSocketUpdateQeo( - _In_ CXPLAT_SOCKET* Socket, - _In_reads_(OffloadCount) - const CXPLAT_QEO_CONNECTION* Offloads, - _In_ uint32_t OffloadCount - ) -{ - UNREFERENCED_PARAMETER(Socket); - UNREFERENCED_PARAMETER(Offloads); - UNREFERENCED_PARAMETER(OffloadCount); - return QUIC_STATUS_NOT_SUPPORTED; -} - _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN CxPlatSocketSetContext( @@ -2045,48 +1922,6 @@ CxPlatSocketGetContext( return Binding->ClientContext; } -_IRQL_requires_max_(DISPATCH_LEVEL) -UINT16 -CxPlatSocketGetLocalMtu( - _In_ CXPLAT_SOCKET* Binding - ) -{ - CXPLAT_DBG_ASSERT(Binding != NULL); - return Binding->Mtu; -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -void -CxPlatSocketGetLocalAddress( - _In_ CXPLAT_SOCKET* Binding, - _Out_ QUIC_ADDR* Address - ) -{ - CXPLAT_DBG_ASSERT(Binding != NULL); - *Address = Binding->LocalAddress; -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -void -CxPlatSocketGetRemoteAddress( - _In_ CXPLAT_SOCKET* Binding, - _Out_ QUIC_ADDR* Address - ) -{ - CXPLAT_DBG_ASSERT(Binding != NULL); - *Address = Binding->RemoteAddress; -} - -_IRQL_requires_max_(DISPATCH_LEVEL) -BOOLEAN -CxPlatSocketRawSocketAvailable( - _In_ CXPLAT_SOCKET* Socket - ) -{ - UNREFERENCED_PARAMETER(Socket); - return FALSE; -} - _IRQL_requires_max_(DISPATCH_LEVEL) DATAPATH_RX_IO_BLOCK* CxPlatSocketAllocRxIoBlock( @@ -2200,6 +2035,7 @@ CxPlatDataPathSocketReceive( SOCKADDR_INET RemoteAddr; UINT16 MessageLength = 0; INT ECN = 0; + INT HopLimitTTL = 0; // // Parse the ancillary data for all the per datagram information that we @@ -2231,6 +2067,10 @@ CxPlatDataPathSocketReceive( } else if (CMsg->cmsg_type == IPV6_ECN) { ECN = *(PINT)WSA_CMSG_DATA(CMsg); CXPLAT_DBG_ASSERT(ECN < UINT8_MAX); + } else if (CMsg->cmsg_type == IPV6_HOPLIMIT) { + HopLimitTTL = *(PINT)WSA_CMSG_DATA(CMsg); + CXPLAT_DBG_ASSERT(HopLimitTTL < 256); + CXPLAT_DBG_ASSERT(HopLimitTTL > 0); } } else if (CMsg->cmsg_level == IPPROTO_IP) { if (CMsg->cmsg_type == IP_PKTINFO) { @@ -2250,6 +2090,10 @@ CxPlatDataPathSocketReceive( } else if (CMsg->cmsg_type == IP_ECN) { ECN = *(PINT)WSA_CMSG_DATA(CMsg); CXPLAT_DBG_ASSERT(ECN < UINT8_MAX); + } else if (CMsg->cmsg_type == IP_TTL) { + HopLimitTTL = *(PINT)WSA_CMSG_DATA(CMsg); + CXPLAT_DBG_ASSERT(HopLimitTTL < 256); + CXPLAT_DBG_ASSERT(HopLimitTTL > 0); } } else if (CMsg->cmsg_level == IPPROTO_UDP) { if (CMsg->cmsg_type == UDP_COALESCED_INFO) { @@ -2416,6 +2260,7 @@ CxPlatDataPathSocketReceive( Datagram->Data.Next = NULL; Datagram->Data.PartitionIndex = (uint16_t)(CurProcNumber % Binding->Datapath->ProcCount); Datagram->Data.TypeOfService = (uint8_t)ECN; + Datagram->Data.HopLimitTTL = (uint8_t)HopLimitTTL; Datagram->Data.Allocated = TRUE; Datagram->Data.QueuedOnConnection = FALSE; @@ -2429,6 +2274,7 @@ CxPlatDataPathSocketReceive( Datagram->Data.BufferLength = MessageLength; Datagram->Data.Route = &IoBlock->Route; + Datagram->Data.Route->DatapathType = Datagram->Data.DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; // // Add the datagram to the end of the current chain. @@ -2513,8 +2359,8 @@ CxPlatDataPathSocketReceive( _IRQL_requires_max_(DISPATCH_LEVEL) void -CxPlatRecvDataReturn( - _In_opt_ CXPLAT_RECV_DATA* RecvDataChain +RecvDataReturn( + _In_ CXPLAT_RECV_DATA* RecvDataChain ) { CXPLAT_SOCKET* Binding = NULL; @@ -2589,7 +2435,7 @@ CxPlatRecvDataReturn( _IRQL_requires_max_(DISPATCH_LEVEL) _Success_(return != NULL) CXPLAT_SEND_DATA* -CxPlatSendDataAlloc( +SendDataAlloc( _In_ CXPLAT_SOCKET* Binding, _Inout_ CXPLAT_SEND_CONFIG* Config ) @@ -2614,6 +2460,7 @@ CxPlatSendDataAlloc( ? Config->MaxPacketSize : 0; SendData->ClientBuffer.Length = 0; SendData->ClientBuffer.Buffer = NULL; + SendData->DatapathType = Config->Route->DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; } return SendData; @@ -2621,7 +2468,7 @@ CxPlatSendDataAlloc( _IRQL_requires_max_(DISPATCH_LEVEL) void -CxPlatSendDataFree( +SendDataFree( _In_ CXPLAT_SEND_DATA* SendData ) { @@ -2858,7 +2705,7 @@ CxPlatSendDataAllocSegmentBuffer( _IRQL_requires_max_(DISPATCH_LEVEL) _Success_(return != NULL) QUIC_BUFFER* -CxPlatSendDataAllocBuffer( +SendDataAllocBuffer( _In_ CXPLAT_SEND_DATA* SendData, _In_ UINT16 MaxBufferLength ) @@ -2912,7 +2759,7 @@ CxPlatSendDataFreeSendBuffer( _IRQL_requires_max_(DISPATCH_LEVEL) void -CxPlatSendDataFreeBuffer( +SendDataFreeBuffer( _In_ CXPLAT_SEND_DATA* SendData, _In_ QUIC_BUFFER* Buffer ) @@ -2943,7 +2790,7 @@ CxPlatSendDataFreeBuffer( _IRQL_requires_max_(DISPATCH_LEVEL) BOOLEAN -CxPlatSendDataIsFull( +SendDataIsFull( _In_ CXPLAT_SEND_DATA* SendData ) { @@ -2976,7 +2823,7 @@ CxPlatDataPathSendComplete( } IoCleanupIrp(&SendData->Irp); - CxPlatSendDataFree(SendData); + SendDataFree(SendData); return STATUS_MORE_PROCESSING_REQUIRED; } @@ -3005,7 +2852,7 @@ CxPlatSocketPrepareSendData( _IRQL_requires_max_(DISPATCH_LEVEL) void -CxPlatSocketSend( +SocketSend( _In_ CXPLAT_SOCKET* Binding, _In_ const CXPLAT_ROUTE* Route, _In_ CXPLAT_SEND_DATA* SendData @@ -3136,55 +2983,11 @@ CxPlatSocketGetTcpStatistics( return QUIC_STATUS_NOT_SUPPORTED; } -_IRQL_requires_max_(PASSIVE_LEVEL) -void -QuicCopyRouteInfo( - _Inout_ CXPLAT_ROUTE* DstRoute, - _In_ CXPLAT_ROUTE* SrcRoute - ) -{ - *DstRoute = *SrcRoute; -} - -void -CxPlatResolveRouteComplete( - _In_ void* Context, - _Inout_ CXPLAT_ROUTE* Route, - _In_reads_bytes_(6) const uint8_t* PhysicalAddress, - _In_ uint8_t PathId - ) -{ - UNREFERENCED_PARAMETER(Context); - UNREFERENCED_PARAMETER(Route); - UNREFERENCED_PARAMETER(PhysicalAddress); - UNREFERENCED_PARAMETER(PathId); -} - -_IRQL_requires_max_(PASSIVE_LEVEL) -QUIC_STATUS -CxPlatResolveRoute( - _In_ CXPLAT_SOCKET* Socket, - _Inout_ CXPLAT_ROUTE* Route, - _In_ uint8_t PathId, - _In_ void* Context, - _In_ CXPLAT_ROUTE_RESOLUTION_CALLBACK_HANDLER Callback - ) -{ - UNREFERENCED_PARAMETER(Socket); - UNREFERENCED_PARAMETER(PathId); - UNREFERENCED_PARAMETER(Context); - UNREFERENCED_PARAMETER(Callback); - Route->State = RouteResolved; - return QUIC_STATUS_SUCCESS; -} - -_IRQL_requires_max_(PASSIVE_LEVEL) void -CxPlatUpdateRoute( - _Inout_ CXPLAT_ROUTE* DstRoute, - _In_ CXPLAT_ROUTE* SrcRoute +DataPathProcessCqe( + _In_ CXPLAT_CQE* Cqe ) { - UNREFERENCED_PARAMETER(DstRoute); - UNREFERENCED_PARAMETER(SrcRoute); + UNREFERENCED_PARAMETER(Cqe); + CXPLAT_DBG_ASSERT(FALSE); } diff --git a/src/platform/datapath_winuser.c b/src/platform/datapath_winuser.c index bfaafb947d..c7d40bf83a 100644 --- a/src/platform/datapath_winuser.c +++ b/src/platform/datapath_winuser.c @@ -153,7 +153,8 @@ typedef struct DATAPATH_RX_IO_BLOCK { RIO_CMSG_BASE_SIZE + WSA_CMSG_SPACE(sizeof(IN6_PKTINFO)) + // IP_PKTINFO WSA_CMSG_SPACE(sizeof(DWORD)) + // UDP_COALESCED_INFO - WSA_CMSG_SPACE(sizeof(INT)) // IP_ECN + WSA_CMSG_SPACE(sizeof(INT)) + // IP_ECN + WSA_CMSG_SPACE(sizeof(INT)) // IP_HOP_LIMIT ]; } DATAPATH_RX_IO_BLOCK; @@ -226,16 +227,6 @@ typedef struct CXPLAT_SEND_DATA { // CXPLAT_POOL* BufferPool; - // - // The total buffer size for WsaBuffers. - // - uint32_t TotalSize; - - // - // The send segmentation size; zero if segmentation is not performed. - // - uint16_t SegmentSize; - // // Set of flags set to configure the send behavior. // @@ -558,6 +549,12 @@ CxPlatDataPathQueryRssScalabilityInfo( } } +// +// To determine the OS version, we are going to use RtlGetVersion API +// since GetVersion call can be shimmed on Win8.1+. +// +typedef LONG (WINAPI *FuncRtlGetVersion)(RTL_OSVERSIONINFOW *); + QUIC_STATUS CxPlatDataPathQuerySockoptSupport( _Inout_ CXPLAT_DATAPATH* Datapath @@ -734,6 +731,28 @@ CxPlatDataPathQuerySockoptSupport( Datapath->Features |= CXPLAT_DATAPATH_FEATURE_RECV_COALESCING; } } + // + // TODO: This "TTL_FEATURE check" code works, and mirrors the approach for Kernel mode. + // However, it is considered a "hack" and we should determine whether or not + // the current release story fits this current workaround. + // + HMODULE NtDllHandle = LoadLibraryA("ntdll.dll"); + if (NtDllHandle) { + FuncRtlGetVersion VersionFunc = (FuncRtlGetVersion)GetProcAddress(NtDllHandle, "RtlGetVersion"); + if (VersionFunc) { + RTL_OSVERSIONINFOW VersionInfo = {0}; + VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); + if ((*VersionFunc)(&VersionInfo) == 0) { + // + // Some USO/URO bug blocks TTL feature support on Windows Server 2022. + // + if (VersionInfo.dwBuildNumber != 20348) { + Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TTL; + } + } + } + FreeLibrary(NtDllHandle); + } Datapath->Features |= CXPLAT_DATAPATH_FEATURE_TCP; @@ -746,12 +765,6 @@ CxPlatDataPathQuerySockoptSupport( return Status; } -// -// To determine the OS version, we are going to use RtlGetVersion API -// since GetVersion call can be shimmed on Win8.1+. -// -typedef LONG (WINAPI *FuncRtlGetVersion)(RTL_OSVERSIONINFOW *); - _IRQL_requires_max_(PASSIVE_LEVEL) QUIC_STATUS DataPathInitialize( @@ -1081,6 +1094,334 @@ DataPathIsPaddingPreferred( return !!(Datapath->Features & CXPLAT_DATAPATH_FEATURE_SEND_SEGMENTATION); } +_IRQL_requires_max_(PASSIVE_LEVEL) +_Success_(QUIC_SUCCEEDED(return)) +QUIC_STATUS +CxPlatDataPathGetLocalAddresses( + _In_ CXPLAT_DATAPATH* Datapath, + _Outptr_ _At_(*Addresses, __drv_allocatesMem(Mem)) + CXPLAT_ADAPTER_ADDRESS** Addresses, + _Out_ uint32_t* AddressesCount + ) +{ + const ULONG Flags = + GAA_FLAG_INCLUDE_ALL_INTERFACES | + GAA_FLAG_SKIP_ANYCAST | + GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_FRIENDLY_NAME | + GAA_FLAG_SKIP_DNS_INFO; + + UNREFERENCED_PARAMETER(Datapath); + + ULONG AdapterAddressesSize = 0; + PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL; + uint32_t Index = 0; + + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + ULONG Error; + do { + Error = + GetAdaptersAddresses( + AF_UNSPEC, + Flags, + NULL, + AdapterAddresses, + &AdapterAddressesSize); + if (Error == ERROR_BUFFER_OVERFLOW) { + if (AdapterAddresses) { + CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); + } + AdapterAddresses = CXPLAT_ALLOC_NONPAGED(AdapterAddressesSize, QUIC_POOL_DATAPATH_ADDRESSES); + if (!AdapterAddresses) { + Error = ERROR_NOT_ENOUGH_MEMORY; + QuicTraceEvent( + AllocFailure, + "Allocation of '%s' failed. (%llu bytes)", + "PIP_ADAPTER_ADDRESSES", + AdapterAddressesSize); + } + } + } while (Error == ERROR_BUFFER_OVERFLOW); + + if (Error != ERROR_SUCCESS) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + Error, + "GetAdaptersAddresses"); + Status = HRESULT_FROM_WIN32(Error); + goto Exit; + } + + for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { + for (PIP_ADAPTER_UNICAST_ADDRESS_LH Iter2 = Iter->FirstUnicastAddress; Iter2 != NULL; Iter2 = Iter2->Next) { + Index++; + } + } + + if (Index == 0) { + QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "No local unicast addresses found"); + Status = QUIC_STATUS_NOT_FOUND; + goto Exit; + } + + *Addresses = CXPLAT_ALLOC_NONPAGED(Index * sizeof(CXPLAT_ADAPTER_ADDRESS), QUIC_POOL_DATAPATH_ADDRESSES); + if (*Addresses == NULL) { + Status = QUIC_STATUS_OUT_OF_MEMORY; + QuicTraceEvent( + AllocFailure, + "Allocation of '%s' failed. (%llu bytes)", + "Addresses", + Index * sizeof(CXPLAT_ADAPTER_ADDRESS)); + goto Exit; + } + + CxPlatZeroMemory(*Addresses, Index * sizeof(CXPLAT_ADAPTER_ADDRESS)); + *AddressesCount = Index; + Index = 0; + + for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { + for (PIP_ADAPTER_UNICAST_ADDRESS_LH Iter2 = Iter->FirstUnicastAddress; Iter2 != NULL; Iter2 = Iter2->Next) { + CxPlatCopyMemory( + &(*Addresses)[Index].Address, + Iter2->Address.lpSockaddr, + sizeof(QUIC_ADDR)); + (*Addresses)[Index].InterfaceIndex = + Iter2->Address.lpSockaddr->sa_family == AF_INET ? + (uint32_t)Iter->IfIndex : (uint32_t)Iter->Ipv6IfIndex; + (*Addresses)[Index].InterfaceType = (uint16_t)Iter->IfType; + (*Addresses)[Index].OperationStatus = (CXPLAT_OPERATION_STATUS)Iter->OperStatus; + Index++; + } + } + +Exit: + + if (AdapterAddresses) { + CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); + } + + return Status; +} + + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Success_(QUIC_SUCCEEDED(return)) +QUIC_STATUS +CxPlatDataPathGetGatewayAddresses( + _In_ CXPLAT_DATAPATH* Datapath, + _Outptr_ _At_(*GatewayAddresses, __drv_allocatesMem(Mem)) + QUIC_ADDR** GatewayAddresses, + _Out_ uint32_t* GatewayAddressesCount + ) +{ + const ULONG Flags = + GAA_FLAG_INCLUDE_GATEWAYS | + GAA_FLAG_INCLUDE_ALL_INTERFACES | + GAA_FLAG_SKIP_DNS_SERVER | + GAA_FLAG_SKIP_MULTICAST; + + UNREFERENCED_PARAMETER(Datapath); + + ULONG AdapterAddressesSize = 0; + PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL; + uint32_t Index = 0; + + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + ULONG Error; + do { + Error = + GetAdaptersAddresses( + AF_UNSPEC, + Flags, + NULL, + AdapterAddresses, + &AdapterAddressesSize); + if (Error == ERROR_BUFFER_OVERFLOW) { + if (AdapterAddresses) { + CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); + } + AdapterAddresses = CXPLAT_ALLOC_NONPAGED(AdapterAddressesSize, QUIC_POOL_DATAPATH_ADDRESSES); + if (!AdapterAddresses) { + Error = ERROR_NOT_ENOUGH_MEMORY; + QuicTraceEvent( + AllocFailure, + "Allocation of '%s' failed. (%llu bytes)", + "PIP_ADAPTER_ADDRESSES", + AdapterAddressesSize); + } + } + } while (Error == ERROR_BUFFER_OVERFLOW); + + if (Error != ERROR_SUCCESS) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + Error, + "GetAdaptersAddresses"); + Status = HRESULT_FROM_WIN32(Error); + goto Exit; + } + + for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { + for (PIP_ADAPTER_GATEWAY_ADDRESS_LH Iter2 = Iter->FirstGatewayAddress; Iter2 != NULL; Iter2 = Iter2->Next) { + Index++; + } + } + + if (Index == 0) { + QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "No gateway server addresses found"); + Status = QUIC_STATUS_NOT_FOUND; + goto Exit; + } + + *GatewayAddresses = CXPLAT_ALLOC_NONPAGED(Index * sizeof(QUIC_ADDR), QUIC_POOL_DATAPATH_ADDRESSES); + if (*GatewayAddresses == NULL) { + Status = QUIC_STATUS_OUT_OF_MEMORY; + QuicTraceEvent( + AllocFailure, + "Allocation of '%s' failed. (%llu bytes)", + "GatewayAddresses", + Index * sizeof(QUIC_ADDR)); + goto Exit; + } + + CxPlatZeroMemory(*GatewayAddresses, Index * sizeof(QUIC_ADDR)); + *GatewayAddressesCount = Index; + Index = 0; + + for (PIP_ADAPTER_ADDRESSES Iter = AdapterAddresses; Iter != NULL; Iter = Iter->Next) { + for (PIP_ADAPTER_GATEWAY_ADDRESS_LH Iter2 = Iter->FirstGatewayAddress; Iter2 != NULL; Iter2 = Iter2->Next) { + CxPlatCopyMemory( + &(*GatewayAddresses)[Index], + Iter2->Address.lpSockaddr, + sizeof(QUIC_ADDR)); + Index++; + } + } + +Exit: + + if (AdapterAddresses) { + CXPLAT_FREE(AdapterAddresses, QUIC_POOL_DATAPATH_ADDRESSES); + } + + return Status; +} + +// private func +void +CxPlatDataPathPopulateTargetAddress( + _In_ ADDRESS_FAMILY Family, + _In_ ADDRINFOW *Ai, + _Out_ SOCKADDR_INET* Address + ) +{ + if (Ai->ai_addr->sa_family == QUIC_ADDRESS_FAMILY_INET6) { + // + // Is this a mapped ipv4 one? + // + PSOCKADDR_IN6 SockAddr6 = (PSOCKADDR_IN6)Ai->ai_addr; + + if (Family == QUIC_ADDRESS_FAMILY_UNSPEC && IN6ADDR_ISV4MAPPED(SockAddr6)) + { + PSOCKADDR_IN SockAddr4 = &Address->Ipv4; + // + // Get the ipv4 address from the mapped address. + // + SockAddr4->sin_family = QUIC_ADDRESS_FAMILY_INET; + SockAddr4->sin_addr = + *(IN_ADDR UNALIGNED *) + IN6_GET_ADDR_V4MAPPED(&SockAddr6->sin6_addr); + SockAddr4->sin_port = SockAddr6->sin6_port; + return; + } + } + + CxPlatCopyMemory(Address, Ai->ai_addr, Ai->ai_addrlen); +} + +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +CxPlatDataPathResolveAddress( + _In_ CXPLAT_DATAPATH* Datapath, + _In_z_ const char* HostName, + _Inout_ QUIC_ADDR* Address + ) +{ + QUIC_STATUS Status; + PWSTR HostNameW = NULL; + ADDRINFOW Hints = { 0 }; + ADDRINFOW *Ai; + + Status = + CxPlatUtf8ToWideChar( + HostName, + QUIC_POOL_PLATFORM_TMP_ALLOC, + &HostNameW); + if (QUIC_FAILED(Status)) { + QuicTraceEvent( + LibraryErrorStatus, + "[ lib] ERROR, %u, %s.", + Status, + "Convert HostName to unicode"); + goto Exit; + } + + // + // Prepopulate hint with input family. It might be unspecified. + // + Hints.ai_family = Address->si_family; + + // + // Try numeric name first. + // + Hints.ai_flags = AI_NUMERICHOST; + if (GetAddrInfoW(HostNameW, NULL, &Hints, &Ai) == 0) { + CxPlatDataPathPopulateTargetAddress((ADDRESS_FAMILY)Hints.ai_family, Ai, Address); + FreeAddrInfoW(Ai); + Status = QUIC_STATUS_SUCCESS; + goto Exit; + } + + // + // Try canonical host name. + // + Hints.ai_flags = AI_CANONNAME; + if (GetAddrInfoW(HostNameW, NULL, &Hints, &Ai) == 0) { + CxPlatDataPathPopulateTargetAddress((ADDRESS_FAMILY)Hints.ai_family, Ai, Address); + FreeAddrInfoW(Ai); + Status = QUIC_STATUS_SUCCESS; + goto Exit; + } + + QuicTraceEvent( + LibraryError, + "[ lib] ERROR, %s.", + "Resolving hostname to IP"); + QuicTraceLogError( + DatapathResolveHostNameFailed, + "[%p] Couldn't resolve hostname '%s' to an IP address", + Datapath, + HostName); + Status = HRESULT_FROM_WIN32(WSAHOST_NOT_FOUND); + +Exit: + + if (HostNameW != NULL) { + CXPLAT_FREE(HostNameW, QUIC_POOL_PLATFORM_TMP_ALLOC); + } + + return Status; +} + void CxPlatSocketArmRioNotify( _In_ CXPLAT_SOCKET_PROC* SocketProc @@ -1403,6 +1744,48 @@ SocketCreateUdp( goto Error; } + if (Datapath->Features & CXPLAT_DATAPATH_FEATURE_TTL) { + Option = TRUE; + Result = + setsockopt( + SocketProc->Socket, + IPPROTO_IP, + IP_HOPLIMIT, + (char*)&Option, + sizeof(Option)); + if (Result == SOCKET_ERROR) { + int WsaError = WSAGetLastError(); + QuicTraceEvent( + DatapathErrorStatus, + "[data][%p] ERROR, %u, %s.", + Socket, + WsaError, + "Set IP_HOPLIMIT"); + Status = HRESULT_FROM_WIN32(WsaError); + goto Error; + } + + Option = TRUE; + Result = + setsockopt( + SocketProc->Socket, + IPPROTO_IPV6, + IPV6_HOPLIMIT, + (char*)&Option, + sizeof(Option)); + if (Result == SOCKET_ERROR) { + int WsaError = WSAGetLastError(); + QuicTraceEvent( + DatapathErrorStatus, + "[data][%p] ERROR, %u, %s.", + Socket, + WsaError, + "Set IPV6_HOPLIMIT"); + Status = HRESULT_FROM_WIN32(WsaError); + goto Error; + } + } + // // The socket is shared by multiple endpoints, so increase the receive // buffer size. @@ -3103,7 +3486,7 @@ CxPlatDataPathUdpRecvComplete( ULONG MessageCount = 0; BOOLEAN IsCoalesced = FALSE; INT ECN = 0; - + INT HopLimitTTL = 0; if (SocketProc->Parent->UseRio) { PRIO_CMSG_BUFFER RioRcvMsg = (PRIO_CMSG_BUFFER)IoBlock->ControlBuf; IoBlock->WsaMsgHdr.Control.buf = IoBlock->ControlBuf + RIO_CMSG_BASE_SIZE; @@ -3126,6 +3509,10 @@ CxPlatDataPathUdpRecvComplete( } else if (CMsg->cmsg_type == IPV6_ECN) { ECN = *(PINT)WSA_CMSG_DATA(CMsg); CXPLAT_DBG_ASSERT(ECN < UINT8_MAX); + } else if (CMsg->cmsg_type == IPV6_HOPLIMIT) { + HopLimitTTL = *(PINT)WSA_CMSG_DATA(CMsg); + CXPLAT_DBG_ASSERT(HopLimitTTL < 256); + CXPLAT_DBG_ASSERT(HopLimitTTL > 0); } } else if (CMsg->cmsg_level == IPPROTO_IP) { if (CMsg->cmsg_type == IP_PKTINFO) { @@ -3138,6 +3525,10 @@ CxPlatDataPathUdpRecvComplete( } else if (CMsg->cmsg_type == IP_ECN) { ECN = *(PINT)WSA_CMSG_DATA(CMsg); CXPLAT_DBG_ASSERT(ECN < UINT8_MAX); + } else if (CMsg->cmsg_type == IP_TTL) { + HopLimitTTL = *(PINT)WSA_CMSG_DATA(CMsg); + CXPLAT_DBG_ASSERT(HopLimitTTL < 256); + CXPLAT_DBG_ASSERT(HopLimitTTL > 0); } } else if (CMsg->cmsg_level == IPPROTO_UDP) { if (CMsg->cmsg_type == UDP_COALESCED_INFO) { @@ -3195,8 +3586,9 @@ CxPlatDataPathUdpRecvComplete( Datagram->PartitionIndex = SocketProc->DatapathProc->PartitionIndex % SocketProc->DatapathProc->Datapath->PartitionCount; Datagram->TypeOfService = (uint8_t)ECN; + Datagram->HopLimitTTL = (uint8_t) HopLimitTTL; Datagram->Allocated = TRUE; - Datagram->Route->DatapathType = Datagram->DatapathType = CXPLAT_DATAPATH_TYPE_USER; + Datagram->Route->DatapathType = Datagram->DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; Datagram->QueuedOnConnection = FALSE; RecvPayload += MessageLength; @@ -3455,7 +3847,7 @@ CxPlatDataPathTcpRecvComplete( Data->PartitionIndex = SocketProc->DatapathProc->PartitionIndex; Data->TypeOfService = 0; Data->Allocated = TRUE; - Data->Route->DatapathType = Data->DatapathType = CXPLAT_DATAPATH_TYPE_USER; + Data->Route->DatapathType = Data->DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; Data->QueuedOnConnection = FALSE; IoBlock->ReferenceCount++; IoBlock = NULL; @@ -3682,7 +4074,7 @@ SendDataAlloc( SendData->WsaBufferCount = 0; SendData->ClientBuffer.len = 0; SendData->ClientBuffer.buf = NULL; - SendData->DatapathType = Config->Route->DatapathType = CXPLAT_DATAPATH_TYPE_USER; + SendData->DatapathType = Config->Route->DatapathType = CXPLAT_DATAPATH_TYPE_NORMAL; #if DEBUG SendData->Sqe.IoType = 0; #endif diff --git a/src/platform/datapath_xplat.c b/src/platform/datapath_xplat.c index 308a36df91..c68ccb4047 100644 --- a/src/platform/datapath_xplat.c +++ b/src/platform/datapath_xplat.c @@ -116,10 +116,10 @@ CxPlatDataPathIsPaddingPreferred( ) { CXPLAT_DBG_ASSERT( - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER || + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL || DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_RAW); return - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER ? + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL ? DataPathIsPaddingPreferred(Datapath) : RawDataPathIsPaddingPreferred(Datapath); } @@ -270,9 +270,9 @@ CxPlatRecvDataReturn( return; } CXPLAT_DBG_ASSERT( - RecvDataChain->DatapathType == CXPLAT_DATAPATH_TYPE_USER || + RecvDataChain->DatapathType == CXPLAT_DATAPATH_TYPE_NORMAL || RecvDataChain->DatapathType == CXPLAT_DATAPATH_TYPE_RAW); - RecvDataChain->DatapathType == CXPLAT_DATAPATH_TYPE_USER ? + RecvDataChain->DatapathType == CXPLAT_DATAPATH_TYPE_NORMAL ? RecvDataReturn(RecvDataChain) : RawRecvDataReturn(RecvDataChain); } @@ -303,9 +303,9 @@ CxPlatSendDataFree( ) { CXPLAT_DBG_ASSERT( - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER || + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL || DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_RAW); - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER ? + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL ? SendDataFree(SendData) : RawSendDataFree(SendData); } @@ -318,10 +318,10 @@ CxPlatSendDataAllocBuffer( ) { CXPLAT_DBG_ASSERT( - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER || + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL || DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_RAW); return - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER ? + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL ? SendDataAllocBuffer(SendData, MaxBufferLength) : RawSendDataAllocBuffer(SendData, MaxBufferLength); } @@ -333,9 +333,9 @@ CxPlatSendDataFreeBuffer( ) { CXPLAT_DBG_ASSERT( - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER || + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL || DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_RAW); - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER ? + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL ? SendDataFreeBuffer(SendData, Buffer) : RawSendDataFreeBuffer(SendData, Buffer); } @@ -346,9 +346,9 @@ CxPlatSendDataIsFull( ) { CXPLAT_DBG_ASSERT( - DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER || + DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL || DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_RAW); - return DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER ? + return DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL ? SendDataIsFull(SendData) : RawSendDataIsFull(SendData); } @@ -360,7 +360,7 @@ CxPlatSocketSend( _In_ CXPLAT_SEND_DATA* SendData ) { - if (DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_USER) { + if (DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_NORMAL) { SocketSend(Socket, Route, SendData); } else { CXPLAT_DBG_ASSERT(DatapathType(SendData) == CXPLAT_DATAPATH_TYPE_RAW); @@ -378,7 +378,7 @@ QuicCopyRouteInfo( if (SrcRoute->DatapathType == CXPLAT_DATAPATH_TYPE_RAW) { CxPlatCopyMemory(DstRoute, SrcRoute, (uint8_t*)&SrcRoute->State - (uint8_t*)SrcRoute); CxPlatUpdateRoute(DstRoute, SrcRoute); - } else if (SrcRoute->DatapathType == CXPLAT_DATAPATH_TYPE_USER) { + } else if (SrcRoute->DatapathType == CXPLAT_DATAPATH_TYPE_NORMAL) { *DstRoute = *SrcRoute; } else { CXPLAT_DBG_ASSERT(FALSE); @@ -393,7 +393,7 @@ CxPlatResolveRouteComplete( _In_ uint8_t PathId ) { - CXPLAT_DBG_ASSERT(Route->DatapathType != CXPLAT_DATAPATH_TYPE_USER); + CXPLAT_DBG_ASSERT(Route->DatapathType != CXPLAT_DATAPATH_TYPE_NORMAL); if (Route->State != RouteResolved) { RawResolveRouteComplete(Context, Route, PhysicalAddress, PathId); } diff --git a/src/platform/platform.kernel.vcxproj b/src/platform/platform.kernel.vcxproj index 765fdf67af..6e1f4506de 100644 --- a/src/platform/platform.kernel.vcxproj +++ b/src/platform/platform.kernel.vcxproj @@ -22,6 +22,9 @@ + + + diff --git a/src/platform/platform_internal.h b/src/platform/platform_internal.h index be58ef523d..11d064fc4a 100644 --- a/src/platform/platform_internal.h +++ b/src/platform/platform_internal.h @@ -61,6 +61,13 @@ typedef struct CXPLAT_DATAPATH_COMMON { // The Worker WorkerPool // CXPLAT_WORKER_POOL* WorkerPool; + + // + // Set of supported features. + // + uint32_t Features; + + CXPLAT_DATAPATH_RAW* RawDataPath; } CXPLAT_DATAPATH_COMMON; typedef struct CXPLAT_SOCKET_COMMON { @@ -73,6 +80,21 @@ typedef struct CXPLAT_SOCKET_COMMON { // The remote address and port. // QUIC_ADDR RemoteAddress; + + // + // Parent datapath. + // + CXPLAT_DATAPATH* Datapath; + + // + // The client context for this binding. + // + void *ClientContext; + + // + // The local interface's MTU. + // + uint16_t Mtu; } CXPLAT_SOCKET_COMMON; typedef struct CXPLAT_SEND_DATA_COMMON { @@ -82,11 +104,21 @@ typedef struct CXPLAT_SEND_DATA_COMMON { // The type of ECN markings needed for send. // uint8_t ECN; // CXPLAT_ECN_TYPE + + // + // The total buffer size for WsaBuffers. + // + uint32_t TotalSize; + + // + // The send segmentation size; zero if segmentation is not performed. + // + uint16_t SegmentSize; } CXPLAT_SEND_DATA_COMMON; typedef enum CXPLAT_DATAPATH_TYPE { CXPLAT_DATAPATH_TYPE_UNKNOWN = 0, - CXPLAT_DATAPATH_TYPE_USER, + CXPLAT_DATAPATH_TYPE_NORMAL, CXPLAT_DATAPATH_TYPE_RAW, // currently raw == xdp } CXPLAT_DATAPATH_TYPE; @@ -137,6 +169,9 @@ typedef enum CXPLAT_SOCKET_TYPE { #define CXPLAT_BASE_REG_PATH L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\MsQuic\\Parameters\\" +#define SOCKET PWSK_SOCKET +#define INVALID_SOCKET NULL + typedef struct CX_PLATFORM { // @@ -159,6 +194,134 @@ typedef struct CX_PLATFORM { } CX_PLATFORM; +typedef struct _WSK_DATAGRAM_SOCKET { + const WSK_PROVIDER_DATAGRAM_DISPATCH* Dispatch; +} WSK_DATAGRAM_SOCKET, * PWSK_DATAGRAM_SOCKET; + +// +// Per-port state. +// +typedef struct CXPLAT_SOCKET { + CXPLAT_SOCKET_COMMON; + + // + // Flag indicates the binding has a default remote destination. + // + BOOLEAN Connected : 1; + + // + // Flag indicates the binding is being used for PCP. + // + BOOLEAN PcpBinding : 1; + + // + // UDP socket used for sending/receiving datagrams. + // + union { + PWSK_SOCKET Socket; + PWSK_DATAGRAM_SOCKET DgrmSocket; + }; + + // + // Event used to wait for completion of socket functions. + // + CXPLAT_EVENT WskCompletionEvent; + + // + // IRP used for socket functions. + // + union { + IRP Irp; + UCHAR IrpBuffer[sizeof(IRP) + sizeof(IO_STACK_LOCATION)]; + }; + + uint8_t UseTcp : 1; // always false? + uint8_t RawSocketAvailable : 1; + + CXPLAT_RUNDOWN_REF Rundown[0]; // Per-proc + +} CXPLAT_SOCKET; + +// +// Represents the per-processor state of the datapath context. +// +typedef struct CXPLAT_DATAPATH_PROC_CONTEXT { + + // + // Pool of send contexts to be shared by all sockets on this core. + // + CXPLAT_POOL SendDataPool; + + // + // Pool of send buffers to be shared by all sockets on this core. + // + CXPLAT_POOL SendBufferPool; + + // + // Pool of large segmented send buffers to be shared by all sockets on this + // core. + // + CXPLAT_POOL LargeSendBufferPool; + + // + // Pool of receive datagram contexts and buffers to be shared by all sockets + // on this core. Index 0 is regular, Index 1 is URO. + // + // + CXPLAT_POOL RecvDatagramPools[2]; + + // + // Pool of receive data buffers. Index 0 is 4096, Index 1 is 65536. + // + CXPLAT_POOL RecvBufferPools[2]; + + int64_t OutstandingPendingBytes; + +} CXPLAT_DATAPATH_PROC_CONTEXT; + +// +// Structure that maintains all the internal state for the +// CxPlatDataPath interface. +// +typedef struct CXPLAT_DATAPATH { + CXPLAT_DATAPATH_COMMON; + + // + // The registration with WinSock Kernel. + // + WSK_REGISTRATION WskRegistration; + WSK_PROVIDER_NPI WskProviderNpi; + WSK_CLIENT_DATAGRAM_DISPATCH WskDispatch; + + // + // The size of the buffer to allocate for client's receive context structure. + // + uint32_t ClientRecvDataLength; + + // + // The size of each receive datagram array element, including client context, + // internal context, and padding. + // + uint32_t DatagramStride; + + // + // The number of processors. + // + uint32_t ProcCount; + + uint8_t UseTcp : 1; // Not supported. always false + + // + // Per-processor completion contexts. + // + CXPLAT_DATAPATH_PROC_CONTEXT ProcContexts[0]; + +} CXPLAT_DATAPATH; + +#ifndef htonl +#define htonl _byteswap_ulong +#endif + #elif _WIN32 #pragma warning(push) @@ -396,11 +559,6 @@ typedef struct CXPLAT_DATAPATH { // CXPLAT_REF_COUNT RefCount; - // - // Set of supported features. - // - uint32_t Features; - // // The size of each receive datagram array element, including client context, // internal context, and padding. @@ -436,8 +594,6 @@ typedef struct CXPLAT_DATAPATH { uint8_t UseTcp : 1; - CXPLAT_DATAPATH_RAW* RawDataPath; - // // Per-processor completion contexts. // @@ -451,17 +607,6 @@ typedef struct CXPLAT_DATAPATH { typedef struct CXPLAT_SOCKET { CXPLAT_SOCKET_COMMON; - // - // Parent datapath. - // - // CXPLAT_DATAPATH_BASE* Datapath; - CXPLAT_DATAPATH* Datapath; - - // - // Client context pointer. - // - void *ClientContext; - // // Synchronization mechanism for cleanup. // @@ -472,11 +617,6 @@ typedef struct CXPLAT_SOCKET { // uint32_t RecvBufLen; - // - // The local interface's MTU. - // - uint16_t Mtu; - // // Indicates the binding connected to a remote IP address. // @@ -530,11 +670,6 @@ typedef struct CXPLAT_SOCKET { } CXPLAT_SOCKET; -#define IS_LOOPBACK(Address) ((Address.si_family == QUIC_ADDRESS_FAMILY_INET && \ - Address.Ipv4.sin_addr.S_un.S_addr == htonl(INADDR_LOOPBACK)) || \ - (Address.si_family == QUIC_ADDRESS_FAMILY_INET6 && \ - IN6_IS_ADDR_LOOPBACK(&Address.Ipv6.sin6_addr))) - #elif defined(CX_PLATFORM_LINUX) || defined(CX_PLATFORM_DARWIN) typedef struct CX_PLATFORM { @@ -630,7 +765,12 @@ CxPlatConvertFromMappedV6( } #pragma warning(pop) -#endif +#define IS_LOOPBACK(Address) ((Address.si_family == QUIC_ADDRESS_FAMILY_INET && \ + IN4_IS_ADDR_LOOPBACK(&Address.Ipv4.sin_addr)) || \ + (Address.si_family == QUIC_ADDRESS_FAMILY_INET6 && \ + IN6_IS_ADDR_LOOPBACK(&Address.Ipv6.sin6_addr))) + +#endif // _WIN32 // // Crypt Initialization @@ -771,26 +911,11 @@ typedef struct QUIC_CACHEALIGN CXPLAT_SOCKET_CONTEXT { typedef struct CXPLAT_SOCKET { CXPLAT_SOCKET_COMMON; - // - // A pointer to datapath object. - // - CXPLAT_DATAPATH* Datapath; - - // - // The client context for this binding. - // - void *ClientContext; - // // Synchronization mechanism for cleanup. // CXPLAT_REF_COUNT RefCount; - // - // The MTU for this binding. - // - uint16_t Mtu; - // // The size of a receive buffer's payload. // @@ -898,11 +1023,6 @@ typedef struct CXPLAT_DATAPATH { // CXPLAT_REF_COUNT RefCount; - // - // Set of supported features. - // - uint32_t Features; - // // The proc count to create per proc datapath state. // @@ -946,8 +1066,6 @@ typedef struct CXPLAT_DATAPATH { uint8_t UseTcp : 1; - CXPLAT_DATAPATH_RAW* RawDataPath; - // // The per proc datapath contexts. // diff --git a/src/platform/platform_worker.c b/src/platform/platform_worker.c index 00b7937cfa..ffe4c73d22 100644 --- a/src/platform/platform_worker.c +++ b/src/platform/platform_worker.c @@ -162,6 +162,9 @@ CxPlatWorkerPoolLazyStart( if (Config->Flags & QUIC_EXECUTION_CONFIG_FLAG_HIGH_PRIORITY) { ThreadFlags |= CXPLAT_THREAD_FLAG_HIGH_PRIORITY; } + if (Config->Flags & QUIC_EXECUTION_CONFIG_FLAG_AFFINITIZE) { + ThreadFlags |= CXPLAT_THREAD_FLAG_SET_AFFINITIZE; + } } CXPLAT_THREAD_CONFIG ThreadConfig = { diff --git a/src/platform/tls_openssl.c b/src/platform/tls_openssl.c index 429ba83cf4..38f34fadca 100644 --- a/src/platform/tls_openssl.c +++ b/src/platform/tls_openssl.c @@ -981,7 +981,8 @@ CxPlatTlsSecConfigCreate( CredConfigFlags & QUIC_CREDENTIAL_FLAG_IGNORE_NO_REVOCATION_CHECK || CredConfigFlags & QUIC_CREDENTIAL_FLAG_IGNORE_REVOCATION_OFFLINE || CredConfigFlags & QUIC_CREDENTIAL_FLAG_CACHE_ONLY_URL_RETRIEVAL || - CredConfigFlags & QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY)) { + CredConfigFlags & QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY || + CredConfigFlags & QUIC_CREDENTIAL_FLAG_DISABLE_AIA)) { return QUIC_STATUS_INVALID_PARAMETER; } @@ -992,7 +993,8 @@ CxPlatTlsSecConfigCreate( CredConfigFlags & QUIC_CREDENTIAL_FLAG_IGNORE_NO_REVOCATION_CHECK || CredConfigFlags & QUIC_CREDENTIAL_FLAG_IGNORE_REVOCATION_OFFLINE || CredConfigFlags & QUIC_CREDENTIAL_FLAG_CACHE_ONLY_URL_RETRIEVAL || - CredConfigFlags & QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY)) { + CredConfigFlags & QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY || + CredConfigFlags & QUIC_CREDENTIAL_FLAG_DISABLE_AIA)) { return QUIC_STATUS_INVALID_PARAMETER; } #endif diff --git a/src/platform/tls_schannel.c b/src/platform/tls_schannel.c index b7b7735013..4afaa8083f 100644 --- a/src/platform/tls_schannel.c +++ b/src/platform/tls_schannel.c @@ -118,6 +118,7 @@ typedef struct _SecPkgCred_ClientCertPolicy #define CERT_CHAIN_REVOCATION_CHECK_CHAIN 0x20000000 #define CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT 0x40000000 #define CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY 0x80000000 +#define CERT_CHAIN_DISABLE_AIA 0x00002000 #define SECPKG_ATTR_REMOTE_CERTIFICATES 0x5F // returns SecPkgContext_Certificates @@ -754,6 +755,9 @@ CxPlatTlsSetClientCertPolicy( if (SecConfig->Flags & QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY) { ClientCertPolicy.dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY; } + if (SecConfig->Flags & QUIC_CREDENTIAL_FLAG_DISABLE_AIA) { + ClientCertPolicy.dwCertFlags |= CERT_CHAIN_DISABLE_AIA; + } SecStatus = SetCredentialsAttributesW( diff --git a/src/platform/unittest/CMakeLists.txt b/src/platform/unittest/CMakeLists.txt index e514d6634a..997066bb10 100644 --- a/src/platform/unittest/CMakeLists.txt +++ b/src/platform/unittest/CMakeLists.txt @@ -20,7 +20,7 @@ set_property(TARGET msquicplatformtest APPEND PROPERTY BUILD_RPATH "$ORIGIN") target_link_libraries(msquicplatformtest msquic) if (BUILD_SHARED_LIBS) - target_link_libraries(msquicplatformtest platform) + target_link_libraries(msquicplatformtest msquic_platform) endif() target_link_libraries(msquicplatformtest inc gtest warnings logging base_link) diff --git a/src/platform/unittest/DataPathTest.cpp b/src/platform/unittest/DataPathTest.cpp index 33906ba672..f64d11e0c5 100644 --- a/src/platform/unittest/DataPathTest.cpp +++ b/src/platform/unittest/DataPathTest.cpp @@ -86,6 +86,7 @@ struct UdpRecvContext { QUIC_ADDR DestinationAddress; CXPLAT_EVENT ClientCompletion; CXPLAT_ECN_TYPE EcnType {CXPLAT_ECN_NON_ECT}; + bool TtlSupported; UdpRecvContext() { CxPlatEventInitialize(&ClientCompletion, FALSE, FALSE); } @@ -286,13 +287,18 @@ struct DataPathTest : public ::testing::TestWithParam { UdpRecvContext* RecvContext = (UdpRecvContext*)Context; ASSERT_NE(nullptr, RecvContext); - CXPLAT_RECV_DATA* RecvData = RecvDataChain; while (RecvData != NULL) { ASSERT_EQ(RecvData->BufferLength, ExpectedDataSize); ASSERT_EQ(0, memcmp(RecvData->Buffer, ExpectedData, ExpectedDataSize)); + if (RecvContext->TtlSupported) { + ASSERT_TRUE(RecvData->HopLimitTTL > 0); + } else { + ASSERT_EQ(0, RecvData->HopLimitTTL); + } + if (RecvData->Route->LocalAddress.Ipv4.sin_port == RecvContext->DestinationAddress.Ipv4.sin_port) { ASSERT_EQ((CXPLAT_ECN_TYPE)RecvData->TypeOfService, RecvContext->EcnType); @@ -775,6 +781,7 @@ TEST_P(DataPathTest, UdpData) { UdpRecvContext RecvContext; CxPlatDataPath Datapath(&UdpRecvCallbacks); + RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); @@ -812,6 +819,7 @@ TEST_P(DataPathTest, UdpDataPolling) QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NONE, UINT32_MAX, 0 }; UdpRecvContext RecvContext; CxPlatDataPath Datapath(&UdpRecvCallbacks, nullptr, 0, &Config); + RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); @@ -848,6 +856,7 @@ TEST_P(DataPathTest, UdpDataRebind) { UdpRecvContext RecvContext; CxPlatDataPath Datapath(&UdpRecvCallbacks); + RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); @@ -904,6 +913,7 @@ TEST_P(DataPathTest, UdpDataECT0) UdpRecvContext RecvContext; RecvContext.EcnType = CXPLAT_ECN_ECT_0; CxPlatDataPath Datapath(&UdpRecvCallbacks); + RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); @@ -940,6 +950,7 @@ TEST_P(DataPathTest, UdpShareClientSocket) { UdpRecvContext RecvContext; CxPlatDataPath Datapath(&UdpRecvCallbacks); + RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); VERIFY_QUIC_SUCCESS(Datapath.GetInitStatus()); ASSERT_NE(nullptr, Datapath.Datapath); // TODO: Linux XDP (duonic) to support port sharing @@ -1001,6 +1012,7 @@ TEST_P(DataPathTest, UdpShareClientSocket) TEST_P(DataPathTest, MultiBindListener) { UdpRecvContext RecvContext; CxPlatDataPath Datapath(&UdpRecvCallbacks); + RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); if (!(Datapath.GetSupportedFeatures() & CXPLAT_DATAPATH_FEATURE_PORT_RESERVATIONS)) { std::cout << "SKIP: Port Reservations Feature Unsupported" << std::endl; return; @@ -1022,6 +1034,7 @@ TEST_P(DataPathTest, MultiBindListenerSingleProcessor) { UdpRecvContext RecvContext; QUIC_EXECUTION_CONFIG Config = { QUIC_EXECUTION_CONFIG_FLAG_NO_IDEAL_PROC, UINT32_MAX, 1, 0 }; CxPlatDataPath Datapath(&UdpRecvCallbacks, nullptr, 0, &Config); + RecvContext.TtlSupported = Datapath.IsSupported(CXPLAT_DATAPATH_FEATURE_TTL); auto ServerAddress = GetNewLocalAddr(); CxPlatSocket Server1(Datapath, &ServerAddress.SockAddr, nullptr, &RecvContext); diff --git a/src/platform/unittest/TlsTest.cpp b/src/platform/unittest/TlsTest.cpp index 04af42af9b..19c77b829e 100644 --- a/src/platform/unittest/TlsTest.cpp +++ b/src/platform/unittest/TlsTest.cpp @@ -2222,6 +2222,7 @@ TEST_F(TlsTest, PlatformSpecificFlagsSchannel) QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_END_CERT, QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, QUIC_CREDENTIAL_FLAG_IGNORE_NO_REVOCATION_CHECK, QUIC_CREDENTIAL_FLAG_IGNORE_REVOCATION_OFFLINE, QUIC_CREDENTIAL_FLAG_CACHE_ONLY_URL_RETRIEVAL, QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY, + QUIC_CREDENTIAL_FLAG_DISABLE_AIA, #ifndef __APPLE__ QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CHAIN, #endif diff --git a/src/platform/unittest/external/CMakeLists.txt b/src/platform/unittest/external/CMakeLists.txt new file mode 100644 index 0000000000..cfb1ccba9b --- /dev/null +++ b/src/platform/unittest/external/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +cmake_minimum_required(VERSION 3.16) + +project(msquic_platform_external) + +find_package(msquic CONFIG REQUIRED) + +message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") + +include(FetchContent) +FetchContent_Declare( + googletest + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../submodules/googletest +) +FetchContent_MakeAvailable(googletest) + +set(SOURCES + ../main.cpp + ../CryptTest.cpp + ../DataPathTest.cpp + ../PlatformTest.cpp + # ../StorageTest.cpp + ../TlsTest.cpp +) + +# Remove QUIC_EVENTS_MANIFEST_ETW and QUIC_LOGS_MANIFEST_ETW definitions. These definitions for internal use in msquic +get_target_property(defs msquic::inc INTERFACE_COMPILE_DEFINITIONS) +list(FILTER defs EXCLUDE REGEX "^(QUIC_EVENTS_MANIFEST_ETW|QUIC_LOGS_MANIFEST_ETW)$") +set_target_properties(msquic::inc PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${defs}") + +add_executable(msquicplatformtest ${SOURCES}) +target_include_directories(msquicplatformtest PRIVATE ${CMAKE_INSTALL_PREFIX}/include) +target_link_libraries(msquicplatformtest PRIVATE gtest msquic::msquic msquic::msquic_platform ws2_32 ntdll ncrypt crypt32 iphlpapi) +target_compile_definitions(msquicplatformtest PRIVATE QUIC_EVENTS_STUB QUIC_LOGS_STUB _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION) diff --git a/src/test/MsQuicTests.h b/src/test/MsQuicTests.h index f75e1a8aa7..aaae75274e 100644 --- a/src/test/MsQuicTests.h +++ b/src/test/MsQuicTests.h @@ -712,6 +712,7 @@ static const GUID QUIC_TEST_DEVICE_INSTANCE = typedef struct { BOOLEAN UseDuoNic; + QUIC_EXECUTION_CONFIG Config; char CurrentDirectory[MAX_PATH]; } QUIC_TEST_CONFIGURATION_PARAMS; diff --git a/src/test/bin/CMakeLists.txt b/src/test/bin/CMakeLists.txt index 895d64d053..c4388894f5 100644 --- a/src/test/bin/CMakeLists.txt +++ b/src/test/bin/CMakeLists.txt @@ -16,7 +16,7 @@ set_property(TARGET msquictest APPEND PROPERTY BUILD_RPATH "$ORIGIN") target_link_libraries(msquictest msquic testlib) if (BUILD_SHARED_LIBS) - target_link_libraries(msquictest platform) + target_link_libraries(msquictest msquic_platform) endif() target_link_libraries(msquictest inc gtest logging base_link) diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp index c717d79bb2..2010fb93bd 100644 --- a/src/test/bin/quic_gtest.cpp +++ b/src/test/bin/quic_gtest.cpp @@ -63,6 +63,7 @@ class QuicTestEnvironment : public ::testing::Environment { TRUE, NULL )) != nullptr); + QUIC_EXECUTION_CONFIG Config = {QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0, {0}}; if (TestingKernelMode) { printf("Initializing for Kernel Mode tests\n"); const char* DriverName; @@ -88,8 +89,14 @@ class QuicTestEnvironment : public ::testing::Environment { ASSERT_TRUE(DriverService.Start()); ASSERT_TRUE(DriverClient.Initialize(&CertParams, DriverName)); +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) + if (UseDuoNic) { + Config.Flags |= QUIC_EXECUTION_CONFIG_FLAG_XDP; + } +#endif QUIC_TEST_CONFIGURATION_PARAMS Params { UseDuoNic, + Config, }; #ifdef _WIN32 @@ -104,7 +111,6 @@ class QuicTestEnvironment : public ::testing::Environment { MsQuic = new(std::nothrow) MsQuicApi(); ASSERT_TRUE(QUIC_SUCCEEDED(MsQuic->GetInitStatus())); #if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) - QUIC_EXECUTION_CONFIG Config = {QUIC_EXECUTION_CONFIG_FLAG_NONE, 0, 0, {0}}; if (UseQTIP) { Config.PollingIdleTimeoutUs = 10000; Config.Flags |= QUIC_EXECUTION_CONFIG_FLAG_QTIP; diff --git a/src/test/bin/winkernel/control.cpp b/src/test/bin/winkernel/control.cpp index 839457729c..bdeab35fd3 100644 --- a/src/test/bin/winkernel/control.cpp +++ b/src/test/bin/winkernel/control.cpp @@ -690,6 +690,19 @@ QuicTestCtlEvtIoDeviceControl( nullptr, nullptr, STRSAFE_NULL_ON_FAILURE); + +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) + QUIC_EXECUTION_CONFIG Config = Params->TestConfigurationParams.Config; + if (Config.Flags != QUIC_EXECUTION_CONFIG_FLAG_NONE) { + Status = + MsQuic->SetParam( + nullptr, + QUIC_PARAM_GLOBAL_EXECUTION_CONFIG, + sizeof(Config), + &Config); + } +#endif + break; case IOCTL_QUIC_SET_CERT_PARAMS: diff --git a/src/test/lib/ApiTest.cpp b/src/test/lib/ApiTest.cpp index 4935cf1d65..71bda9c8ef 100644 --- a/src/test/lib/ApiTest.cpp +++ b/src/test/lib/ApiTest.cpp @@ -2090,6 +2090,32 @@ void SettingApplyTests(HQUIC Handle, uint32_t Param, bool AllowMtuEcnChanges = t sizeof(QUIC_SETTINGS), &Settings)); } + + // + // MaxOperationsPerDrain + // + { + QUIC_SETTINGS Settings{0}; + Settings.IsSet.MaxOperationsPerDrain = TRUE; + + Settings.MaxOperationsPerDrain = 0; // Not allowed + TEST_QUIC_STATUS( + QUIC_STATUS_INVALID_PARAMETER, + MsQuic->SetParam( + Handle, + Param, + sizeof(QUIC_SETTINGS), + &Settings)); + + Settings.MaxOperationsPerDrain = 255; // Max allowed + TEST_QUIC_STATUS( + QUIC_STATUS_SUCCESS, + MsQuic->SetParam( + Handle, + Param, + sizeof(QUIC_SETTINGS), + &Settings)); + } } void QuicTestStatefulGlobalSetParam() diff --git a/src/test/lib/DataTest.cpp b/src/test/lib/DataTest.cpp index 2d462ff08e..e6bd872cad 100644 --- a/src/test/lib/DataTest.cpp +++ b/src/test/lib/DataTest.cpp @@ -3604,13 +3604,13 @@ struct ConnectionPriorityTestContext { const uint8_t ConnectionPriorityTestContext::NumSend = 3; typedef void ConnectionPriorityTestType( - MsQuicConnection** Connections, + UniquePtr Connections[], uint8_t NumConnections, MsQuicStream** Streams, QUIC_BUFFER Buffer ); -static void ConnectionPriorityTest1(MsQuicConnection* Connections[], uint8_t NumConnections, MsQuicStream* Streams[], QUIC_BUFFER Buffer) { +static void ConnectionPriorityTest1(UniquePtr Connections[], uint8_t NumConnections, MsQuicStream* Streams[], QUIC_BUFFER Buffer) { // s: stream op // p: prioritized op // s0/s2: stream start, stream send @@ -3666,7 +3666,7 @@ static void ConnectionPriorityTest1(MsQuicConnection* Connections[], uint8_t Num } } -static void ConnectionPriorityTest2(MsQuicConnection* Connections[], uint8_t NumConnections, MsQuicStream* Streams[], QUIC_BUFFER Buffer) { +static void ConnectionPriorityTest2(UniquePtr Connections[], uint8_t NumConnections, MsQuicStream* Streams[], QUIC_BUFFER Buffer) { // processing | queued // [1[s0]] | [] // [1[s0]] | [2[s(n-1)0p, s(n-1)2p, s00, s02, ..., s(n-2)0, s(n-2)2]] @@ -3730,7 +3730,7 @@ static void ConnectionPriorityTest2(MsQuicConnection* Connections[], uint8_t Num } } -static void ConnectionPriorityTest3(MsQuicConnection* Connections[], uint8_t NumConnections, MsQuicStream* Streams[], QUIC_BUFFER Buffer) { +static void ConnectionPriorityTest3(UniquePtr Connections[], uint8_t NumConnections, MsQuicStream* Streams[], QUIC_BUFFER Buffer) { // processing | queued // [1[s0]] | [] // [1[s0]] | [5[s0, s2]] @@ -3807,9 +3807,9 @@ void ConnectionPriorityCommon(ConnectionPriorityTestType* ConnectionPriorityTest const uint8_t NumConnections = 8; { - MsQuicConnection *Connections[NumConnections] = {0}; + UniquePtr Connections[NumConnections]; for (uint8_t i = 0; i < NumConnections; ++i) { - Connections[i] = new(std::nothrow) MsQuicConnection(Registration); + Connections[i].reset(new(std::nothrow) MsQuicConnection(Registration)); TEST_QUIC_SUCCEEDED(Connections[i]->GetInitStatus()); TEST_QUIC_SUCCEEDED(Connections[i]->Start(ClientConfiguration, ServerLocalAddr.GetFamily(), QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()), ServerLocalAddr.GetPort())); TEST_TRUE(Connections[i]->HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); @@ -3832,10 +3832,6 @@ void ConnectionPriorityCommon(ConnectionPriorityTestType* ConnectionPriorityTest } ConnectionPriorityTest(Connections, NumConnections, Streams, Buffer); - - for (uint8_t i = 0; i < NumConnections; ++i) { - delete Connections[i]; - } } } diff --git a/src/test/lib/HandshakeTest.cpp b/src/test/lib/HandshakeTest.cpp index 71cddd74c5..edb6e714d8 100644 --- a/src/test/lib/HandshakeTest.cpp +++ b/src/test/lib/HandshakeTest.cpp @@ -317,6 +317,13 @@ QuicTestConnect( } TEST_TRUE(Client.GetIsConnected()); + // After handshake, check and see if we have cached the TTL of the handshake packet. + if (QuitTestIsFeatureSupported(CXPLAT_DATAPATH_FEATURE_TTL)) { + TEST_TRUE(Client.GetStatistics().HandshakeHopLimitTTL > 0); + } else { + TEST_EQUAL(Client.GetStatistics().HandshakeHopLimitTTL, 0); + } + TEST_NOT_EQUAL(nullptr, Server); Server->SetSslKeyLogFilePath(); if (!Server->WaitForConnectionComplete()) { diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 48519c06c1..14582b3d7a 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -9,7 +9,7 @@ function(add_quic_tool) target_link_libraries(${targetname} inc warnings msquic) if (BUILD_SHARED_LIBS) - target_link_libraries(${targetname} platform) + target_link_libraries(${targetname} msquic_platform) endif() target_link_libraries(${targetname} logging base_link) diff --git a/src/tools/attack/CMakeLists.txt b/src/tools/attack/CMakeLists.txt index 23fcf87861..04ef2eb761 100644 --- a/src/tools/attack/CMakeLists.txt +++ b/src/tools/attack/CMakeLists.txt @@ -12,7 +12,7 @@ target_include_directories(quicattack PRIVATE ${PROJECT_SOURCE_DIR}/src/core) target_link_libraries(quicattack) if (BUILD_SHARED_LIBS) - target_link_libraries(quicattack core platform) + target_link_libraries(quicattack core msquic_platform) endif() target_link_libraries(quicattack logging) diff --git a/src/tools/pcp/CMakeLists.txt b/src/tools/pcp/CMakeLists.txt index 43761a8758..f9f6d6d50e 100644 --- a/src/tools/pcp/CMakeLists.txt +++ b/src/tools/pcp/CMakeLists.txt @@ -8,5 +8,5 @@ set(SOURCES add_quic_tool(quicpcp ${SOURCES}) target_include_directories(quicpcp PRIVATE ${PROJECT_SOURCE_DIR}/src/core) -# OK to include platform a second time, will not cause multiple link issues -target_link_libraries(quicpcp core platform) +# OK to include msquic_platform a second time, will not cause multiple link issues +target_link_libraries(quicpcp core msquic_platform) diff --git a/src/tools/recvfuzz/CMakeLists.txt b/src/tools/recvfuzz/CMakeLists.txt index 60af99758c..c8d56aead8 100644 --- a/src/tools/recvfuzz/CMakeLists.txt +++ b/src/tools/recvfuzz/CMakeLists.txt @@ -7,5 +7,5 @@ endif() add_quic_tool(recvfuzz recvfuzz.cpp) target_include_directories(recvfuzz PRIVATE ${PROJECT_SOURCE_DIR}/src/core) if (BUILD_SHARED_LIBS) - target_link_libraries(recvfuzz core msquic platform) + target_link_libraries(recvfuzz core msquic msquic_platform) endif() diff --git a/submodules/CMakeLists.txt b/submodules/CMakeLists.txt index a6c5929512..4bf8117629 100644 --- a/submodules/CMakeLists.txt +++ b/submodules/CMakeLists.txt @@ -157,8 +157,9 @@ if (WIN32) target_include_directories( OpenSSLQuic INTERFACE - $<$:${OPENSSL_DIR}/debug/include> - $<$>:${OPENSSL_DIR}/release/include> + $:${OPENSSL_DIR}/debug/include> + $<$>:${OPENSSL_DIR}/release/include>> + $ ) target_link_libraries( OpenSSLQuic @@ -327,7 +328,8 @@ else() target_include_directories( OpenSSLQuic INTERFACE - ${OPENSSL_DIR}/include + $ + $ ) target_link_libraries( diff --git a/submodules/googletest b/submodules/googletest index 71815bbf7d..7d76a231b0 160000 --- a/submodules/googletest +++ b/submodules/googletest @@ -1 +1 @@ -Subproject commit 71815bbf7de6e10c11821d654a2fae2cf42de0f7 +Subproject commit 7d76a231b0e29caf86e68d1df858308cd53b2a66