├── .clang-format ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── logo.jpg └── main.jpg ├── corpus ├── 0a399de681be01030f153b46392d38d64d82d78a ├── 0dca570bf754fd714714c5138c4a11f107960c2d ├── 2171d39aba22a970087967e87a046abac2dce468 ├── 2b886c7f8a0d0531160b63a3ebf576f6a8dacd85 ├── 40dee7955c00d21cb26da5eb406cc7ee41a6c362 ├── 62418d0e56a8dcea60f7891d9a33b659c7b3e7bd ├── 64b68bf5b882b9bd0b37267287980ecfa0e44a85 ├── 76fd19f9ef948864613e2cbf5cfea2d95f7f28e6 ├── 80f90ef100ffec3ed550b90314ab33ecc0d8ce94 ├── 8e259d8a0b21698bf5731a964c38797b3e40e2e5 ├── 92361a23c86485ad68292506bc54ca077d111f87 ├── 94c57455b53b04173d9c2b0289a1d9cd0067d767 ├── 990336e07122731336a8fcb345b36f057e3016d4 ├── 9a52b25d098f6a4257ed48df74f60d59183d1f91 ├── 9d8401c2a100a7d416ff83f9751c25337e566233 ├── a970114d1a081060268852221c1593608594f22d ├── b2639321ec13cc077e6a70d81ebd8813a84d30ec ├── ccb4d6c5b7f9eb65f6b2f051626e1e4eae2cc0ea ├── cd8b0f2a6fffa66c0e0dfe051c19643a609f55a5 ├── db4b7b0acf91ef8ef42d9c11b524dcecb74bdf9d ├── e2d9da838028d2e97152858b474682420b59c10b ├── e328aa61973fa43cfb2aa0cbea594e16baddaa30 ├── f6afe9dda9439811b81d6f4feaaf18d706182ab6 └── ff2d1eeea04672d78870670a0cd553b03be09512 ├── cpanfile ├── examples └── echo.c ├── fuzz └── packet.cc ├── include ├── quicly.h └── quicly │ ├── cc.h │ ├── cid.h │ ├── constants.h │ ├── defaults.h │ ├── frame.h │ ├── linklist.h │ ├── local_cid.h │ ├── loss.h │ ├── maxsender.h │ ├── pacer.h │ ├── ranges.h │ ├── rate.h │ ├── recvstate.h │ ├── remote_cid.h │ ├── retire_cid.h │ ├── sendstate.h │ ├── sentmap.h │ └── streambuf.h ├── lib ├── cc-cubic.c ├── cc-pico.c ├── cc-reno.c ├── defaults.c ├── frame.c ├── local_cid.c ├── loss.c ├── quicly.c ├── ranges.c ├── rate.c ├── recvstate.c ├── remote_cid.c ├── retire_cid.c ├── sendstate.c ├── sentmap.c └── streambuf.c ├── misc ├── build_libFuzzer.sh ├── docker-ci.mk ├── find-cids.py ├── probe2trace.pl ├── qlog-adapter.py ├── quic-interop-runner │ ├── Dockerfile │ ├── deploy.sh │ ├── run_endpoint.sh │ ├── server.crt │ └── server.key └── quictrace-adapter.py ├── parselog.py ├── plot.sh ├── quicly-probes.d ├── quicly.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── src └── cli.c └── t ├── assets ├── ec256-key-pair.pem ├── ec256-pub.pem ├── server.crt └── server.key ├── cplusplus.t ├── e2e.t ├── frame.c ├── jumpstart.c ├── local_cid.c ├── loss.c ├── lossy.c ├── maxsender.c ├── pacer.c ├── ranges.c ├── rate.c ├── remote_cid.c ├── retire_cid.c ├── sentmap.c ├── simple.c ├── simulator.c ├── stream-concurrency.c ├── test.c ├── test.h └── udpfw.c /.clang-format: -------------------------------------------------------------------------------- 1 | # requires clang-format >= 3.6 2 | BasedOnStyle: "LLVM" 3 | IndentWidth: 4 4 | ColumnLimit: 132 5 | BreakBeforeBraces: Linux 6 | AllowShortFunctionsOnASingleLine: None 7 | SortIncludes: false 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: "${{ matrix.name }}" 8 | runs-on: [ubuntu-latest] 9 | 10 | # We want to run on external PRs, but not on our own internal PRs as they'll be run 11 | # by the push to the branch. 12 | if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | include: 18 | - name: default 19 | command: make -f misc/docker-ci.mk 20 | - name: openssl-3.0 21 | command: make -f misc/docker-ci.mk CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2204 22 | - name: boringssl 23 | command: make -f misc/docker-ci.mk CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2204 CMAKE_ARGS='-DOPENSSL_ROOT_DIR=/opt/boringssl' 24 | - name: asan 25 | command: make -f misc/docker-ci.mk CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2204 CMAKE_ARGS='-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_FLAGS=-fsanitize=address -DCMAKE_CXX_FLAGS=-fsanitize=address' CHECK_ENVS='ASAN_OPTIONS=detect_leaks=0' 26 | 27 | timeout-minutes: 10 28 | steps: 29 | - uses: actions/checkout@v2 30 | with: 31 | submodules: recursive 32 | - name: setup 33 | run: | 34 | sudo sysctl -w vm.mmap_rnd_bits=28 # new default is 32 that causes libasan crashes 35 | 36 | - name: Run with Docker 37 | shell: 'script -q -e -c "bash -xe {0}"' 38 | run: | 39 | chmod -R ugo+w . 40 | ${{ matrix.command }} 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.old 3 | *.csr 4 | */*.dSYM/ 5 | .DS_Store 6 | CMakeCache.txt 7 | CMakeFiles/ 8 | Makefile 9 | build/ 10 | cmake_install.cmake 11 | xcuserdata 12 | *.xccheckout 13 | tmp/ 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/picotls"] 2 | path = deps/picotls 3 | url = https://github.com/h2o/picotls.git 4 | [submodule "deps/klib"] 5 | path = deps/klib 6 | url = https://github.com/attractivechaos/klib.git 7 | [submodule "deps/picotest"] 8 | path = deps/picotest 9 | url = https://github.com/kazuho/picotest.git 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12) 2 | CMAKE_POLICY(SET CMP0003 NEW) 3 | 4 | PROJECT(quicly) 5 | 6 | INCLUDE(CMakePushCheckState) 7 | INCLUDE(CheckCSourceCompiles) 8 | INCLUDE(deps/picotls/cmake/boringssl-adjust.cmake) 9 | INCLUDE(deps/picotls/cmake/dtrace-utils.cmake) 10 | INCLUDE(deps/picotls/cmake/fusion.cmake) 11 | 12 | FIND_PACKAGE(OpenSSL REQUIRED) 13 | BORINGSSL_ADJUST() 14 | IF (OPENSSL_FOUND AND (OPENSSL_VERSION VERSION_LESS "1.0.2")) 15 | MESSAGE(FATAL "OpenSSL 1.0.2 or above is missing") 16 | ENDIF () 17 | 18 | CHECK_DTRACE(${CMAKE_SOURCE_DIR}/deps/picotls/picotls-probes.d) 19 | OPTION(WITH_DTRACE "use USDT (userspace Dtrace probes)" ${HAVE_DTRACE}) 20 | IF (WITH_DTRACE) 21 | MESSAGE(STATUS "Enabling USDT support") 22 | ENDIF () 23 | 24 | CHECK_FUSION_PREREQUISITES() 25 | OPTION(WITH_FUSION "whether or not to use the Fusion AES-GCM engine in the cli binary" ${WITH_FUSION_DEFAULT}) 26 | 27 | # CMake defaults to a Debug build, whereas quicly defaults to an optimized (Release) build 28 | IF(NOT CMAKE_BUILD_TYPE) 29 | SET(CMAKE_BUILD_TYPE Release) 30 | ENDIF(NOT CMAKE_BUILD_TYPE) 31 | SET(CMAKE_C_FLAGS "-std=c99 -Wall -g -DQUICLY_USE_TRACER=1 ${CC_WARNING_FLAGS} ${CMAKE_C_FLAGS}") 32 | SET(CMAKE_C_FLAGS_DEBUG "-O0") 33 | SET(CMAKE_C_FLAGS_RELEASE "-O2") 34 | 35 | INCLUDE_DIRECTORIES( 36 | ${OPENSSL_INCLUDE_DIR} 37 | deps/klib 38 | deps/picotls/include 39 | deps/picotest 40 | include 41 | ${CMAKE_CURRENT_BINARY_DIR}) 42 | 43 | SET(PICOTLS_OPENSSL_FILES 44 | deps/picotls/lib/hpke.c 45 | deps/picotls/lib/openssl.c 46 | deps/picotls/lib/pembase64.c 47 | deps/picotls/lib/picotls.c) 48 | 49 | SET(QUICLY_LIBRARY_FILES 50 | lib/frame.c 51 | lib/cc-reno.c 52 | lib/cc-cubic.c 53 | lib/cc-pico.c 54 | lib/defaults.c 55 | lib/local_cid.c 56 | lib/loss.c 57 | lib/quicly.c 58 | lib/ranges.c 59 | lib/rate.c 60 | lib/recvstate.c 61 | lib/remote_cid.c 62 | lib/retire_cid.c 63 | lib/sendstate.c 64 | lib/sentmap.c 65 | lib/streambuf.c 66 | ${CMAKE_CURRENT_BINARY_DIR}/quicly-tracer.h) 67 | 68 | SET(UNITTEST_SOURCE_FILES 69 | deps/picotest/picotest.c 70 | t/frame.c 71 | t/jumpstart.c 72 | t/local_cid.c 73 | t/loss.c 74 | t/lossy.c 75 | t/maxsender.c 76 | t/pacer.c 77 | t/ranges.c 78 | t/rate.c 79 | t/remote_cid.c 80 | t/retire_cid.c 81 | t/sentmap.c 82 | t/simple.c 83 | t/stream-concurrency.c 84 | t/test.c) 85 | 86 | IF (WITH_DTRACE) 87 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPICOTLS_USE_DTRACE=1 -DQUICLY_USE_DTRACE=1") 88 | DEFINE_DTRACE_DEPENDENCIES(${CMAKE_SOURCE_DIR}/deps/picotls/picotls-probes.d picotls) 89 | DEFINE_DTRACE_DEPENDENCIES(${CMAKE_SOURCE_DIR}/quicly-probes.d quicly) 90 | LIST(APPEND PICOTLS_OPENSSL_FILES ${CMAKE_CURRENT_BINARY_DIR}/picotls-probes.h) 91 | LIST(APPEND QUICLY_LIBRARY_FILES ${CMAKE_CURRENT_BINARY_DIR}/quicly-probes.h) 92 | LIST(APPEND UNITTEST_SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/quicly-probes.h) 93 | IF (DTRACE_USES_OBJFILE) 94 | LIST(APPEND PICOTLS_OPENSSL_FILES ${CMAKE_CURRENT_BINARY_DIR}/picotls-probes.o) 95 | LIST(APPEND QUICLY_LIBRARY_FILES ${CMAKE_CURRENT_BINARY_DIR}/quicly-probes.o) 96 | LIST(APPEND UNITTEST_SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/quicly-probes.o) 97 | ENDIF () 98 | ENDIF () 99 | 100 | ADD_CUSTOM_COMMAND( 101 | OUTPUT quicly-tracer.h 102 | COMMAND ${PROJECT_SOURCE_DIR}/misc/probe2trace.pl -a tracer < ${PROJECT_SOURCE_DIR}/quicly-probes.d > ${CMAKE_CURRENT_BINARY_DIR}/quicly-tracer.h 103 | DEPENDS quicly-probes.d misc/probe2trace.pl 104 | VERBATIM) 105 | 106 | ADD_LIBRARY(quicly ${QUICLY_LIBRARY_FILES}) 107 | TARGET_LINK_LIBRARIES(quicly LINK_PUBLIC m) 108 | 109 | SET(CLI_FILES ${PICOTLS_OPENSSL_FILES} ${QUICLY_LIBRARY_FILES} src/cli.c) 110 | SET(CLI_COMPILE_FLAGS "") 111 | IF (WITH_FUSION) 112 | LIST(APPEND CLI_FILES deps/picotls/lib/fusion.c) 113 | SET(CLI_COMPILE_FLAGS "-mavx2 -maes -mpclmul -mvaes -mvpclmulqdq -DQUICLY_HAVE_FUSION=1 ${CLI_COMPILE_FLAGS}") 114 | ENDIF () 115 | ADD_EXECUTABLE(cli ${CLI_FILES}) 116 | SET_TARGET_PROPERTIES(cli PROPERTIES COMPILE_FLAGS "${CLI_COMPILE_FLAGS}") 117 | TARGET_LINK_LIBRARIES(cli ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_DL_LIBS} m) 118 | 119 | ADD_EXECUTABLE(test.t ${PICOTLS_OPENSSL_FILES} ${UNITTEST_SOURCE_FILES}) 120 | TARGET_LINK_LIBRARIES(test.t quicly ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_DL_LIBS}) 121 | 122 | ADD_EXECUTABLE(simulator ${PICOTLS_OPENSSL_FILES} ${QUICLY_LIBRARY_FILES} t/simulator.c) 123 | TARGET_LINK_LIBRARIES(simulator ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_DL_LIBS} m) 124 | 125 | ADD_EXECUTABLE(examples-echo ${PICOTLS_OPENSSL_FILES} examples/echo.c) 126 | TARGET_LINK_LIBRARIES(examples-echo quicly ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_DL_LIBS}) 127 | 128 | ADD_EXECUTABLE(udpfw t/udpfw.c) 129 | 130 | ADD_CUSTOM_TARGET(check env BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} WITH_DTRACE=${WITH_DTRACE} prove --exec "sh -c" -v ${CMAKE_CURRENT_BINARY_DIR}/*.t t/*.t 131 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 132 | DEPENDS cli udpfw test.t) 133 | 134 | ADD_CUSTOM_TARGET(format clang-format -i `git ls-files include lib src t | egrep '\\.[ch]$$'`) 135 | 136 | IF (CMAKE_SYSTEM_NAME STREQUAL "Linux") 137 | SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -pthread ${CMAKE_C_FLAGS}") 138 | ENDIF () 139 | 140 | IF (BUILD_FUZZER) 141 | MESSAGE(STATUS "************* Making the fuzzer") 142 | IF(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 143 | MESSAGE(FATAL_ERROR "The fuzzer needs clang as a compiler") 144 | ENDIF() 145 | ADD_EXECUTABLE(quicly-fuzzer-packet fuzz/packet.cc ${PICOTLS_OPENSSL_FILES}) 146 | SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_C_FLAGS}") 147 | IF (OSS_FUZZ) 148 | # Use https://github.com/google/oss-fuzz compatible options 149 | SET(LIB_FUZZER FuzzingEngine) 150 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer") 151 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") 152 | TARGET_LINK_LIBRARIES(quicly-fuzzer-packet quicly ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_DL_LIBS}) 153 | ELSEIF (USE_CLANG_RT) 154 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=fuzzer,address,undefined -fsanitize-coverage=edge,indirect-calls") 155 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fno-omit-frame-pointer -fsanitize=fuzzer,address,undefined -fsanitize-coverage=edge,indirect-calls") 156 | TARGET_LINK_LIBRARIES(quicly-fuzzer-packet quicly ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_DL_LIBS}) 157 | ELSE() 158 | SET(LIB_FUZZER "${CMAKE_CURRENT_BINARY_DIR}/libFuzzer.a") 159 | SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link") 160 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link") 161 | ADD_CUSTOM_TARGET(libFuzzer ${CMAKE_CURRENT_SOURCE_DIR}/misc/build_libFuzzer.sh WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 162 | ADD_DEPENDENCIES(quicly-fuzzer-packet libFuzzer) 163 | TARGET_LINK_LIBRARIES(quicly-fuzzer-packet quicly ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_DL_LIBS} ${LIB_FUZZER}) 164 | ENDIF(OSS_FUZZ) 165 | ENDIF() 166 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | We welcome third party contributions to quicly. The most straightforward way to 2 | do so, is to fork the project and submit a PR. If the change you're proposing 3 | is substantial, it might be a good idea to open an issue in the 4 | [issue tracker](https://github.com/h2o/h2o/issues) in order to discuss it first. 5 | 6 | By submitting a pull request, you agree to license the submitted code under 7 | [the MIT License](https://opensource.org/licenses/MIT). If you do not own the 8 | copyright of the code that is being submitted, please clarify the name of the 9 | copyright holder and the license under which the copyrighted material can be 10 | used so that we can review if it could be incorporated as part of the project. 11 | 12 | You may also want to read the [developer documentation](https://github.com/h2o/quicly/wiki#Development-Documentation), 13 | that includes: 14 | * [Coding Style](https://github.com/h2o/quicly/wiki/Coding-Style) 15 | * [Running Tests, Fuzzers](https://github.com/h2o/quicly/wiki/Running-Tests%2C-Fuzzers) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Fastly, Kazuho Oku 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | quicly [![CI](https://github.com/h2o/quicly/actions/workflows/ci.yml/badge.svg)](https://github.com/h2o/quicly/actions/workflows/ci.yml) 2 | === 3 | 4 | Quicly is an IETF [QUIC](https://quicwg.github.io/) protocol implementation, written from the ground up to be used primarily within the H2O HTTP server. 5 | 6 | The software is licensed under the MIT License. 7 | 8 | How to build 9 | --- 10 | 11 | ``` 12 | % git submodule update --init --recursive 13 | % cmake . 14 | % make 15 | ``` 16 | 17 | Building the software requires OpenSSL 1.0.2 or above. 18 | If you have OpenSSL installed in a non-standard directory, you can pass the location using the `PKG_CONFIG_PATH` environment variable. 19 | 20 | ``` 21 | % PKG_CONFIG_PATH=/path/to/openssl/lib/pkgconfig cmake . 22 | ``` 23 | 24 | How to test 25 | --- 26 | 27 | Install dependencies first: 28 | 29 | ``` 30 | # If you use system perl, use --sudo 31 | % curl -sL https://cpanmin.us | perl - --sudo --self-upgrade 32 | % cpanm --installdeps --notest --sudo . 33 | 34 | # Otherwise, you'd better omit --sudo 35 | % curl -sL https://cpanmin.us | perl - --self-upgrade 36 | % cpanm --installdeps --notest . 37 | ``` 38 | 39 | Then, run the tests: 40 | ``` 41 | % make check 42 | ``` 43 | 44 | Running quicly 45 | --- 46 | 47 | A command-line program (named `cli`) that runs either as a server or a client `cli` is provided. 48 | 49 | To run the command as a client, specify the peer hostname and port number as the arguments. 50 | 51 | ``` 52 | % ./cli host port 53 | ``` 54 | 55 | To run the command as a server, specify the files that contain the certificate and private key, as well as the hostname and the port number to which the server should bind. 56 | 57 | ``` 58 | % ./cli -c server.crt -k server.key 0.0.0.0 4433 59 | ``` 60 | 61 | For more options, please refer to `./cli --help`. 62 | -------------------------------------------------------------------------------- /assets/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/assets/logo.jpg -------------------------------------------------------------------------------- /assets/main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/assets/main.jpg -------------------------------------------------------------------------------- /corpus/0a399de681be01030f153b46392d38d64d82d78a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/0a399de681be01030f153b46392d38d64d82d78a -------------------------------------------------------------------------------- /corpus/0dca570bf754fd714714c5138c4a11f107960c2d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/0dca570bf754fd714714c5138c4a11f107960c2d -------------------------------------------------------------------------------- /corpus/2171d39aba22a970087967e87a046abac2dce468: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/2171d39aba22a970087967e87a046abac2dce468 -------------------------------------------------------------------------------- /corpus/2b886c7f8a0d0531160b63a3ebf576f6a8dacd85: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/2b886c7f8a0d0531160b63a3ebf576f6a8dacd85 -------------------------------------------------------------------------------- /corpus/40dee7955c00d21cb26da5eb406cc7ee41a6c362: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/40dee7955c00d21cb26da5eb406cc7ee41a6c362 -------------------------------------------------------------------------------- /corpus/62418d0e56a8dcea60f7891d9a33b659c7b3e7bd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/62418d0e56a8dcea60f7891d9a33b659c7b3e7bd -------------------------------------------------------------------------------- /corpus/64b68bf5b882b9bd0b37267287980ecfa0e44a85: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/64b68bf5b882b9bd0b37267287980ecfa0e44a85 -------------------------------------------------------------------------------- /corpus/76fd19f9ef948864613e2cbf5cfea2d95f7f28e6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/76fd19f9ef948864613e2cbf5cfea2d95f7f28e6 -------------------------------------------------------------------------------- /corpus/80f90ef100ffec3ed550b90314ab33ecc0d8ce94: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/80f90ef100ffec3ed550b90314ab33ecc0d8ce94 -------------------------------------------------------------------------------- /corpus/8e259d8a0b21698bf5731a964c38797b3e40e2e5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/8e259d8a0b21698bf5731a964c38797b3e40e2e5 -------------------------------------------------------------------------------- /corpus/92361a23c86485ad68292506bc54ca077d111f87: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/92361a23c86485ad68292506bc54ca077d111f87 -------------------------------------------------------------------------------- /corpus/94c57455b53b04173d9c2b0289a1d9cd0067d767: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/94c57455b53b04173d9c2b0289a1d9cd0067d767 -------------------------------------------------------------------------------- /corpus/990336e07122731336a8fcb345b36f057e3016d4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/990336e07122731336a8fcb345b36f057e3016d4 -------------------------------------------------------------------------------- /corpus/9a52b25d098f6a4257ed48df74f60d59183d1f91: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/9a52b25d098f6a4257ed48df74f60d59183d1f91 -------------------------------------------------------------------------------- /corpus/9d8401c2a100a7d416ff83f9751c25337e566233: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/9d8401c2a100a7d416ff83f9751c25337e566233 -------------------------------------------------------------------------------- /corpus/a970114d1a081060268852221c1593608594f22d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/a970114d1a081060268852221c1593608594f22d -------------------------------------------------------------------------------- /corpus/b2639321ec13cc077e6a70d81ebd8813a84d30ec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/b2639321ec13cc077e6a70d81ebd8813a84d30ec -------------------------------------------------------------------------------- /corpus/ccb4d6c5b7f9eb65f6b2f051626e1e4eae2cc0ea: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/ccb4d6c5b7f9eb65f6b2f051626e1e4eae2cc0ea -------------------------------------------------------------------------------- /corpus/cd8b0f2a6fffa66c0e0dfe051c19643a609f55a5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/cd8b0f2a6fffa66c0e0dfe051c19643a609f55a5 -------------------------------------------------------------------------------- /corpus/db4b7b0acf91ef8ef42d9c11b524dcecb74bdf9d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/db4b7b0acf91ef8ef42d9c11b524dcecb74bdf9d -------------------------------------------------------------------------------- /corpus/e2d9da838028d2e97152858b474682420b59c10b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/e2d9da838028d2e97152858b474682420b59c10b -------------------------------------------------------------------------------- /corpus/e328aa61973fa43cfb2aa0cbea594e16baddaa30: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/e328aa61973fa43cfb2aa0cbea594e16baddaa30 -------------------------------------------------------------------------------- /corpus/f6afe9dda9439811b81d6f4feaaf18d706182ab6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/f6afe9dda9439811b81d6f4feaaf18d706182ab6 -------------------------------------------------------------------------------- /corpus/ff2d1eeea04672d78870670a0cd553b03be09512: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/h2o/quicly/8046973e0e599ce3d5b75750690de20ea6128972/corpus/ff2d1eeea04672d78870670a0cd553b03be09512 -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | #!perl 2 | # cpanm --installdeps --notest . 3 | 4 | requires 'JSON'; 5 | requires 'Test::TCP'; 6 | requires 'Scope::Guard'; 7 | -------------------------------------------------------------------------------- /fuzz/packet.cc: -------------------------------------------------------------------------------- 1 | #include "quicly.h" 2 | #include "quicly/defaults.h" 3 | #include "quicly/frame.h" 4 | 5 | void __sanitizer_cov_trace_pc(void) 6 | { 7 | } 8 | 9 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) 10 | { 11 | int ret; 12 | quicly_context_t ctx; 13 | quicly_decoded_packet_t p; 14 | 15 | ctx = quicly_spec_context; 16 | 17 | ret = quicly_decode_packet(&ctx, &p, Data, Size); 18 | 19 | if (ret != Size) 20 | return 0; 21 | const uint8_t *src = p.octets.base, *end = src + p.octets.len; 22 | if (p.octets.len == 0) 23 | return 0; 24 | 25 | uint8_t type_flags = *src++; 26 | if ((type_flags & ~QUICLY_FRAME_TYPE_STREAM_BITS) == QUICLY_FRAME_TYPE_STREAM_BASE) { 27 | quicly_stream_frame_t frame; 28 | if ((ret = quicly_decode_stream_frame(type_flags, &src, end, &frame)) != 0) 29 | return 0; 30 | } else { 31 | switch (type_flags) { 32 | case QUICLY_FRAME_TYPE_TRANSPORT_CLOSE: { 33 | quicly_transport_close_frame_t frame; 34 | if ((ret = quicly_decode_transport_close_frame(&src, end, &frame)) != 0) 35 | return 0; 36 | } break; 37 | case QUICLY_FRAME_TYPE_APPLICATION_CLOSE: { 38 | quicly_application_close_frame_t frame; 39 | if ((ret = quicly_decode_application_close_frame(&src, end, &frame)) != 0) 40 | return 0; 41 | } break; 42 | case QUICLY_FRAME_TYPE_ACK: 43 | case QUICLY_FRAME_TYPE_ACK_ECN: { 44 | quicly_ack_frame_t frame; 45 | if ((ret = quicly_decode_ack_frame(&src, end, &frame, type_flags == QUICLY_FRAME_TYPE_ACK_ECN)) != 0) 46 | return 0; 47 | } break; 48 | case QUICLY_FRAME_TYPE_CRYPTO: 49 | quicly_stream_frame_t frame; 50 | if ((ret = quicly_decode_crypto_frame(&src, end, &frame)) != 0) 51 | return 0; 52 | break; 53 | case QUICLY_FRAME_TYPE_RESET_STREAM: { 54 | quicly_reset_stream_frame_t frame; 55 | if ((ret = quicly_decode_reset_stream_frame(&src, end, &frame)) != 0) 56 | return 0; 57 | } break; 58 | case QUICLY_FRAME_TYPE_MAX_DATA: { 59 | quicly_max_data_frame_t frame; 60 | if ((ret = quicly_decode_max_data_frame(&src, end, &frame)) != 0) 61 | return 0; 62 | } break; 63 | case QUICLY_FRAME_TYPE_MAX_STREAM_DATA: { 64 | quicly_max_stream_data_frame_t frame; 65 | if ((ret = quicly_decode_max_stream_data_frame(&src, end, &frame)) != 0) 66 | return 0; 67 | } break; 68 | case QUICLY_FRAME_TYPE_MAX_STREAMS_BIDI: 69 | case QUICLY_FRAME_TYPE_MAX_STREAMS_UNI: { 70 | quicly_max_streams_frame_t frame; 71 | if ((ret = quicly_decode_max_streams_frame(&src, end, &frame)) != 0) 72 | return 0; 73 | } break; 74 | case QUICLY_FRAME_TYPE_PING: 75 | ret = 0; 76 | break; 77 | case QUICLY_FRAME_TYPE_DATA_BLOCKED: { 78 | quicly_data_blocked_frame_t frame; 79 | if ((ret = quicly_decode_data_blocked_frame(&src, end, &frame)) != 0) 80 | return 0; 81 | } break; 82 | case QUICLY_FRAME_TYPE_STREAM_DATA_BLOCKED: { 83 | quicly_stream_data_blocked_frame_t frame; 84 | if ((ret = quicly_decode_stream_data_blocked_frame(&src, end, &frame)) != 0) 85 | return 0; 86 | } break; 87 | case QUICLY_FRAME_TYPE_STREAMS_BLOCKED_BIDI: 88 | case QUICLY_FRAME_TYPE_STREAMS_BLOCKED_UNI: { 89 | quicly_streams_blocked_frame_t frame; 90 | if ((ret = quicly_decode_streams_blocked_frame(&src, end, &frame)) != 0) 91 | return 0; 92 | } break; 93 | case QUICLY_FRAME_TYPE_NEW_CONNECTION_ID: { 94 | quicly_new_connection_id_frame_t frame; 95 | if ((ret = quicly_decode_new_connection_id_frame(&src, end, &frame)) != 0) 96 | return 0; 97 | } break; 98 | case QUICLY_FRAME_TYPE_STOP_SENDING: { 99 | quicly_stop_sending_frame_t frame; 100 | if ((ret = quicly_decode_stop_sending_frame(&src, end, &frame)) != 0) 101 | return 0; 102 | } break; 103 | case QUICLY_FRAME_TYPE_PATH_CHALLENGE: { 104 | quicly_path_challenge_frame_t frame; 105 | if ((ret = quicly_decode_path_challenge_frame(&src, end, &frame)) != 0) 106 | return 0; 107 | } break; 108 | case QUICLY_FRAME_TYPE_NEW_TOKEN: { 109 | quicly_new_token_frame_t frame; 110 | if ((ret = quicly_decode_new_token_frame(&src, end, &frame)) != 0) 111 | return 0; 112 | } break; 113 | }; 114 | } 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /include/quicly/cid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Fastly, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_cid_h 23 | #define quicly_cid_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include 31 | #include "picotls.h" 32 | #include "quicly/constants.h" 33 | 34 | /** 35 | * Guard value. We would never send path_id of this value. 36 | */ 37 | #define QUICLY_MAX_PATH_ID UINT8_MAX 38 | 39 | typedef struct st_quicly_cid_t { 40 | uint8_t cid[QUICLY_MAX_CID_LEN_V1]; 41 | uint8_t len; 42 | } quicly_cid_t; 43 | 44 | /** 45 | * The structure of CID issued by quicly. 46 | * 47 | * Authentication of the CID can be done by validating if server_id and thread_id contain correct values. 48 | */ 49 | typedef struct st_quicly_cid_plaintext_t { 50 | /** 51 | * the internal "connection ID" unique to each connection (rather than QUIC's CID being unique to each path) 52 | */ 53 | uint32_t master_id; 54 | /** 55 | * path ID of the connection; we issue up to 255 CIDs per connection (see QUICLY_MAX_PATH_ID) 56 | */ 57 | uint32_t path_id : 8; 58 | /** 59 | * for intra-node routing 60 | */ 61 | uint32_t thread_id : 24; 62 | /** 63 | * for inter-node routing; available only when using a 16-byte cipher to encrypt CIDs, otherwise set to zero. 64 | */ 65 | uint64_t node_id; 66 | } quicly_cid_plaintext_t; 67 | 68 | /** 69 | * A CID used for indicating that a CID is invalid. Both .node_id and .thread_id are set to their maximum. Therefore, mappings must 70 | * refrain from using those values. 71 | */ 72 | extern quicly_cid_plaintext_t quicly_cid_plaintext_invalid; 73 | 74 | /** 75 | * CID encryption 76 | */ 77 | typedef struct st_quicly_cid_encryptor_t { 78 | /** 79 | * encrypts CID and optionally generates a stateless reset token 80 | */ 81 | void (*encrypt_cid)(struct st_quicly_cid_encryptor_t *self, quicly_cid_t *encrypted, void *stateless_reset_token, 82 | const quicly_cid_plaintext_t *plaintext); 83 | /** 84 | * decrypts a CID 85 | * @param plaintext if successful, the decoded CID will be written 86 | * @param encrypt the encrypted CID 87 | * @param len length of the encrypted CID when the packet is a long header packet, or 0 if it is a short header packet 88 | * @return length of the CID if successful, or SIZE_MAX if failed 89 | */ 90 | size_t (*decrypt_cid)(struct st_quicly_cid_encryptor_t *self, quicly_cid_plaintext_t *plaintext, const void *encrypted, 91 | size_t len); 92 | /** 93 | * generates a stateless reset token (returns if generated) 94 | */ 95 | int (*generate_stateless_reset_token)(struct st_quicly_cid_encryptor_t *self, void *token, const void *cid); 96 | } quicly_cid_encryptor_t; 97 | 98 | static void quicly_set_cid(quicly_cid_t *dest, ptls_iovec_t src); 99 | static int quicly_cid_is_equal(const quicly_cid_t *cid, ptls_iovec_t vec); 100 | 101 | /* inline functions */ 102 | 103 | inline int quicly_cid_is_equal(const quicly_cid_t *cid, ptls_iovec_t vec) 104 | { 105 | return cid->len == vec.len && memcmp(cid->cid, vec.base, vec.len) == 0; 106 | } 107 | 108 | inline void quicly_set_cid(quicly_cid_t *dest, ptls_iovec_t src) 109 | { 110 | memcpy(dest->cid, src.base, src.len); 111 | dest->len = src.len; 112 | } 113 | 114 | #ifdef __cplusplus 115 | } 116 | #endif 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /include/quicly/constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_constants_h 23 | #define quicly_constants_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include 31 | #include "picotls.h" 32 | 33 | #define QUICLY_DELAYED_ACK_TIMEOUT 25 /* milliseconds */ 34 | #define QUICLY_DEFAULT_MAX_ACK_DELAY 25 /* milliseconds */ 35 | #define QUICLY_LOCAL_MAX_ACK_DELAY 25 /* milliseconds */ 36 | #define QUICLY_DEFAULT_ACK_DELAY_EXPONENT 3 37 | #define QUICLY_LOCAL_ACK_DELAY_EXPONENT 10 38 | #define QUICLY_MIN_INITIAL_DCID_LEN 8 39 | #define QUICLY_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT 2 /* If this transport parameter is absent, a default of 2 is assumed. (18.2) */ 40 | /** 41 | * how many CIDs is quicly willing to manage at the same time? 42 | * this value is used in two ways: 43 | * - active_connection_id_limit transport parameter advertised to the remote peer 44 | * - maximum number of connection IDs we issue to the remote peer at a moment 45 | */ 46 | #define QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT 4 47 | #define QUICLY_MIN_ACTIVE_CONNECTION_ID_LIMIT 2 48 | #define QUICLY_DEFAULT_MAX_UDP_PAYLOAD_SIZE 65527 49 | #define QUICLY_MIN_CLIENT_INITIAL_SIZE 1200 50 | #define QUICLY_DEFAULT_MIN_PTO 1 /* milliseconds */ 51 | #define QUICLY_DEFAULT_INITIAL_RTT 66 /* initial retransmission timeout is *3, i.e. 200ms */ 52 | #define QUICLY_LOSS_DEFAULT_PACKET_THRESHOLD 3 53 | 54 | #define QUICLY_DEFAULT_PACKET_TOLERANCE 2 55 | #define QUICLY_MAX_PACKET_TOLERANCE 10 56 | #define QUICLY_FIRST_ACK_FREQUENCY_LOSS_EPISODE 4 57 | 58 | #define QUICLY_AEAD_TAG_SIZE 16 59 | 60 | #define QUICLY_MAX_CID_LEN_V1 20 61 | #define QUICLY_STATELESS_RESET_TOKEN_LEN 16 62 | 63 | #define QUICLY_EPOCH_INITIAL 0 64 | #define QUICLY_EPOCH_0RTT 1 65 | #define QUICLY_EPOCH_HANDSHAKE 2 66 | #define QUICLY_EPOCH_1RTT 3 67 | #define QUICLY_NUM_EPOCHS 4 68 | 69 | /** 70 | * Error code used by quicly. The code coalesces the following to an int64_t. 71 | * * 0..0x2ff: picotls error codes (of type int) 72 | * * 0x30000..0x400000000002ffff: QUIC application error codes 73 | * * 0x4000000000030000..0x800000000002ffff...: QUIC protocol error codes 74 | * Internal error codes should be allocated from the unused space below 0x30000 (i.e., unused space of picotls error codes); 75 | * quicly itself uses 0xffxx. `quicly_error_t` is defined as a signed type so that the picotls error code space can be mapped 76 | * without sign conversion. 77 | */ 78 | typedef int64_t quicly_error_t; 79 | 80 | #define QUICLY_ERROR_IS_QUIC(e) ((uint64_t)(quicly_error_t)(e) - 0x30000u < 0x8000000000000000u) 81 | #define QUICLY_ERROR_IS_QUIC_TRANSPORT(e) ((uint64_t)(quicly_error_t)(e) - 0x4000000000030000u < 0x4000000000000000u) 82 | #define QUICLY_ERROR_IS_QUIC_APPLICATION(e) ((uint64_t)(quicly_error_t)(e) - 0x30000u < 0x4000000000000000u) 83 | #define QUICLY_ERROR_GET_ERROR_CODE(e) (((uint64_t)(quicly_error_t)(e) - 0x30000u) & 0x3fffffffffffffffu) 84 | #define QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(e) ((quicly_error_t)((uint64_t)(e) + 0x4000000000030000u)) 85 | #define QUICLY_ERROR_FROM_APPLICATION_ERROR_CODE(e) ((quicly_error_t)(uint64_t)(e) + 0x30000) 86 | /** 87 | * PTLS_ERROR_NO_MEMORY and QUICLY_ERROR_STATE_EXHAUSTION are special error codes that are internal but can be passed to 88 | * quicly_close. These are converted to QUICLY_TRANSPORT_ERROR_INTERNAL when sent over the wire. 89 | */ 90 | #define QUICLY_ERROR_IS_CONCEALED(err) ((err) == PTLS_ERROR_NO_MEMORY || (err) == QUICLY_ERROR_STATE_EXHAUSTION) 91 | 92 | /* transport error codes */ 93 | #define QUICLY_TRANSPORT_ERROR_NONE QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x0) 94 | #define QUICLY_TRANSPORT_ERROR_INTERNAL QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x1) 95 | #define QUICLY_TRANSPORT_ERROR_CONNECTION_REFUSED QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x2) 96 | #define QUICLY_TRANSPORT_ERROR_FLOW_CONTROL QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x3) 97 | #define QUICLY_TRANSPORT_ERROR_STREAM_LIMIT QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x4) 98 | #define QUICLY_TRANSPORT_ERROR_STREAM_STATE QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x5) 99 | #define QUICLY_TRANSPORT_ERROR_FINAL_SIZE QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x6) 100 | #define QUICLY_TRANSPORT_ERROR_FRAME_ENCODING QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x7) 101 | #define QUICLY_TRANSPORT_ERROR_TRANSPORT_PARAMETER QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x8) 102 | #define QUICLY_TRANSPORT_ERROR_CONNECTION_ID_LIMIT QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x9) 103 | #define QUICLY_TRANSPORT_ERROR_PROTOCOL_VIOLATION QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0xa) 104 | #define QUICLY_TRANSPORT_ERROR_INVALID_TOKEN QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0xb) 105 | #define QUICLY_TRANSPORT_ERROR_APPLICATION QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0xc) 106 | #define QUICLY_TRANSPORT_ERROR_CRYPTO_BUFFER_EXCEEDED QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0xd) 107 | #define QUICLY_TRANSPORT_ERROR_KEY_UPDATE QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0xe) 108 | #define QUICLY_TRANSPORT_ERROR_AEAD_LIMIT_REACHED QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0xf) 109 | #define QUICLY_TRANSPORT_ERROR_CRYPTO(tls_alert) QUICLY_ERROR_FROM_TRANSPORT_ERROR_CODE(0x100 + (tls_alert)) 110 | 111 | /* internal error codes, used purely for signaling status to the application */ 112 | #define QUICLY_ERROR_PACKET_IGNORED 0xff01 113 | #define QUICLY_ERROR_SENDBUF_FULL 0xff02 /* internal use only; the error code is never exposed to the application */ 114 | #define QUICLY_ERROR_FREE_CONNECTION 0xff03 /* returned by quicly_send when the connection is freeable */ 115 | #define QUICLY_ERROR_RECEIVED_STATELESS_RESET 0xff04 116 | #define QUICLY_ERROR_NO_COMPATIBLE_VERSION 0xff05 117 | #define QUICLY_ERROR_IS_CLOSING 0xff06 /* indicates that the connection has already entered closing state */ 118 | #define QUICLY_ERROR_STATE_EXHAUSTION 0xff07 119 | #define QUICLY_ERROR_INVALID_INITIAL_VERSION 0xff08 120 | #define QUICLY_ERROR_DECRYPTION_FAILED 0xff09 121 | 122 | typedef int64_t quicly_stream_id_t; 123 | 124 | typedef struct st_quicly_conn_t quicly_conn_t; 125 | 126 | /** 127 | * Used for emitting arbitrary debug message through probes. The debug message might get emitted unescaped as a JSON string, 128 | * therefore cannot contain characters that are required to be escaped as a JSON string (e.g., `\n`, `"`). 129 | */ 130 | void quicly__debug_printf(quicly_conn_t *conn, const char *function, int line, const char *fmt, ...) 131 | __attribute__((format(printf, 4, 5))); 132 | 133 | #define quicly_debug_printf(conn, ...) quicly__debug_printf((conn), __FUNCTION__, __LINE__, __VA_ARGS__) 134 | 135 | #ifdef __cplusplus 136 | } 137 | #endif 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /include/quicly/defaults.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2019 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_defaults_h 23 | #define quicly_defaults_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include "quicly.h" 30 | 31 | extern const quicly_context_t quicly_spec_context; 32 | extern const quicly_context_t quicly_performant_context; 33 | 34 | /** 35 | * Instantiates a CID cipher. 36 | * The CID cipher MUST be a block cipher. It MAY be a 64-bit block cipher (e.g., blowfish) when `quicly_cid_plaintext_t::node_id` is 37 | * not utilized by the application. Otherwise, it MUST be a 128-bit block cipher (e.g., AES). 38 | * The reset token cipher MUST be a 128-bit block cipher. 39 | */ 40 | quicly_cid_encryptor_t *quicly_new_default_cid_encryptor(ptls_cipher_algorithm_t *cid_cipher, 41 | ptls_cipher_algorithm_t *reset_token_cipher, ptls_hash_algorithm_t *hash, 42 | ptls_iovec_t key); 43 | /** 44 | * 45 | */ 46 | void quicly_free_default_cid_encryptor(quicly_cid_encryptor_t *self); 47 | /** 48 | * 49 | */ 50 | extern quicly_stream_scheduler_t quicly_default_stream_scheduler; 51 | /** 52 | * 53 | */ 54 | extern quicly_now_t quicly_default_now; 55 | /** 56 | * 57 | */ 58 | extern quicly_crypto_engine_t quicly_default_crypto_engine; 59 | 60 | #define quicly_default_cc quicly_cc_type_reno 61 | #define quicly_default_init_cc quicly_cc_reno_init 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /include/quicly/linklist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_linklist_h 23 | #define quicly_linklist_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | 31 | typedef struct st_quicly_linklist_t { 32 | struct st_quicly_linklist_t *prev; 33 | struct st_quicly_linklist_t *next; 34 | } quicly_linklist_t; 35 | 36 | static void quicly_linklist_init(quicly_linklist_t *l); 37 | static int quicly_linklist_is_linked(quicly_linklist_t *l); 38 | static void quicly_linklist_insert(quicly_linklist_t *prev, quicly_linklist_t *n); 39 | static void quicly_linklist_unlink(quicly_linklist_t *l); 40 | static void quicly_linklist_insert_list(quicly_linklist_t *prev, quicly_linklist_t *l); 41 | 42 | /* inline functions */ 43 | 44 | inline void quicly_linklist_init(quicly_linklist_t *l) 45 | { 46 | l->prev = l->next = l; 47 | } 48 | 49 | inline int quicly_linklist_is_linked(quicly_linklist_t *l) 50 | { 51 | return l->prev != l; 52 | } 53 | 54 | inline void quicly_linklist_insert(quicly_linklist_t *prev, quicly_linklist_t *n) 55 | { 56 | assert(!quicly_linklist_is_linked(n)); 57 | n->prev = prev; 58 | n->next = prev->next; 59 | n->prev->next = n; 60 | n->next->prev = n; 61 | } 62 | 63 | inline void quicly_linklist_unlink(quicly_linklist_t *l) 64 | { 65 | l->prev->next = l->next; 66 | l->next->prev = l->prev; 67 | quicly_linklist_init(l); 68 | } 69 | 70 | inline void quicly_linklist_insert_list(quicly_linklist_t *prev, quicly_linklist_t *l) 71 | { 72 | if (quicly_linklist_is_linked(l)) { 73 | l->next->prev = prev; 74 | l->prev->next = prev->next; 75 | prev->next->prev = l->prev; 76 | prev->next = l->next; 77 | quicly_linklist_init(l); 78 | } 79 | } 80 | 81 | #ifdef __cplusplus 82 | } 83 | #endif 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /include/quicly/local_cid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Fastly, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_local_cid_h 23 | #define quicly_local_cid_h 24 | 25 | #include "quicly/cid.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | enum en_quicly_local_cid_state_t { 32 | /** 33 | * this entry is free for use 34 | */ 35 | QUICLY_LOCAL_CID_STATE_IDLE, 36 | /** 37 | * this entry is to be sent at the next round of send operation 38 | */ 39 | QUICLY_LOCAL_CID_STATE_PENDING, 40 | /** 41 | * this entry has been sent and is waiting for ACK (or to be deemed lost) 42 | */ 43 | QUICLY_LOCAL_CID_STATE_INFLIGHT, 44 | /** 45 | * this CID has been delivered to the remote peer (ACKed) and in use 46 | */ 47 | QUICLY_LOCAL_CID_STATE_DELIVERED, 48 | }; 49 | 50 | /** 51 | * records information for sending NEW_CONNECTION_ID frame 52 | */ 53 | typedef struct st_quicly_local_cid_t { 54 | enum en_quicly_local_cid_state_t state; 55 | uint64_t sequence; 56 | quicly_cid_t cid; 57 | uint8_t stateless_reset_token[QUICLY_STATELESS_RESET_TOKEN_LEN]; 58 | } quicly_local_cid_t; 59 | 60 | /** 61 | * manages a list of connection IDs we issue to the remote peer 62 | */ 63 | typedef struct st_quicly_local_cid_set_t { 64 | /** 65 | * Identifier of the connection used by quicly. Three tuple of (node_id, thread_id, master_id) is used to identify the 66 | * connection. `path_id` is maintained by the "local_cid" module, and used for identifying each CID being issued. 67 | */ 68 | quicly_cid_plaintext_t plaintext; 69 | /** 70 | * storage to retain local CIDs 71 | * 72 | * Pending CIDs (state == STATE_PENDING) are moved to the front of the array, in the order it was marked as pending. 73 | * This ensures that pending CIDs are sent in FIFO manner. Order of CIDs with other states is not defined. 74 | * 75 | * Actual size of the array is constrained by _size. 76 | */ 77 | quicly_local_cid_t cids[QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT]; 78 | /** 79 | * how many entries are actually usable in `cids`? 80 | */ 81 | size_t _size; 82 | quicly_cid_encryptor_t *_encryptor; 83 | } quicly_local_cid_set_t; 84 | 85 | /** 86 | * initialize the structure 87 | * 88 | * If `encryptor` is non-NULL, it is initialized with size==1 (sequence==0 is registered as DELIVERED). 89 | * Otherwise, it is initialized with size==0, and the size shall never be increased. 90 | */ 91 | void quicly_local_cid_init_set(quicly_local_cid_set_t *set, quicly_cid_encryptor_t *encryptor, 92 | const quicly_cid_plaintext_t *new_cid); 93 | /** 94 | * sets a new size of locally issued CIDs. 95 | * 96 | * The new size must be equal to or grater than the current size, and must be equal to or less than the elements of `cids`. 97 | * 98 | * Returns true if there is something to send. 99 | */ 100 | int quicly_local_cid_set_size(quicly_local_cid_set_t *set, size_t new_cap); 101 | /** 102 | * returns true if all entries in the given set is in IDLE state 103 | */ 104 | static size_t quicly_local_cid_get_size(const quicly_local_cid_set_t *set); 105 | /** 106 | * tells the module that the first `num_sent` pending CIDs have been sent 107 | */ 108 | void quicly_local_cid_on_sent(quicly_local_cid_set_t *set, size_t num_sent); 109 | /** 110 | * tells the module that the given sequence number was ACKed 111 | */ 112 | void quicly_local_cid_on_acked(quicly_local_cid_set_t *set, uint64_t sequence); 113 | /** 114 | * tells the module that the given sequence number was lost 115 | * 116 | * returns true if there is something to send 117 | */ 118 | int quicly_local_cid_on_lost(quicly_local_cid_set_t *set, uint64_t sequence); 119 | /** 120 | * remove the specified CID from the storage. 121 | * 122 | * This makes one slot for CIDs empty. The CID generator callback is then called to fill the slot with a new CID. 123 | * @return 0 if the request was legal, otherwise an error code 124 | */ 125 | quicly_error_t quicly_local_cid_retire(quicly_local_cid_set_t *set, uint64_t sequence, int *has_pending); 126 | 127 | /* inline definitions */ 128 | 129 | inline size_t quicly_local_cid_get_size(const quicly_local_cid_set_t *set) 130 | { 131 | return set->_size; 132 | } 133 | 134 | #ifdef __cplusplus 135 | } 136 | #endif 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /include/quicly/maxsender.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_maxsender_h 23 | #define quicly_maxsender_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | #include "quicly/constants.h" 33 | 34 | typedef struct st_quicly_maxsender_t { 35 | /** 36 | * maximum value being announced (never decreases) 37 | */ 38 | int64_t max_committed; 39 | /** 40 | * maximum value being acked by remote peer 41 | */ 42 | int64_t max_acked; 43 | /** 44 | * number of maximums inflight 45 | */ 46 | size_t num_inflight; 47 | /** 48 | * 49 | */ 50 | unsigned force_send : 1; 51 | } quicly_maxsender_t; 52 | 53 | typedef struct st_quicly_maxsender_sent_t { 54 | uint64_t inflight : 1; 55 | uint64_t value : 63; 56 | } quicly_maxsender_sent_t; 57 | 58 | static void quicly_maxsender_init(quicly_maxsender_t *m, int64_t initial_value); 59 | static void quicly_maxsender_dispose(quicly_maxsender_t *m); 60 | static void quicly_maxsender_request_transmit(quicly_maxsender_t *m); 61 | static int quicly_maxsender_should_send_max(quicly_maxsender_t *m, int64_t buffered_from, uint32_t window_size, 62 | uint32_t update_ratio); 63 | static int quicly_maxsender_should_send_blocked(quicly_maxsender_t *m, int64_t local_max); 64 | static void quicly_maxsender_record(quicly_maxsender_t *m, int64_t value, quicly_maxsender_sent_t *sent); 65 | static void quicly_maxsender_acked(quicly_maxsender_t *m, quicly_maxsender_sent_t *sent); 66 | static void quicly_maxsender_lost(quicly_maxsender_t *m, quicly_maxsender_sent_t *sent); 67 | 68 | /* inline definitions */ 69 | 70 | inline void quicly_maxsender_init(quicly_maxsender_t *m, int64_t initial_value) 71 | { 72 | m->max_committed = initial_value; 73 | m->max_acked = initial_value; 74 | m->num_inflight = 0; 75 | m->force_send = 0; 76 | } 77 | 78 | inline void quicly_maxsender_dispose(quicly_maxsender_t *m) 79 | { 80 | } 81 | 82 | inline void quicly_maxsender_request_transmit(quicly_maxsender_t *m) 83 | { 84 | m->force_send = 1; 85 | } 86 | 87 | inline int quicly_maxsender_should_send_max(quicly_maxsender_t *m, int64_t buffered_from, uint32_t window_size, 88 | uint32_t update_ratio) 89 | { 90 | if (m->force_send) 91 | return 1; 92 | 93 | /* ratio is permil (1/1024) */ 94 | int64_t threshold = buffered_from + ((int64_t)window_size * update_ratio) / 1024; 95 | return (m->num_inflight != 0 ? m->max_committed : m->max_acked) <= threshold; 96 | } 97 | 98 | inline int quicly_maxsender_should_send_blocked(quicly_maxsender_t *m, int64_t local_max) 99 | { 100 | return m->max_committed < local_max; 101 | } 102 | 103 | inline void quicly_maxsender_record(quicly_maxsender_t *m, int64_t value, quicly_maxsender_sent_t *sent) 104 | { 105 | assert(value >= m->max_committed); 106 | m->max_committed = value; 107 | ++m->num_inflight; 108 | m->force_send = 0; 109 | sent->inflight = 1; 110 | sent->value = value; 111 | } 112 | 113 | inline void quicly_maxsender_acked(quicly_maxsender_t *m, quicly_maxsender_sent_t *sent) 114 | { 115 | if (m->max_acked < (int64_t)sent->value) 116 | m->max_acked = sent->value; 117 | /* num_inflight should not be adjusted in case of a late ACK */ 118 | if (sent->inflight) { 119 | assert(m->num_inflight != 0); 120 | --m->num_inflight; 121 | sent->inflight = 0; 122 | } 123 | } 124 | 125 | inline void quicly_maxsender_lost(quicly_maxsender_t *m, quicly_maxsender_sent_t *sent) 126 | { 127 | /* the function must be called at most once (when LOST event occurs, but not EXPIRED), hence assert and always decrement */ 128 | assert(m->num_inflight != 0); 129 | --m->num_inflight; 130 | sent->inflight = 0; 131 | } 132 | 133 | #ifdef __cplusplus 134 | } 135 | #endif 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /include/quicly/pacer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_pacer_h 23 | #define quicly_pacer_h 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | /** 34 | * Simple pacer. The design guarantees that the formula below is met for any given pacer-restricted period: 35 | * 36 | * flow_rate * duration + 8 * mtu <= bytes_sent < flow_rate * duration + 10 * mtu 37 | */ 38 | typedef struct st_quicly_pacer_t { 39 | /** 40 | * clock 41 | */ 42 | int64_t at; 43 | /** 44 | * amount of credit being spent at `at` 45 | */ 46 | size_t bytes_sent; 47 | } quicly_pacer_t; 48 | 49 | #define QUICLY_PACER_BURST_LOW 8 /* lower bound in packets */ 50 | #define QUICLY_PACER_BURST_HIGH 10 /* high bound in packets */ 51 | 52 | /** 53 | * resets the pacer 54 | */ 55 | static void quicly_pacer_reset(quicly_pacer_t *pacer); 56 | /** 57 | * returns when the next chunk of data can be sent 58 | */ 59 | static int64_t quicly_pacer_can_send_at(quicly_pacer_t *pacer, uint32_t bytes_per_msec, uint16_t mtu); 60 | /** 61 | * returns the number of bytes that can be sent at this moment 62 | */ 63 | static uint64_t quicly_pacer_get_window(quicly_pacer_t *pacer, int64_t now, uint32_t bytes_per_msec, uint16_t mtu); 64 | /** 65 | * updates the window size available at current time 66 | */ 67 | static void quicly_pacer_consume_window(quicly_pacer_t *pacer, size_t delta); 68 | /** 69 | * Calculates the flow rate as `bytes_per_msec`. The returned value is no less than 1. 70 | */ 71 | static uint32_t quicly_pacer_calc_send_rate(uint32_t multiplier, uint32_t cwnd, uint32_t rtt); 72 | 73 | /* inline definitions */ 74 | 75 | inline void quicly_pacer_reset(quicly_pacer_t *pacer) 76 | { 77 | pacer->at = INT64_MIN; 78 | pacer->bytes_sent = 0; 79 | } 80 | 81 | inline int64_t quicly_pacer_can_send_at(quicly_pacer_t *pacer, uint32_t bytes_per_msec, uint16_t mtu) 82 | { 83 | /* return "now" if we have room in current msec */ 84 | size_t burst_size = QUICLY_PACER_BURST_LOW * mtu + 1; 85 | size_t burst_credit = burst_size > bytes_per_msec ? burst_size - bytes_per_msec : 0; 86 | if (pacer->bytes_sent < bytes_per_msec + burst_credit) 87 | return 0; 88 | 89 | /* calculate delay; the value is rounded down, as it is better for a pacer to be a bit aggressive than not */ 90 | int64_t delay = (pacer->bytes_sent - burst_credit) / bytes_per_msec; 91 | assert(delay > 0); 92 | return pacer->at + delay; 93 | } 94 | 95 | inline uint64_t quicly_pacer_get_window(quicly_pacer_t *pacer, int64_t now, uint32_t bytes_per_msec, uint16_t mtu) 96 | { 97 | assert(pacer->at <= now); 98 | 99 | /* Determine when it is possible to sent one packet. Return if that is a moment in future. */ 100 | int64_t can_send_at = quicly_pacer_can_send_at(pacer, bytes_per_msec, mtu); 101 | if (now < can_send_at) 102 | return 0; 103 | 104 | /* Calculate the upper bound of burst window (the size is later rounded up) */ 105 | size_t burst_window = (QUICLY_PACER_BURST_HIGH - 1) * mtu + 1; 106 | if (burst_window < bytes_per_msec) 107 | burst_window = bytes_per_msec; 108 | 109 | /* Additional amount of data that we can send in `now - restricted_at` milliseconds is that difference multiplied by 110 | * `bytes_per_msec`. Adjust `bytes_sent` by that amount before setting `restricted_at` to `now`. `uint64_t` is used to store 111 | * window and delta so that the multiplication would not overflow assuming that the quiescence period is shorter than 2**32 112 | * milliseconds. */ 113 | uint64_t window, delta = (now - pacer->at) * bytes_per_msec; 114 | if (pacer->bytes_sent > delta) { 115 | pacer->bytes_sent -= delta; 116 | if (burst_window > pacer->bytes_sent) { 117 | window = (burst_window - pacer->bytes_sent + mtu - 1) / mtu; 118 | if (window < 2) 119 | window = 2; 120 | } else { 121 | window = 2; 122 | } 123 | } else { 124 | pacer->bytes_sent = 0; 125 | window = (burst_window + mtu - 1) / mtu; 126 | } 127 | window *= mtu; 128 | 129 | pacer->at = now; 130 | 131 | return window; 132 | } 133 | 134 | inline void quicly_pacer_consume_window(quicly_pacer_t *pacer, size_t delta) 135 | { 136 | pacer->bytes_sent += delta; 137 | } 138 | 139 | inline uint32_t quicly_pacer_calc_send_rate(uint32_t multiplier, uint32_t cwnd, uint32_t rtt) 140 | { 141 | return (cwnd * multiplier + rtt - 1) / rtt; 142 | } 143 | 144 | #ifdef __cplusplus 145 | } 146 | #endif 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /include/quicly/ranges.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_ranges_h 23 | #define quicly_ranges_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | typedef struct st_quicly_range_t { 34 | uint64_t start; 35 | uint64_t end; /* non-inclusive */ 36 | } quicly_range_t; 37 | 38 | typedef struct st_quicly_ranges_t { 39 | quicly_range_t *ranges; 40 | size_t num_ranges, capacity; 41 | quicly_range_t _initial; 42 | } quicly_ranges_t; 43 | 44 | /** 45 | * initializes the structure 46 | */ 47 | static void quicly_ranges_init(quicly_ranges_t *ranges); 48 | /** 49 | * initializes the structure, registering given range 50 | */ 51 | int quicly_ranges_init_with_range(quicly_ranges_t *ranges, uint64_t start, uint64_t end); 52 | /** 53 | * clears the structure 54 | */ 55 | static void quicly_ranges_clear(quicly_ranges_t *ranges); 56 | /** 57 | * adds given range, returns 0 if successful 58 | */ 59 | int quicly_ranges_add(quicly_ranges_t *ranges, uint64_t start, uint64_t end); 60 | /** 61 | * subtracts given range, returns 0 if successful 62 | */ 63 | int quicly_ranges_subtract(quicly_ranges_t *ranges, uint64_t start, uint64_t end); 64 | /** 65 | * removes ranges->ranges[I] where begin_index <= I && I < end_index 66 | */ 67 | void quicly_ranges_drop_by_range_indices(quicly_ranges_t *ranges, size_t begin_index, size_t end_index); 68 | 69 | /* inline functions */ 70 | 71 | inline void quicly_ranges_init(quicly_ranges_t *ranges) 72 | { 73 | ranges->ranges = &ranges->_initial; 74 | ranges->num_ranges = 0; 75 | ranges->capacity = 1; 76 | } 77 | 78 | inline void quicly_ranges_clear(quicly_ranges_t *ranges) 79 | { 80 | if (ranges->ranges != &ranges->_initial) { 81 | free(ranges->ranges); 82 | ranges->ranges = &ranges->_initial; 83 | } 84 | ranges->num_ranges = 0; 85 | ranges->capacity = 1; 86 | } 87 | 88 | #ifdef __cplusplus 89 | } 90 | #endif 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /include/quicly/rate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef quicly_rate_h 24 | #define quicly_rate_h 25 | 26 | #include 27 | #include "quicly/ranges.h" 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | #ifndef QUICLY_DELIVERY_RATE_SAMPLE_PERIOD 34 | /** 35 | * sampling period of delivery rate, in milliseconds 36 | */ 37 | #define QUICLY_DELIVERY_RATE_SAMPLE_PERIOD 50 38 | #endif 39 | 40 | #ifndef QUICLY_DELIVERY_RATE_SAMPLE_COUNT 41 | /** 42 | * number of samples to retain (and to calculate average from) 43 | */ 44 | #define QUICLY_DELIVERY_RATE_SAMPLE_COUNT 10 45 | #endif 46 | 47 | struct st_quicly_rate_sample_t { 48 | uint32_t elapsed; 49 | uint32_t bytes_acked; 50 | }; 51 | 52 | /** 53 | * State used for estimating the delivery rate. 54 | */ 55 | typedef struct st_quicly_ratemeter_t { 56 | /** 57 | * ring buffer retaining the most recent samples 58 | */ 59 | struct { 60 | struct st_quicly_rate_sample_t entries[QUICLY_DELIVERY_RATE_SAMPLE_COUNT]; 61 | size_t latest; 62 | } past_samples; 63 | /** 64 | * packet number range within which the flow has been CWND-limited 65 | */ 66 | quicly_range_t pn_cwnd_limited; 67 | /** 68 | * Current sample being collected, if any. When running, `start.at` and `start.bytes_acked` retains the values at the start of 69 | * the current sampling period. When not, `start.at` is set to INT64_MAX, and `sample` is zero-cleared. 70 | */ 71 | struct { 72 | struct { 73 | int64_t at; 74 | uint64_t bytes_acked; 75 | } start; 76 | struct st_quicly_rate_sample_t sample; 77 | } current; 78 | } quicly_ratemeter_t; 79 | 80 | /** 81 | * Estimated delivery rate, in bytes / second. 82 | */ 83 | typedef struct st_quicly_rate_t { 84 | uint64_t latest; 85 | uint64_t smoothed; 86 | uint64_t stdev; 87 | } quicly_rate_t; 88 | 89 | /** 90 | * 91 | */ 92 | void quicly_ratemeter_init(quicly_ratemeter_t *meter); 93 | /** 94 | * returns if currently marked as CC-limited 95 | */ 96 | static int quicly_ratemeter_is_cc_limited(quicly_ratemeter_t *meter); 97 | /** 98 | * Notifies the estimator that the flow is becoming CC-limited at the point of sending packets *starting* from packet number `pn`. 99 | */ 100 | void quicly_ratemeter_enter_cc_limited(quicly_ratemeter_t *meter, uint64_t pn); 101 | /** 102 | * Notifies that the estimator that the flow is exiting from CC-limited when the packet number of the next packet will be `pn`. 103 | */ 104 | void quicly_ratemeter_exit_cc_limited(quicly_ratemeter_t *meter, uint64_t pn); 105 | /** 106 | * Given three values, update the estimation. 107 | * @param bytes_acked total number of bytes being acked from the beginning of the connection; i.e., 108 | * `quicly_stats_t::num_bytes.ack_received` 109 | */ 110 | void quicly_ratemeter_on_ack(quicly_ratemeter_t *meter, int64_t now, uint64_t bytes_acked, uint64_t pn); 111 | /** 112 | * Returns the delivery rate estimate 113 | */ 114 | void quicly_ratemeter_report(quicly_ratemeter_t *meter, quicly_rate_t *rate); 115 | 116 | /* inline definitions */ 117 | 118 | inline int quicly_ratemeter_is_cc_limited(quicly_ratemeter_t *meter) 119 | { 120 | return meter->pn_cwnd_limited.start != UINT64_MAX && meter->pn_cwnd_limited.end == UINT64_MAX; 121 | } 122 | 123 | #ifdef __cplusplus 124 | } 125 | #endif 126 | 127 | #endif 128 | -------------------------------------------------------------------------------- /include/quicly/recvstate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_recvstate_h 23 | #define quicly_recvstate_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include 31 | #include "picotls.h" 32 | #include "quicly/ranges.h" 33 | 34 | typedef struct st_quicly_recvstate_t { 35 | /** 36 | * ranges that have been received (starts and remains non-empty until transfer completes) 37 | */ 38 | quicly_ranges_t received; 39 | /** 40 | * starting offset of data 41 | */ 42 | uint64_t data_off; 43 | /** 44 | * end_of_stream offset (or UINT64_MAX) 45 | */ 46 | uint64_t eos; 47 | } quicly_recvstate_t; 48 | 49 | void quicly_recvstate_init(quicly_recvstate_t *state); 50 | void quicly_recvstate_init_closed(quicly_recvstate_t *state); 51 | void quicly_recvstate_dispose(quicly_recvstate_t *state); 52 | static int quicly_recvstate_transfer_complete(quicly_recvstate_t *state); 53 | static size_t quicly_recvstate_bytes_available(quicly_recvstate_t *state); 54 | /** 55 | * Records that the range identified by (off, *len) has been received. When 0 (success) is returned, *len contains the number of 56 | * bytes that might have been newly received and therefore need to be written to the receive buffer (this number of bytes counts 57 | * backward from the end of given range). 58 | */ 59 | quicly_error_t quicly_recvstate_update(quicly_recvstate_t *state, uint64_t off, size_t *len, int is_fin, size_t max_ranges); 60 | quicly_error_t quicly_recvstate_reset(quicly_recvstate_t *state, uint64_t eos_at, uint64_t *bytes_missing); 61 | 62 | /* inline definitions */ 63 | 64 | inline int quicly_recvstate_transfer_complete(quicly_recvstate_t *state) 65 | { 66 | return state->received.num_ranges == 0; 67 | } 68 | 69 | inline size_t quicly_recvstate_bytes_available(quicly_recvstate_t *state) 70 | { 71 | uint64_t total = quicly_recvstate_transfer_complete(state) ? state->eos : state->received.ranges[0].end; 72 | assert(state->data_off <= total); 73 | return total - state->data_off; 74 | } 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /include/quicly/remote_cid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Fastly, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_received_cid_h 23 | #define quicly_received_cid_h 24 | 25 | #include "quicly/cid.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * state of `quicly_remote_cid_t` 33 | */ 34 | typedef enum en_quicly_remote_cid_state_t { 35 | /** 36 | * cid and stateless reset token have not been received for the sequence number 37 | */ 38 | QUICLY_REMOTE_CID_UNAVAILABLE, 39 | /** 40 | * cid is in use 41 | */ 42 | QUICLY_REMOTE_CID_IN_USE, 43 | /** 44 | * cid has been receive but has not been used yet 45 | */ 46 | QUICLY_REMOTE_CID_AVAILABLE 47 | } quicly_remote_cid_state_t; 48 | 49 | /** 50 | * records a CID given by the remote peer 51 | */ 52 | typedef struct st_quicly_remote_cid_t { 53 | /** 54 | * state 55 | */ 56 | quicly_remote_cid_state_t state; 57 | /** 58 | * sequence number of the CID; if `state` is UNAVAILABLE, this is a reserved slot meaning that we are expecting to receive a 59 | * NEW_CONNECTION_ID frame with this sequence number. This helps determine if a received frame is carrying a CID that is already 60 | * retired. 61 | */ 62 | uint64_t sequence; 63 | /** 64 | * CID; available unless `state` is UNAVAILABLE 65 | */ 66 | struct st_quicly_cid_t cid; 67 | /** 68 | * stateless reset token; only usable if `is_active` is true 69 | */ 70 | uint8_t stateless_reset_token[QUICLY_STATELESS_RESET_TOKEN_LEN]; 71 | } quicly_remote_cid_t; 72 | 73 | /** 74 | * structure to hold active connection IDs received from the remote peer 75 | */ 76 | typedef struct st_quicly_remote_cid_set_t { 77 | /** 78 | * We retain QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT active connection IDs. `cids[0]` used to retain the current DCID, but it is 79 | * no longer the case. DCID of the non-probing path should now be obtained via `get_dcid(conn->paths[0])` where `paths[0]` is 80 | * the non-probing path. 81 | */ 82 | quicly_remote_cid_t cids[QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT]; 83 | /** 84 | * we expect to receive CIDs with sequence number smaller than or equal to this number 85 | */ 86 | uint64_t _largest_sequence_expected; 87 | } quicly_remote_cid_set_t; 88 | 89 | /** 90 | * Initializes the set. If `initial_cid` is NULL, the first value is automatically generated so that the endpoint running as client 91 | * can use it. Stateless reset token of the initial CID is set to a random value so that it would not match against any value being 92 | * received. 93 | */ 94 | void quicly_remote_cid_init_set(quicly_remote_cid_set_t *set, ptls_iovec_t *initial_cid, void (*random_bytes)(void *, size_t)); 95 | /** 96 | * registers received connection ID 97 | * returns 0 if successful (registered or ignored because of duplication/stale information), transport error code otherwise 98 | */ 99 | quicly_error_t quicly_remote_cid_register(quicly_remote_cid_set_t *set, uint64_t sequence, const uint8_t *cid, size_t cid_len, 100 | const uint8_t srt[QUICLY_STATELESS_RESET_TOKEN_LEN], uint64_t retire_prior_to, 101 | uint64_t unregistered_seqs[QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT], 102 | size_t *num_unregistered_seqs); 103 | /** 104 | * unregisters specified CID from the store 105 | */ 106 | void quicly_remote_cid_unregister(quicly_remote_cid_set_t *set, uint64_t sequence); 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /include/quicly/retire_cid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Fastly, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_retire_cid_h 23 | #define quicly_retire_cid_h 24 | 25 | #include "quicly/cid.h" 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * up to how many RETIRE_CONNECTION_IDs to keep for retransmission 33 | */ 34 | #define QUICLY_RETIRE_CONNECTION_ID_LIMIT (QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT * 2) 35 | 36 | typedef struct st_quicly_retire_cid_set_t quicly_retire_cid_set_t; 37 | 38 | struct st_quicly_retire_cid_set_t { 39 | /** 40 | * sequence numbers to ask for retirement 41 | * Valid entries are packed in the front of the array with FIFO manner. 42 | */ 43 | uint64_t sequences[QUICLY_RETIRE_CONNECTION_ID_LIMIT]; 44 | /** 45 | * number of pending sequence numbers 46 | */ 47 | size_t _num_pending; 48 | }; 49 | 50 | void quicly_retire_cid_init(quicly_retire_cid_set_t *set); 51 | void quicly_retire_cid_push(quicly_retire_cid_set_t *set, uint64_t sequence); 52 | void quicly_retire_cid_shift(quicly_retire_cid_set_t *set, size_t num_shift); 53 | static size_t quicly_retire_cid_get_num_pending(const quicly_retire_cid_set_t *set); 54 | 55 | inline size_t quicly_retire_cid_get_num_pending(const quicly_retire_cid_set_t *set) 56 | { 57 | return set->_num_pending; 58 | } 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /include/quicly/sendstate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_sendstate_h 23 | #define quicly_sendstate_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include "quicly/ranges.h" 30 | 31 | typedef struct st_quicly_sendstate_t { 32 | /** 33 | * ranges that have been acked (guaranteed to be non-empty; i.e., acked.ranges[0].end == contiguous_acked_offset). Offset may 34 | * include the EOS position. 35 | */ 36 | quicly_ranges_t acked; 37 | /** 38 | * ranges that needs to be sent. Offset may include the EOS position. 39 | */ 40 | quicly_ranges_t pending; 41 | /** 42 | * number of bytes that have been inflight (regardless of acked or not). Used for capping max_data, therefore does not include 43 | * eos. 44 | */ 45 | uint64_t size_inflight; 46 | /** 47 | * UINT64_MAX until closed. Does not include the EOS position. 48 | */ 49 | uint64_t final_size; 50 | } quicly_sendstate_t; 51 | 52 | typedef struct st_quicly_sendstate_sent_t { 53 | uint64_t start; 54 | uint64_t end; 55 | } quicly_sendstate_sent_t; 56 | 57 | void quicly_sendstate_init(quicly_sendstate_t *state); 58 | void quicly_sendstate_init_closed(quicly_sendstate_t *state); 59 | void quicly_sendstate_dispose(quicly_sendstate_t *state); 60 | static int quicly_sendstate_transfer_complete(quicly_sendstate_t *state); 61 | static int quicly_sendstate_is_open(quicly_sendstate_t *state); 62 | static int quicly_sendstate_is_fully_inflight(quicly_sendstate_t *state); 63 | int quicly_sendstate_activate(quicly_sendstate_t *state); 64 | int quicly_sendstate_shutdown(quicly_sendstate_t *state, uint64_t final_size); 65 | void quicly_sendstate_reset(quicly_sendstate_t *state); 66 | int quicly_sendstate_acked(quicly_sendstate_t *state, quicly_sendstate_sent_t *args, size_t *bytes_to_shift); 67 | int quicly_sendstate_lost(quicly_sendstate_t *state, quicly_sendstate_sent_t *args); 68 | 69 | /* inline definitions */ 70 | 71 | inline int quicly_sendstate_transfer_complete(quicly_sendstate_t *state) 72 | { 73 | return state->final_size != UINT64_MAX && state->acked.ranges[0].end == state->final_size + 1; 74 | } 75 | 76 | inline int quicly_sendstate_is_open(quicly_sendstate_t *state) 77 | { 78 | return state->final_size == UINT64_MAX; 79 | } 80 | 81 | inline int quicly_sendstate_is_fully_inflight(quicly_sendstate_t *state) 82 | { 83 | return state->size_inflight == state->final_size; 84 | } 85 | 86 | #ifdef __cplusplus 87 | } 88 | #endif 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /include/quicly/streambuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef quicly_streambuf_h 23 | #define quicly_streambuf_h 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "picotls.h" 34 | #include "quicly.h" 35 | 36 | typedef struct st_quicly_sendbuf_vec_t quicly_sendbuf_vec_t; 37 | 38 | /** 39 | * Callback that flattens the contents of an iovec. 40 | * @param dst the destination 41 | * @param off offset within the iovec from where serialization should happen 42 | * @param len number of bytes to serialize 43 | * @return 0 if successful, otherwise an error code 44 | */ 45 | typedef quicly_error_t (*quicly_sendbuf_flatten_vec_cb)(quicly_sendbuf_vec_t *vec, void *dst, size_t off, size_t len); 46 | /** 47 | * An optional callback that is called when an iovec is discarded. 48 | */ 49 | typedef void (*quicly_sendbuf_discard_vec_cb)(quicly_sendbuf_vec_t *vec); 50 | 51 | typedef struct st_quicly_streambuf_sendvec_callbacks_t { 52 | quicly_sendbuf_flatten_vec_cb flatten_vec; 53 | quicly_sendbuf_discard_vec_cb discard_vec; 54 | } quicly_streambuf_sendvec_callbacks_t; 55 | 56 | struct st_quicly_sendbuf_vec_t { 57 | const quicly_streambuf_sendvec_callbacks_t *cb; 58 | size_t len; 59 | void *cbdata; 60 | }; 61 | 62 | /** 63 | * A simple stream-level send buffer that can be used to store data to be sent. 64 | */ 65 | typedef struct st_quicly_sendbuf_t { 66 | struct { 67 | quicly_sendbuf_vec_t *entries; 68 | size_t size, capacity; 69 | } vecs; 70 | size_t off_in_first_vec; 71 | uint64_t bytes_written; 72 | } quicly_sendbuf_t; 73 | 74 | /** 75 | * Initializes the send buffer. 76 | */ 77 | static void quicly_sendbuf_init(quicly_sendbuf_t *sb); 78 | /** 79 | * Disposes of the send buffer. 80 | */ 81 | void quicly_sendbuf_dispose(quicly_sendbuf_t *sb); 82 | /** 83 | * The concrete function to be used when `quicly_stream_callbacks_t::on_send_shift` is being invoked (i.e., applications using 84 | * `quicly_sendbuf_t` as the stream-level send buffer should call this function from it's `on_send_shift` callback). 85 | */ 86 | void quicly_sendbuf_shift(quicly_stream_t *stream, quicly_sendbuf_t *sb, size_t delta); 87 | /** 88 | * The concrete function for `quicly_stream_callbacks_t::on_send_emit`. 89 | */ 90 | void quicly_sendbuf_emit(quicly_stream_t *stream, quicly_sendbuf_t *sb, size_t off, void *dst, size_t *len, int *wrote_all); 91 | /** 92 | * Appends some bytes to the send buffer. The data being appended is copied. 93 | */ 94 | int quicly_sendbuf_write(quicly_stream_t *stream, quicly_sendbuf_t *sb, const void *src, size_t len); 95 | /** 96 | * Appends a vector to the send buffer. Members of the `quicly_sendbuf_vec_t` are copied. 97 | */ 98 | int quicly_sendbuf_write_vec(quicly_stream_t *stream, quicly_sendbuf_t *sb, quicly_sendbuf_vec_t *vec); 99 | 100 | /** 101 | * Pops the specified amount of bytes at the beginning of the simple stream-level receive buffer (which in fact is `ptls_buffer_t`). 102 | */ 103 | void quicly_recvbuf_shift(quicly_stream_t *stream, ptls_buffer_t *rb, size_t delta); 104 | /** 105 | * Returns an iovec that refers to the data available in the receive buffer. Applications are expected to call `quicly_recvbuf_get` 106 | * to first peek at the received data, process the bytes they can, then call `quicly_recvbuf_shift` to pop the bytes that have been 107 | * processed. 108 | */ 109 | ptls_iovec_t quicly_recvbuf_get(quicly_stream_t *stream, ptls_buffer_t *rb); 110 | /** 111 | * The concrete function for `quicly_stream_callbacks_t::on_receive`. 112 | */ 113 | int quicly_recvbuf_receive(quicly_stream_t *stream, ptls_buffer_t *rb, size_t off, const void *src, size_t len); 114 | 115 | /** 116 | * The simple stream buffer. The API assumes that stream->data points to quicly_streambuf_t. Applications can extend the structure 117 | * by passing arbitrary size to `quicly_streambuf_create`. 118 | */ 119 | typedef struct st_quicly_streambuf_t { 120 | quicly_sendbuf_t egress; 121 | ptls_buffer_t ingress; 122 | } quicly_streambuf_t; 123 | 124 | int quicly_streambuf_create(quicly_stream_t *stream, size_t sz); 125 | void quicly_streambuf_destroy(quicly_stream_t *stream, quicly_error_t err); 126 | static void quicly_streambuf_egress_shift(quicly_stream_t *stream, size_t delta); 127 | void quicly_streambuf_egress_emit(quicly_stream_t *stream, size_t off, void *dst, size_t *len, int *wrote_all); 128 | static int quicly_streambuf_egress_write(quicly_stream_t *stream, const void *src, size_t len); 129 | static int quicly_streambuf_egress_write_vec(quicly_stream_t *stream, quicly_sendbuf_vec_t *vec); 130 | int quicly_streambuf_egress_shutdown(quicly_stream_t *stream); 131 | static void quicly_streambuf_ingress_shift(quicly_stream_t *stream, size_t delta); 132 | static ptls_iovec_t quicly_streambuf_ingress_get(quicly_stream_t *stream); 133 | /** 134 | * Writes given data into `quicly_stream_buf_t::ingress` and returns 0 if successful. Upon failure, `quicly_close` is called 135 | * automatically, and a non-zero value is returned. Applications can ignore the returned value, or use it to find out if it can use 136 | * the information stored in the ingress buffer. 137 | */ 138 | int quicly_streambuf_ingress_receive(quicly_stream_t *stream, size_t off, const void *src, size_t len); 139 | 140 | /* inline definitions */ 141 | 142 | inline void quicly_sendbuf_init(quicly_sendbuf_t *sb) 143 | { 144 | memset(sb, 0, sizeof(*sb)); 145 | } 146 | 147 | inline void quicly_streambuf_egress_shift(quicly_stream_t *stream, size_t delta) 148 | { 149 | quicly_streambuf_t *sbuf = (quicly_streambuf_t *)stream->data; 150 | quicly_sendbuf_shift(stream, &sbuf->egress, delta); 151 | } 152 | 153 | inline int quicly_streambuf_egress_write(quicly_stream_t *stream, const void *src, size_t len) 154 | { 155 | quicly_streambuf_t *sbuf = (quicly_streambuf_t *)stream->data; 156 | return quicly_sendbuf_write(stream, &sbuf->egress, src, len); 157 | } 158 | 159 | inline int quicly_streambuf_egress_write_vec(quicly_stream_t *stream, quicly_sendbuf_vec_t *vec) 160 | { 161 | quicly_streambuf_t *sbuf = (quicly_streambuf_t *)stream->data; 162 | return quicly_sendbuf_write_vec(stream, &sbuf->egress, vec); 163 | } 164 | 165 | inline void quicly_streambuf_ingress_shift(quicly_stream_t *stream, size_t delta) 166 | { 167 | quicly_streambuf_t *sbuf = (quicly_streambuf_t *)stream->data; 168 | quicly_recvbuf_shift(stream, &sbuf->ingress, delta); 169 | } 170 | 171 | inline ptls_iovec_t quicly_streambuf_ingress_get(quicly_stream_t *stream) 172 | { 173 | quicly_streambuf_t *sbuf = (quicly_streambuf_t *)stream->data; 174 | return quicly_recvbuf_get(stream, &sbuf->ingress); 175 | } 176 | 177 | #ifdef __cplusplus 178 | } 179 | #endif 180 | 181 | #endif 182 | -------------------------------------------------------------------------------- /lib/cc-reno.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 Fastly, Janardhan Iyengar 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/cc.h" 23 | #include "quicly.h" 24 | 25 | /* TODO: Avoid increase if sender was application limited. */ 26 | static void reno_on_acked(quicly_cc_t *cc, const quicly_loss_t *loss, uint32_t bytes, uint64_t largest_acked, uint32_t inflight, 27 | int cc_limited, uint64_t next_pn, int64_t now, uint32_t max_udp_payload_size) 28 | { 29 | assert(inflight >= bytes); 30 | 31 | /* Do not increase congestion window while in recovery (but jumpstart may do something different). */ 32 | if (largest_acked < cc->recovery_end) { 33 | quicly_cc_jumpstart_on_acked(cc, 1, bytes, largest_acked, inflight, next_pn); 34 | return; 35 | } 36 | 37 | quicly_cc_jumpstart_on_acked(cc, 0, bytes, largest_acked, inflight, next_pn); 38 | 39 | /* Slow start. */ 40 | if (cc->cwnd < cc->ssthresh) { 41 | if (cc_limited) { 42 | cc->cwnd += bytes; 43 | if (cc->cwnd_maximum < cc->cwnd) 44 | cc->cwnd_maximum = cc->cwnd; 45 | } 46 | return; 47 | } 48 | /* Congestion avoidance. */ 49 | if (!cc_limited) 50 | return; 51 | cc->state.reno.stash += bytes; 52 | if (cc->state.reno.stash < cc->cwnd) 53 | return; 54 | /* Increase congestion window by 1 MSS per congestion window acked. */ 55 | uint32_t count = cc->state.reno.stash / cc->cwnd; 56 | cc->state.reno.stash -= count * cc->cwnd; 57 | cc->cwnd += count * max_udp_payload_size; 58 | if (cc->cwnd_maximum < cc->cwnd) 59 | cc->cwnd_maximum = cc->cwnd; 60 | } 61 | 62 | void quicly_cc_reno_on_lost(quicly_cc_t *cc, const quicly_loss_t *loss, uint32_t bytes, uint64_t lost_pn, uint64_t next_pn, 63 | int64_t now, uint32_t max_udp_payload_size) 64 | { 65 | quicly_cc__update_ecn_episodes(cc, bytes, lost_pn); 66 | 67 | /* Nothing to do if loss is in recovery window. */ 68 | if (lost_pn < cc->recovery_end) 69 | return; 70 | cc->recovery_end = next_pn; 71 | 72 | /* if detected loss before receiving all acks for jumpstart, restore original CWND */ 73 | if (cc->ssthresh == UINT32_MAX) 74 | quicly_cc_jumpstart_on_first_loss(cc, lost_pn); 75 | 76 | ++cc->num_loss_episodes; 77 | if (cc->cwnd_exiting_slow_start == 0) { 78 | cc->cwnd_exiting_slow_start = cc->cwnd; 79 | cc->exit_slow_start_at = now; 80 | } 81 | 82 | /* Reduce congestion window. */ 83 | cc->cwnd *= cc->ssthresh == UINT32_MAX ? 0.5 : QUICLY_RENO_BETA; /* without HyStart++, we overshoot by 2x in slowstart */ 84 | if (cc->cwnd < QUICLY_MIN_CWND * max_udp_payload_size) 85 | cc->cwnd = QUICLY_MIN_CWND * max_udp_payload_size; 86 | cc->ssthresh = cc->cwnd; 87 | 88 | if (cc->cwnd_minimum > cc->cwnd) 89 | cc->cwnd_minimum = cc->cwnd; 90 | } 91 | 92 | void quicly_cc_reno_on_persistent_congestion(quicly_cc_t *cc, const quicly_loss_t *loss, int64_t now) 93 | { 94 | /* TODO */ 95 | } 96 | 97 | void quicly_cc_reno_on_sent(quicly_cc_t *cc, const quicly_loss_t *loss, uint32_t bytes, int64_t now) 98 | { 99 | /* Unused */ 100 | } 101 | 102 | static void reno_reset(quicly_cc_t *cc, uint32_t initcwnd) 103 | { 104 | memset(cc, 0, sizeof(quicly_cc_t)); 105 | cc->type = &quicly_cc_type_reno; 106 | cc->cwnd = cc->cwnd_initial = cc->cwnd_maximum = initcwnd; 107 | cc->exit_slow_start_at = INT64_MAX; 108 | cc->ssthresh = cc->cwnd_minimum = UINT32_MAX; 109 | 110 | quicly_cc_jumpstart_reset(cc); 111 | } 112 | 113 | static int reno_on_switch(quicly_cc_t *cc) 114 | { 115 | if (cc->type == &quicly_cc_type_reno) { 116 | return 1; /* nothing to do */ 117 | } else if (cc->type == &quicly_cc_type_pico) { 118 | cc->type = &quicly_cc_type_reno; 119 | cc->state.reno.stash = cc->state.pico.stash; 120 | return 1; 121 | } else if (cc->type == &quicly_cc_type_cubic) { 122 | /* When in slow start, state can be reused as-is; otherwise, restart. */ 123 | if (cc->cwnd_exiting_slow_start == 0) { 124 | cc->type = &quicly_cc_type_reno; 125 | } else { 126 | reno_reset(cc, cc->cwnd_initial); 127 | } 128 | return 1; 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | static void reno_init(quicly_init_cc_t *self, quicly_cc_t *cc, uint32_t initcwnd, int64_t now) 135 | { 136 | reno_reset(cc, initcwnd); 137 | } 138 | 139 | quicly_cc_type_t quicly_cc_type_reno = {"reno", 140 | &quicly_cc_reno_init, 141 | reno_on_acked, 142 | quicly_cc_reno_on_lost, 143 | quicly_cc_reno_on_persistent_congestion, 144 | quicly_cc_reno_on_sent, 145 | reno_on_switch, 146 | quicly_cc_jumpstart_enter}; 147 | quicly_init_cc_t quicly_cc_reno_init = {reno_init}; 148 | 149 | quicly_cc_type_t *quicly_cc_all_types[] = {&quicly_cc_type_reno, &quicly_cc_type_cubic, &quicly_cc_type_pico, NULL}; 150 | 151 | uint32_t quicly_cc_calc_initial_cwnd(uint32_t max_packets, uint16_t max_udp_payload_size) 152 | { 153 | static const uint32_t mtu_max = 1472; 154 | 155 | /* apply filters to the two arguments */ 156 | if (max_packets < QUICLY_MIN_CWND) 157 | max_packets = QUICLY_MIN_CWND; 158 | if (max_udp_payload_size > mtu_max) 159 | max_udp_payload_size = mtu_max; 160 | 161 | uint64_t cwnd_bytes = (uint64_t)max_packets * max_udp_payload_size; 162 | return cwnd_bytes <= UINT32_MAX ? (uint32_t)cwnd_bytes : UINT32_MAX; 163 | } 164 | -------------------------------------------------------------------------------- /lib/frame.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include "quicly/frame.h" 25 | 26 | uint8_t *quicly_encode_path_challenge_frame(uint8_t *dst, int is_response, const uint8_t *data) 27 | { 28 | *dst++ = is_response ? QUICLY_FRAME_TYPE_PATH_RESPONSE : QUICLY_FRAME_TYPE_PATH_CHALLENGE; 29 | memcpy(dst, data, QUICLY_PATH_CHALLENGE_DATA_LEN); 30 | dst += QUICLY_PATH_CHALLENGE_DATA_LEN; 31 | return dst; 32 | } 33 | 34 | uint8_t *quicly_encode_ack_frame(uint8_t *dst, uint8_t *dst_end, quicly_ranges_t *ranges, uint64_t *ecn_counts, uint64_t ack_delay) 35 | { 36 | #define WRITE_BLOCK(start, end) \ 37 | do { \ 38 | uint64_t _start = (start), _end = (end); \ 39 | assert(_start < _end); \ 40 | if (dst_end - dst < 8) \ 41 | return NULL; \ 42 | dst = quicly_encodev(dst, _end - _start - 1); \ 43 | } while (0) 44 | 45 | /* emit ACK_ECN frame if any of the three ECN counts are non-zero */ 46 | uint8_t frame_type = (ecn_counts[0] | ecn_counts[1] | ecn_counts[2]) != 0 ? QUICLY_FRAME_TYPE_ACK_ECN : QUICLY_FRAME_TYPE_ACK; 47 | size_t range_index = ranges->num_ranges - 1; 48 | 49 | assert(ranges->num_ranges != 0); 50 | 51 | /* number of bytes being emitted without space check are 1 + 8 + 8 + 1 bytes (as defined in QUICLY_ACK_FRAME_CAPACITY) */ 52 | *dst++ = frame_type; 53 | dst = quicly_encodev(dst, ranges->ranges[range_index].end - 1); /* largest acknowledged */ 54 | dst = quicly_encodev(dst, ack_delay); /* ack delay */ 55 | PTLS_BUILD_ASSERT(QUICLY_MAX_ACK_BLOCKS - 1 <= 63); 56 | *dst++ = (uint8_t)(ranges->num_ranges - 1); /* ack blocks */ 57 | 58 | while (1) { 59 | WRITE_BLOCK(ranges->ranges[range_index].start, ranges->ranges[range_index].end); /* ACK block count */ 60 | if (range_index-- == 0) 61 | break; 62 | WRITE_BLOCK(ranges->ranges[range_index].end, ranges->ranges[range_index + 1].start); 63 | } 64 | 65 | if (frame_type == QUICLY_FRAME_TYPE_ACK_ECN) { 66 | uint8_t buf[24], *p = buf; 67 | for (size_t i = 0; i < 3; ++i) 68 | p = quicly_encodev(p, ecn_counts[i]); 69 | size_t len = p - buf; 70 | if (dst_end - dst < len) 71 | return NULL; 72 | memcpy(dst, buf, len); 73 | dst += len; 74 | } 75 | 76 | return dst; 77 | 78 | #undef WRITE_BLOCK 79 | } 80 | 81 | quicly_error_t quicly_decode_ack_frame(const uint8_t **src, const uint8_t *end, quicly_ack_frame_t *frame, int is_ack_ecn) 82 | { 83 | uint64_t i, num_gaps, gap, ack_range; 84 | 85 | if ((frame->largest_acknowledged = quicly_decodev(src, end)) == UINT64_MAX) 86 | goto Error; 87 | if ((frame->ack_delay = quicly_decodev(src, end)) == UINT64_MAX) 88 | goto Error; 89 | if ((num_gaps = quicly_decodev(src, end)) == UINT64_MAX) 90 | goto Error; 91 | 92 | if ((ack_range = quicly_decodev(src, end)) == UINT64_MAX) 93 | goto Error; 94 | if (frame->largest_acknowledged < ack_range) 95 | goto Error; 96 | frame->smallest_acknowledged = frame->largest_acknowledged - ack_range; 97 | frame->ack_block_lengths[0] = ack_range + 1; 98 | frame->num_gaps = 0; 99 | 100 | for (i = 0; i != num_gaps; ++i) { 101 | if ((gap = quicly_decodev(src, end)) == UINT64_MAX) 102 | goto Error; 103 | if ((ack_range = quicly_decodev(src, end)) == UINT64_MAX) 104 | goto Error; 105 | if (i < QUICLY_ACK_MAX_GAPS) { 106 | if (frame->smallest_acknowledged < gap + ack_range + 2) 107 | goto Error; 108 | frame->gaps[i] = gap + 1; 109 | frame->ack_block_lengths[i + 1] = ack_range + 1; 110 | frame->smallest_acknowledged -= gap + ack_range + 2; 111 | ++frame->num_gaps; 112 | } 113 | } 114 | 115 | if (is_ack_ecn) { 116 | for (i = 0; i < PTLS_ELEMENTSOF(frame->ecn_counts); ++i) 117 | if ((frame->ecn_counts[i] = quicly_decodev(src, end)) == UINT64_MAX) 118 | goto Error; 119 | } else { 120 | for (i = 0; i < PTLS_ELEMENTSOF(frame->ecn_counts); ++i) 121 | frame->ecn_counts[i] = 0; 122 | } 123 | 124 | return 0; 125 | Error: 126 | return QUICLY_TRANSPORT_ERROR_FRAME_ENCODING; 127 | } 128 | 129 | uint8_t *quicly_encode_close_frame(uint8_t *const base, uint64_t error_code, uint64_t offending_frame_type, 130 | const char *reason_phrase) 131 | { 132 | size_t offset = 0, reason_phrase_len = strlen(reason_phrase); 133 | 134 | #define PUSHV(v) \ 135 | do { \ 136 | if (base != NULL) { \ 137 | offset = quicly_encodev(base + offset, (v)) - base; \ 138 | } else { \ 139 | offset += quicly_encodev_capacity(v); \ 140 | } \ 141 | } while (0) 142 | 143 | PUSHV(offending_frame_type == UINT64_MAX ? QUICLY_FRAME_TYPE_APPLICATION_CLOSE : QUICLY_FRAME_TYPE_TRANSPORT_CLOSE); 144 | PUSHV(error_code); 145 | if (offending_frame_type != UINT64_MAX) 146 | PUSHV(offending_frame_type); 147 | PUSHV(reason_phrase_len); 148 | if (base != NULL) 149 | memcpy(base + offset, reason_phrase, reason_phrase_len); 150 | offset += reason_phrase_len; 151 | 152 | #undef PUSHV 153 | 154 | return base + offset; 155 | } 156 | -------------------------------------------------------------------------------- /lib/loss.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2020 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/loss.h" 23 | 24 | quicly_error_t quicly_loss_init_sentmap_iter(quicly_loss_t *loss, quicly_sentmap_iter_t *iter, int64_t now, uint32_t max_ack_delay, 25 | int is_closing) 26 | { 27 | quicly_sentmap_init_iter(&loss->sentmap, iter); 28 | 29 | int64_t retire_before = now - quicly_loss_get_sentmap_expiration_time(loss, max_ack_delay); 30 | 31 | /* Retire entries older than the time specified, unless the connection is alive and the number of packets in the sentmap is 32 | * below 32 packets. This exception (the threshold of 32) exists to be capable of recognizing excessively late-ACKs when under 33 | * heavy loss; in such case, 32 is more than enough, yet small enough that the memory footprint does not matter. */ 34 | const quicly_sent_packet_t *sent; 35 | while ((sent = quicly_sentmap_get(iter))->sent_at <= retire_before) { 36 | if (!is_closing && loss->sentmap.num_packets < 32) 37 | break; 38 | if (sent->cc_bytes_in_flight != 0) { 39 | /* cannot retire packets with cc_bytes_in_flight, but we may find retirable ones later in the map */ 40 | quicly_sentmap_skip(iter); 41 | continue; 42 | } 43 | quicly_error_t ret; 44 | if ((ret = quicly_sentmap_update(&loss->sentmap, iter, QUICLY_SENTMAP_EVENT_EXPIRED)) != 0) 45 | return ret; 46 | } 47 | 48 | /* rewind iter to the head of the sentmap, before returning it to the caller */ 49 | quicly_sentmap_init_iter(&loss->sentmap, iter); 50 | 51 | return 0; 52 | } 53 | 54 | quicly_error_t quicly_loss_detect_loss(quicly_loss_t *loss, int64_t now, uint32_t max_ack_delay, int is_1rtt_only, 55 | quicly_loss_on_detect_cb on_loss_detected) 56 | { 57 | /* This function ensures that the value returned in loss_time is when the next application timer should be set for loss 58 | * detection. if no timer is required, loss_time is set to INT64_MAX. */ 59 | 60 | const uint32_t delay_until_lost = ((loss->rtt.latest > loss->rtt.smoothed ? loss->rtt.latest : loss->rtt.smoothed) * 61 | (1024 + loss->thresholds.time_based_percentile) + 62 | 1023) / 63 | 1024; 64 | quicly_sentmap_iter_t iter; 65 | const quicly_sent_packet_t *sent; 66 | quicly_error_t ret; 67 | 68 | #define CHECK_TIME_THRESHOLD(sent) ((sent)->sent_at <= now - delay_until_lost) 69 | #define CHECK_PACKET_THRESHOLD(sent) \ 70 | (loss->thresholds.use_packet_based && \ 71 | (int64_t)(sent)->packet_number <= largest_acked_signed - QUICLY_LOSS_DEFAULT_PACKET_THRESHOLD) 72 | 73 | loss->loss_time = INT64_MAX; 74 | 75 | if ((ret = quicly_loss_init_sentmap_iter(loss, &iter, now, max_ack_delay, 0)) != 0) 76 | return ret; 77 | 78 | /* Mark packets as lost if they are smaller than the largest_acked and outside either time-threshold or packet-threshold 79 | * windows. Once marked as lost, cc_bytes_in_flight becomes zero. */ 80 | while ((sent = quicly_sentmap_get(&iter))->packet_number != UINT64_MAX) { 81 | int64_t largest_acked_signed = loss->largest_acked_packet_plus1[sent->ack_epoch] - 1; 82 | if ((int64_t)sent->packet_number < largest_acked_signed && (CHECK_TIME_THRESHOLD(sent) || CHECK_PACKET_THRESHOLD(sent))) { 83 | if (sent->cc_bytes_in_flight != 0) { 84 | on_loss_detected(loss, sent, !CHECK_PACKET_THRESHOLD(sent)); 85 | if ((ret = quicly_sentmap_update(&loss->sentmap, &iter, QUICLY_SENTMAP_EVENT_LOST)) != 0) 86 | return ret; 87 | } else { 88 | quicly_sentmap_skip(&iter); 89 | } 90 | } else { 91 | /* When only one PN space is active, it is possible to stop looking for packets that have to be considered lost and 92 | * continue on to calculating the loss time. Otherwise, iterate through the entire sentmap. */ 93 | if (is_1rtt_only) 94 | break; 95 | quicly_sentmap_skip(&iter); 96 | } 97 | } 98 | 99 | #undef CHECK_TIME_THRESHOLD 100 | #undef CHECK_PACKET_THRESHOLD 101 | 102 | if (!is_1rtt_only) { 103 | if ((ret = quicly_loss_init_sentmap_iter(loss, &iter, now, max_ack_delay, 0)) != 0) 104 | return ret; 105 | sent = quicly_sentmap_get(&iter); 106 | } 107 | 108 | /* schedule time-threshold alarm if there is a packet outstanding that is smaller than largest_acked */ 109 | while (sent->sent_at != INT64_MAX && sent->packet_number + 1 < loss->largest_acked_packet_plus1[sent->ack_epoch]) { 110 | if (sent->cc_bytes_in_flight != 0) { 111 | assert(now < sent->sent_at + delay_until_lost); 112 | loss->loss_time = sent->sent_at + delay_until_lost; 113 | break; 114 | } 115 | quicly_sentmap_skip(&iter); 116 | sent = quicly_sentmap_get(&iter); 117 | } 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /lib/rate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include "picotls.h" 25 | #include "quicly/rate.h" 26 | 27 | static void start_sampling(quicly_ratemeter_t *meter, int64_t now, uint64_t bytes_acked) 28 | { 29 | meter->current.start.at = now; 30 | meter->current.start.bytes_acked = bytes_acked; 31 | } 32 | 33 | static void commit_sample(quicly_ratemeter_t *meter) 34 | { 35 | ++meter->past_samples.latest; 36 | if (meter->past_samples.latest >= PTLS_ELEMENTSOF(meter->past_samples.entries)) 37 | meter->past_samples.latest = 0; 38 | meter->past_samples.entries[meter->past_samples.latest] = meter->current.sample; 39 | 40 | meter->current.start.at = INT64_MAX; 41 | meter->current.sample = (struct st_quicly_rate_sample_t){}; 42 | } 43 | 44 | void quicly_ratemeter_init(quicly_ratemeter_t *meter) 45 | { 46 | *meter = (quicly_ratemeter_t){ 47 | .past_samples = {.latest = PTLS_ELEMENTSOF(meter->past_samples.entries) - 1}, 48 | .pn_cwnd_limited = {.start = UINT64_MAX, .end = UINT64_MAX}, 49 | .current = {.start = {.at = INT64_MAX}}, 50 | }; 51 | } 52 | 53 | void quicly_ratemeter_enter_cc_limited(quicly_ratemeter_t *meter, uint64_t pn) 54 | { 55 | assert(!quicly_ratemeter_is_cc_limited(meter)); 56 | 57 | /* if the estimator was waiting for the end of the previous phase, and if a valid partial sample exists, commit it now */ 58 | if (meter->pn_cwnd_limited.end != UINT64_MAX && meter->current.sample.elapsed != 0) 59 | commit_sample(meter); 60 | 61 | /* begin new cwnd-limited phase */ 62 | meter->pn_cwnd_limited = (quicly_range_t){.start = pn, .end = UINT64_MAX}; 63 | } 64 | 65 | void quicly_ratemeter_exit_cc_limited(quicly_ratemeter_t *meter, uint64_t pn) 66 | { 67 | assert(quicly_ratemeter_is_cc_limited(meter)); 68 | 69 | meter->pn_cwnd_limited.end = pn; 70 | } 71 | 72 | void quicly_ratemeter_on_ack(quicly_ratemeter_t *meter, int64_t now, uint64_t bytes_acked, uint64_t pn) 73 | { 74 | if (meter->pn_cwnd_limited.start <= pn && pn < meter->pn_cwnd_limited.end) { 75 | /* At the moment, the flow is CWND-limited. Either start the timer or update. */ 76 | if (meter->current.start.at == INT64_MAX) { 77 | start_sampling(meter, now, bytes_acked); 78 | } else { 79 | /* Update current sample whenever receiving an ACK, so that the sample can be committed other than when receiving an ACK 80 | * (i.e., when opening a new CWND-limited phase). */ 81 | meter->current.sample = (struct st_quicly_rate_sample_t){ 82 | .elapsed = (uint32_t)(now - meter->current.start.at), 83 | .bytes_acked = (uint32_t)(bytes_acked - meter->current.start.bytes_acked), 84 | }; 85 | if (meter->current.sample.elapsed >= QUICLY_DELIVERY_RATE_SAMPLE_PERIOD) { 86 | commit_sample(meter); 87 | start_sampling(meter, now, bytes_acked); 88 | } 89 | } 90 | } else if (meter->pn_cwnd_limited.end <= pn) { 91 | /* We have exited CWND-limited state. Save current value, if any. */ 92 | if (meter->current.start.at != INT64_MAX) { 93 | if (meter->current.sample.elapsed != 0) 94 | commit_sample(meter); 95 | meter->pn_cwnd_limited = (quicly_range_t){.start = UINT64_MAX, .end = UINT64_MAX}; 96 | meter->current.start.at = INT64_MAX; 97 | } 98 | } 99 | } 100 | 101 | static uint64_t to_speed(uint64_t bytes_acked, uint32_t elapsed) 102 | { 103 | return bytes_acked * 1000 / elapsed; 104 | } 105 | 106 | void quicly_ratemeter_report(quicly_ratemeter_t *meter, quicly_rate_t *rate) 107 | { 108 | { /* Calculate latest, or return if there are no samples at all. `latest` being reported will be the most recent "full" sample 109 | * if available, or else a partial sample. */ 110 | const struct st_quicly_rate_sample_t *latest_sample = &meter->past_samples.entries[meter->past_samples.latest]; 111 | if (latest_sample->elapsed == 0) { 112 | latest_sample = &meter->current.sample; 113 | if (latest_sample->elapsed == 0) { 114 | rate->latest = rate->smoothed = rate->stdev = 0; 115 | return; 116 | } 117 | } 118 | rate->latest = to_speed(latest_sample->bytes_acked, latest_sample->elapsed); 119 | } 120 | 121 | #define FOREACH_SAMPLE(func) \ 122 | do { \ 123 | const struct st_quicly_rate_sample_t *sample; \ 124 | for (size_t i = 0; i < PTLS_ELEMENTSOF(meter->past_samples.entries); ++i) { \ 125 | if ((sample = &meter->past_samples.entries[i])->elapsed != 0) { \ 126 | func \ 127 | } \ 128 | } \ 129 | if ((sample = &meter->current.sample)->elapsed != 0) { \ 130 | func \ 131 | } \ 132 | } while (0) 133 | 134 | { /* calculate average */ 135 | uint64_t total_acked = 0; 136 | uint32_t total_elapsed = 0; 137 | FOREACH_SAMPLE({ 138 | total_acked += sample->bytes_acked; 139 | total_elapsed += sample->elapsed; 140 | }); 141 | rate->smoothed = to_speed(total_acked, total_elapsed); 142 | } 143 | 144 | { /* calculate stdev */ 145 | uint64_t sum = 0; 146 | size_t count = 0; 147 | FOREACH_SAMPLE({ 148 | uint64_t sample_speed = to_speed(sample->bytes_acked, sample->elapsed); 149 | sum += (sample_speed - rate->smoothed) * (sample_speed - rate->smoothed); 150 | ++count; 151 | }); 152 | rate->stdev = sqrt(sum / count); 153 | } 154 | 155 | #undef FOREACH_SAMPLE 156 | } 157 | -------------------------------------------------------------------------------- /lib/recvstate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/constants.h" 23 | #include "quicly/recvstate.h" 24 | 25 | void quicly_recvstate_init(quicly_recvstate_t *state) 26 | { 27 | quicly_ranges_init_with_range(&state->received, 0, 0); 28 | state->data_off = 0; 29 | state->eos = UINT64_MAX; 30 | } 31 | 32 | void quicly_recvstate_init_closed(quicly_recvstate_t *state) 33 | { 34 | quicly_ranges_init(&state->received); 35 | state->data_off = 0; 36 | state->eos = 0; 37 | } 38 | 39 | void quicly_recvstate_dispose(quicly_recvstate_t *state) 40 | { 41 | quicly_ranges_clear(&state->received); 42 | } 43 | 44 | quicly_error_t quicly_recvstate_update(quicly_recvstate_t *state, uint64_t off, size_t *len, int is_fin, size_t max_ranges) 45 | { 46 | assert(!quicly_recvstate_transfer_complete(state)); 47 | 48 | /* eos handling */ 49 | if (state->eos == UINT64_MAX) { 50 | if (is_fin) { 51 | state->eos = off + *len; 52 | if (state->eos < state->received.ranges[state->received.num_ranges - 1].end) 53 | return QUICLY_TRANSPORT_ERROR_FINAL_SIZE; 54 | } 55 | } else { 56 | if (off + *len > state->eos) 57 | return QUICLY_TRANSPORT_ERROR_FINAL_SIZE; 58 | } 59 | 60 | /* no state change; entire data has already been received */ 61 | if (off + *len <= state->data_off) { 62 | *len = 0; 63 | if (state->received.ranges[0].end == state->eos) 64 | goto Complete; 65 | return 0; 66 | } 67 | 68 | /* adjust if partially received */ 69 | if (off < state->data_off) { 70 | size_t delta = state->data_off - off; 71 | off += delta; 72 | *len -= delta; 73 | } 74 | 75 | /* update received range */ 76 | if (*len != 0) { 77 | int ret; 78 | if ((ret = quicly_ranges_add(&state->received, off, off + *len)) != 0) 79 | return ret; 80 | if (state->received.num_ranges > max_ranges) 81 | return QUICLY_ERROR_STATE_EXHAUSTION; 82 | } 83 | if (state->received.num_ranges == 1 && state->received.ranges[0].start == 0 && state->received.ranges[0].end == state->eos) 84 | goto Complete; 85 | 86 | return 0; 87 | 88 | Complete: 89 | quicly_ranges_clear(&state->received); 90 | return 0; 91 | } 92 | 93 | quicly_error_t quicly_recvstate_reset(quicly_recvstate_t *state, uint64_t eos_at, uint64_t *bytes_missing) 94 | { 95 | assert(!quicly_recvstate_transfer_complete(state)); 96 | 97 | /* validate */ 98 | if (state->eos != UINT64_MAX && state->eos != eos_at) 99 | return QUICLY_TRANSPORT_ERROR_FINAL_SIZE; 100 | if (eos_at < state->received.ranges[state->received.num_ranges - 1].end) 101 | return QUICLY_TRANSPORT_ERROR_FINAL_SIZE; 102 | 103 | /* calculate bytes missing */ 104 | *bytes_missing = eos_at - state->received.ranges[state->received.num_ranges - 1].end; 105 | 106 | /* clear the received range */ 107 | quicly_ranges_clear(&state->received); 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /lib/remote_cid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Fastly, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include 23 | #include "quicly/constants.h" 24 | #include "quicly/remote_cid.h" 25 | 26 | void quicly_remote_cid_init_set(quicly_remote_cid_set_t *set, ptls_iovec_t *initial_cid, void (*random_bytes)(void *, size_t)) 27 | { 28 | set->cids[0] = (quicly_remote_cid_t){ 29 | .state = QUICLY_REMOTE_CID_IN_USE, 30 | .sequence = 0, 31 | }; 32 | if (initial_cid != NULL) { 33 | quicly_set_cid(&set->cids[0].cid, *initial_cid); 34 | } else { 35 | random_bytes(set->cids[0].cid.cid, QUICLY_MIN_INITIAL_DCID_LEN); 36 | set->cids[0].cid.len = QUICLY_MIN_INITIAL_DCID_LEN; 37 | } 38 | random_bytes(set->cids[0].stateless_reset_token, sizeof(set->cids[0].stateless_reset_token)); 39 | 40 | for (size_t i = 1; i < PTLS_ELEMENTSOF(set->cids); i++) 41 | set->cids[i] = (quicly_remote_cid_t){ 42 | .state = QUICLY_REMOTE_CID_UNAVAILABLE, 43 | .sequence = i, 44 | }; 45 | 46 | set->_largest_sequence_expected = PTLS_ELEMENTSOF(set->cids) - 1; 47 | } 48 | 49 | static quicly_error_t do_register(quicly_remote_cid_set_t *set, uint64_t sequence, const uint8_t *cid, size_t cid_len, 50 | const uint8_t srt[QUICLY_STATELESS_RESET_TOKEN_LEN]) 51 | { 52 | int was_stored = 0; 53 | 54 | if (set->_largest_sequence_expected < sequence) 55 | return QUICLY_TRANSPORT_ERROR_CONNECTION_ID_LIMIT; 56 | 57 | for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) { 58 | if (set->cids[i].state != QUICLY_REMOTE_CID_UNAVAILABLE) { 59 | /* compare newly received CID against what we already have, to see if there is duplication/conflicts */ 60 | 61 | /* If an endpoint receives a NEW_CONNECTION_ID frame that repeats a previously issued connection ID with 62 | * a different Stateless Reset Token or a different sequence number, or if a sequence number is used for 63 | * different connection IDs, the endpoint MAY treat that receipt as a connection error of type PROTOCOL_VIOLATION. 64 | * (19.15) 65 | */ 66 | if (quicly_cid_is_equal(&set->cids[i].cid, ptls_iovec_init(cid, cid_len))) { 67 | if (set->cids[i].sequence == sequence && 68 | memcmp(set->cids[i].stateless_reset_token, srt, QUICLY_STATELESS_RESET_TOKEN_LEN) == 0) { 69 | /* likely a duplicate due to retransmission */ 70 | return 0; 71 | } else { 72 | /* received a frame that carries conflicting information */ 73 | return QUICLY_TRANSPORT_ERROR_PROTOCOL_VIOLATION; 74 | } 75 | } 76 | /* here we know CID is not equal */ 77 | if (set->cids[i].sequence == sequence) 78 | return QUICLY_TRANSPORT_ERROR_PROTOCOL_VIOLATION; 79 | } else if (set->cids[i].sequence == sequence) { 80 | assert(!was_stored); 81 | set->cids[i].sequence = sequence; 82 | quicly_set_cid(&set->cids[i].cid, ptls_iovec_init(cid, cid_len)); 83 | memcpy(set->cids[i].stateless_reset_token, srt, QUICLY_STATELESS_RESET_TOKEN_LEN); 84 | set->cids[i].state = QUICLY_REMOTE_CID_AVAILABLE; 85 | was_stored = 1; 86 | } 87 | } 88 | 89 | /* execution reaches here in two cases, 1) normal path, i.e. new CID was successfully registered, and 2) new CID was already 90 | * retired (was_stored == 0). */ 91 | return 0; 92 | } 93 | 94 | static void do_unregister(quicly_remote_cid_set_t *set, size_t idx_to_unreg) 95 | { 96 | set->cids[idx_to_unreg].state = QUICLY_REMOTE_CID_UNAVAILABLE; 97 | set->cids[idx_to_unreg].sequence = ++set->_largest_sequence_expected; 98 | } 99 | 100 | void quicly_remote_cid_unregister(quicly_remote_cid_set_t *set, uint64_t sequence) 101 | { 102 | for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) { 103 | if (sequence == set->cids[i].sequence) { 104 | do_unregister(set, i); 105 | return; 106 | } 107 | } 108 | assert(!"invalid CID sequence number"); 109 | } 110 | 111 | static size_t unregister_prior_to(quicly_remote_cid_set_t *set, uint64_t seq_unreg_prior_to, 112 | uint64_t unregistered_seqs[QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT]) 113 | { 114 | size_t num_unregistered = 0; 115 | 116 | for (size_t i = 0; i < PTLS_ELEMENTSOF(set->cids); i++) { 117 | if (set->cids[i].sequence < seq_unreg_prior_to) { 118 | unregistered_seqs[num_unregistered++] = set->cids[i].sequence; 119 | do_unregister(set, i); 120 | } 121 | } 122 | 123 | return num_unregistered; 124 | } 125 | 126 | quicly_error_t quicly_remote_cid_register(quicly_remote_cid_set_t *set, uint64_t sequence, const uint8_t *cid, size_t cid_len, 127 | const uint8_t srt[QUICLY_STATELESS_RESET_TOKEN_LEN], uint64_t retire_prior_to, 128 | uint64_t unregistered_seqs[QUICLY_LOCAL_ACTIVE_CONNECTION_ID_LIMIT], 129 | size_t *num_unregistered_seqs) 130 | { 131 | quicly_remote_cid_set_t backup = *set; /* preserve current state so that it can be restored to notify protocol violation */ 132 | quicly_error_t ret; 133 | 134 | assert(sequence >= retire_prior_to); 135 | 136 | /* First, handle retire_prior_to. This order is important as it is possible to receive a NEW_CONNECTION_ID frame such that it 137 | * retires active_connection_id_limit CIDs and then installs one new CID. */ 138 | *num_unregistered_seqs = unregister_prior_to(set, retire_prior_to, unregistered_seqs); 139 | 140 | /* Then, register given value. If an error occurs, restore the backup and send the error. */ 141 | if ((ret = do_register(set, sequence, cid, cid_len, srt)) != 0) { 142 | *set = backup; 143 | return ret; 144 | } 145 | 146 | return ret; 147 | } 148 | -------------------------------------------------------------------------------- /lib/retire_cid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Fastly, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "quicly/retire_cid.h" 24 | 25 | void quicly_retire_cid_init(quicly_retire_cid_set_t *set) 26 | { 27 | set->_num_pending = 0; 28 | } 29 | 30 | void quicly_retire_cid_push(quicly_retire_cid_set_t *set, uint64_t sequence) 31 | { 32 | if (set->_num_pending == PTLS_ELEMENTSOF(set->sequences)) { 33 | /* in case we don't find an empty slot, we'll just drop this sequence (never send RETIRE_CONNECTION_ID frame) */ 34 | return; 35 | } 36 | 37 | for (size_t i = 0; i < set->_num_pending; i++) { 38 | if (set->sequences[i] == sequence) { 39 | /* already scheduled */ 40 | return; 41 | } 42 | } 43 | 44 | set->sequences[set->_num_pending] = sequence; 45 | set->_num_pending++; 46 | } 47 | 48 | void quicly_retire_cid_shift(quicly_retire_cid_set_t *set, size_t num_shift) 49 | { 50 | assert(num_shift <= PTLS_ELEMENTSOF(set->sequences)); 51 | assert(num_shift <= set->_num_pending); 52 | /* move the remaining pending sequence numbers to the front */ 53 | memmove(set->sequences, set->sequences + num_shift, sizeof(set->sequences[0]) * (set->_num_pending - num_shift)); 54 | set->_num_pending -= num_shift; 55 | } 56 | -------------------------------------------------------------------------------- /lib/sendstate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include "picotls.h" 26 | #include "quicly/constants.h" 27 | #include "quicly/sendstate.h" 28 | 29 | void quicly_sendstate_init(quicly_sendstate_t *state) 30 | { 31 | quicly_ranges_init_with_range(&state->acked, 0, 0); 32 | quicly_ranges_init(&state->pending); 33 | state->size_inflight = 0; 34 | state->final_size = UINT64_MAX; 35 | } 36 | 37 | void quicly_sendstate_init_closed(quicly_sendstate_t *state) 38 | { 39 | quicly_sendstate_init(state); 40 | state->acked.ranges[0].end = 1; 41 | state->final_size = 0; 42 | } 43 | 44 | void quicly_sendstate_dispose(quicly_sendstate_t *state) 45 | { 46 | quicly_ranges_clear(&state->acked); 47 | quicly_ranges_clear(&state->pending); 48 | state->final_size = 0; 49 | state->size_inflight = 0; 50 | } 51 | 52 | int quicly_sendstate_activate(quicly_sendstate_t *state) 53 | { 54 | uint64_t end_off = state->final_size; 55 | 56 | /* take EOS position into account */ 57 | if (end_off != UINT64_MAX) 58 | ++end_off; 59 | 60 | /* do nothing if already active */ 61 | if (state->pending.num_ranges != 0 && state->pending.ranges[state->pending.num_ranges - 1].end == end_off) 62 | return 0; 63 | 64 | return quicly_ranges_add(&state->pending, state->size_inflight, end_off); 65 | } 66 | 67 | int quicly_sendstate_shutdown(quicly_sendstate_t *state, uint64_t final_size) 68 | { 69 | int ret; 70 | 71 | assert(quicly_sendstate_is_open(state)); 72 | assert(state->size_inflight <= final_size); 73 | 74 | if (state->pending.num_ranges != 0 && state->pending.ranges[state->pending.num_ranges - 1].end == UINT64_MAX) { 75 | state->pending.ranges[state->pending.num_ranges - 1].end = final_size + 1; 76 | } else { 77 | if ((ret = quicly_ranges_add(&state->pending, state->size_inflight, final_size + 1)) != 0) 78 | return ret; 79 | } 80 | 81 | state->final_size = final_size; 82 | return 0; 83 | } 84 | 85 | void quicly_sendstate_reset(quicly_sendstate_t *state) 86 | { 87 | int ret; 88 | 89 | if (state->final_size == UINT64_MAX) 90 | state->final_size = state->size_inflight; 91 | 92 | ret = quicly_ranges_add(&state->acked, 0, state->final_size + 1); 93 | assert(ret == 0 && "guaranteed to succeed, because the number of ranges never increases"); 94 | quicly_ranges_clear(&state->pending); 95 | } 96 | 97 | static int check_amount_of_state(quicly_sendstate_t *state) 98 | { 99 | size_t num_ranges = state->acked.num_ranges + state->pending.num_ranges; 100 | 101 | /* Bail out if number of gaps are small. 102 | * In case of HTTP/3, the worst case is when each HTTP request is received as a separate QUIC packet, and sending a small STREAM 103 | * frame carrying a HPACK encoder / decoder in response. If half of those STREAM frames are lost (note: loss of every other 104 | * packet can happen during slow start), `num_ranges` can become as large as `request_concurrency * 2`, as each gaps will be 105 | * recognized in `acked.num_ranges` and `pending.num_ranges`. */ 106 | if (PTLS_LIKELY(num_ranges < 256)) 107 | return 0; 108 | 109 | /* When there are large number of gaps, make sure that the amount of state retained in quicly is relatively smaller than the 110 | * amount of state retained by application (in form of the stream-level send buffer). 512 is used as the threshold, based on the 111 | * assumption that the STREAM frames that have been sent are on average at least 512 bytes long, when seeing excess number of 112 | * gaps. */ 113 | int64_t bytes_buffered = (int64_t)state->size_inflight - (int64_t)state->acked.ranges[0].end; 114 | if ((int64_t)num_ranges * 128 > bytes_buffered) 115 | return QUICLY_ERROR_STATE_EXHAUSTION; 116 | 117 | return 0; 118 | } 119 | 120 | int quicly_sendstate_acked(quicly_sendstate_t *state, quicly_sendstate_sent_t *args, size_t *bytes_to_shift) 121 | { 122 | uint64_t prev_sent_upto = state->acked.ranges[0].end; 123 | int ret; 124 | 125 | /* adjust acked and pending ranges */ 126 | if ((ret = quicly_ranges_add(&state->acked, args->start, args->end)) != 0) 127 | return ret; 128 | if ((ret = quicly_ranges_subtract(&state->pending, args->start, args->end)) != 0) 129 | return ret; 130 | assert(state->pending.num_ranges == 0 || state->acked.ranges[0].end <= state->pending.ranges[0].start); 131 | 132 | /* calculate number of bytes that can be retired from the send buffer */ 133 | if (prev_sent_upto != state->acked.ranges[0].end) { 134 | uint64_t sent_upto = state->acked.ranges[0].end; 135 | if (sent_upto > state->final_size) { 136 | /* adjust EOS position */ 137 | assert(sent_upto == state->final_size + 1); 138 | --sent_upto; 139 | } 140 | *bytes_to_shift = sent_upto - prev_sent_upto; 141 | } else { 142 | *bytes_to_shift = 0; 143 | } 144 | 145 | return check_amount_of_state(state); 146 | } 147 | 148 | int quicly_sendstate_lost(quicly_sendstate_t *state, quicly_sendstate_sent_t *args) 149 | { 150 | uint64_t start = args->start, end = args->end; 151 | size_t acked_slot = 0; 152 | int ret; 153 | 154 | while (start < end) { 155 | if (start < state->acked.ranges[acked_slot].end) 156 | start = state->acked.ranges[acked_slot].end; 157 | ++acked_slot; 158 | if (acked_slot == state->acked.num_ranges || end <= state->acked.ranges[acked_slot].start) { 159 | if (start < end) { 160 | if ((ret = quicly_ranges_add(&state->pending, start, end)) != 0) 161 | return ret; 162 | } 163 | goto Exit; 164 | } 165 | if (start < state->acked.ranges[acked_slot].start) { 166 | if ((ret = quicly_ranges_add(&state->pending, start, state->acked.ranges[acked_slot].start)) != 0) 167 | return ret; 168 | } 169 | } 170 | 171 | Exit: 172 | assert(state->pending.num_ranges == 0 || state->acked.ranges[0].end <= state->pending.ranges[0].start); 173 | return check_amount_of_state(state); 174 | } 175 | -------------------------------------------------------------------------------- /lib/sentmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include "picotls.h" 25 | #include "quicly/sentmap.h" 26 | 27 | const quicly_sent_t quicly_sentmap__end_iter = {quicly_sentmap__type_packet, {{UINT64_MAX, INT64_MAX}}}; 28 | 29 | static void next_entry(quicly_sentmap_iter_t *iter) 30 | { 31 | if (--iter->count != 0) { 32 | ++iter->p; 33 | } else if (*(iter->ref = &(*iter->ref)->next) == NULL) { 34 | iter->p = (quicly_sent_t *)&quicly_sentmap__end_iter; 35 | iter->count = 0; 36 | return; 37 | } else { 38 | assert((*iter->ref)->num_entries != 0); 39 | iter->count = (*iter->ref)->num_entries; 40 | iter->p = (*iter->ref)->entries; 41 | } 42 | while (iter->p->acked == NULL) 43 | ++iter->p; 44 | } 45 | 46 | static struct st_quicly_sent_block_t **free_block(quicly_sentmap_t *map, struct st_quicly_sent_block_t **ref) 47 | { 48 | static const struct st_quicly_sent_block_t dummy = {NULL}; 49 | static const struct st_quicly_sent_block_t *const dummy_ref = &dummy; 50 | struct st_quicly_sent_block_t *block = *ref; 51 | 52 | if (block->next != NULL) { 53 | *ref = block->next; 54 | assert((*ref)->num_entries != 0); 55 | } else { 56 | assert(block == map->tail); 57 | if (ref == &map->head) { 58 | map->head = NULL; 59 | map->tail = NULL; 60 | } else { 61 | map->tail = (void *)((char *)ref - offsetof(struct st_quicly_sent_block_t, next)); 62 | map->tail->next = NULL; 63 | } 64 | ref = (struct st_quicly_sent_block_t **)&dummy_ref; 65 | } 66 | 67 | free(block); 68 | return ref; 69 | } 70 | 71 | static void discard_entry(quicly_sentmap_t *map, quicly_sentmap_iter_t *iter) 72 | { 73 | assert(iter->p->acked != NULL); 74 | iter->p->acked = NULL; 75 | 76 | struct st_quicly_sent_block_t *block = *iter->ref; 77 | if (--block->num_entries == 0) { 78 | iter->ref = free_block(map, iter->ref); 79 | block = *iter->ref; 80 | iter->p = block->entries - 1; 81 | iter->count = block->num_entries + 1; 82 | } 83 | } 84 | 85 | void quicly_sentmap_dispose(quicly_sentmap_t *map) 86 | { 87 | struct st_quicly_sent_block_t *block; 88 | 89 | while ((block = map->head) != NULL) { 90 | map->head = block->next; 91 | free(block); 92 | } 93 | } 94 | 95 | quicly_error_t quicly_sentmap_prepare(quicly_sentmap_t *map, uint64_t packet_number, int64_t now, uint8_t ack_epoch) 96 | { 97 | assert(map->_pending_packet == NULL); 98 | 99 | if ((map->_pending_packet = quicly_sentmap_allocate(map, quicly_sentmap__type_packet)) == NULL) 100 | return PTLS_ERROR_NO_MEMORY; 101 | map->_pending_packet->data.packet = (quicly_sent_packet_t){packet_number, now, ack_epoch}; 102 | return 0; 103 | } 104 | 105 | struct st_quicly_sent_block_t *quicly_sentmap__new_block(quicly_sentmap_t *map) 106 | { 107 | struct st_quicly_sent_block_t *block; 108 | 109 | if ((block = malloc(sizeof(*block))) == NULL) 110 | return NULL; 111 | 112 | block->next = NULL; 113 | block->num_entries = 0; 114 | block->next_insert_at = 0; 115 | if (map->tail != NULL) { 116 | map->tail->next = block; 117 | map->tail = block; 118 | } else { 119 | map->head = map->tail = block; 120 | } 121 | 122 | return block; 123 | } 124 | 125 | void quicly_sentmap_skip(quicly_sentmap_iter_t *iter) 126 | { 127 | do { 128 | next_entry(iter); 129 | } while (iter->p->acked != quicly_sentmap__type_packet); 130 | } 131 | 132 | quicly_error_t quicly_sentmap_update(quicly_sentmap_t *map, quicly_sentmap_iter_t *iter, quicly_sentmap_event_t event) 133 | { 134 | quicly_sent_packet_t packet; 135 | quicly_error_t ret = 0; 136 | 137 | assert(iter->p != &quicly_sentmap__end_iter); 138 | assert(iter->p->acked == quicly_sentmap__type_packet); 139 | 140 | /* copy packet info */ 141 | packet = iter->p->data.packet; 142 | 143 | /* update CC state unless the event is PTO */ 144 | if (packet.cc_bytes_in_flight != 0 && event != QUICLY_SENTMAP_EVENT_PTO) { 145 | assert(map->bytes_in_flight >= packet.cc_bytes_in_flight); 146 | map->bytes_in_flight -= packet.cc_bytes_in_flight; 147 | iter->p->data.packet.cc_bytes_in_flight = 0; 148 | } 149 | iter->p->data.packet.frames_in_flight = 0; 150 | 151 | int should_notify = event == QUICLY_SENTMAP_EVENT_ACKED || packet.frames_in_flight, 152 | should_discard = event == QUICLY_SENTMAP_EVENT_ACKED || event == QUICLY_SENTMAP_EVENT_EXPIRED; 153 | 154 | /* Advance to next packet, while if necessary, doing either or both of the following: 155 | * * discard entries (if should_discard is set) 156 | * * invoke the frame-level callbacks (if should_notify is set) */ 157 | if (should_discard) { 158 | discard_entry(map, iter); 159 | --map->num_packets; 160 | } 161 | for (next_entry(iter); iter->p->acked != quicly_sentmap__type_packet; next_entry(iter)) { 162 | if (should_notify && (ret = iter->p->acked(map, &packet, event == QUICLY_SENTMAP_EVENT_ACKED, iter->p)) != 0) 163 | goto Exit; 164 | if (should_discard) 165 | discard_entry(map, iter); 166 | } 167 | 168 | Exit: 169 | return ret; 170 | } 171 | 172 | quicly_error_t quicly_sentmap__type_packet(quicly_sentmap_t *map, const quicly_sent_packet_t *packet, int acked, 173 | quicly_sent_t *sent) 174 | { 175 | assert(!"quicly_sentmap__type_packet cannot be called"); 176 | return QUICLY_TRANSPORT_ERROR_INTERNAL; 177 | } 178 | -------------------------------------------------------------------------------- /misc/build_libFuzzer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [ -e libFuzzer.a ] && exit 0 4 | [ -d Fuzzer ] || git clone https://github.com/llvm-mirror/compiler-rt.git Fuzzer 5 | #git checkout 29d1659edabe4ba2396f9697915bb7a0880cbd2f 6 | Fuzzer/lib/fuzzer/build.sh 7 | -------------------------------------------------------------------------------- /misc/docker-ci.mk: -------------------------------------------------------------------------------- 1 | CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2004 2 | SRC_DIR=/quicly 3 | CI_MK=$(SRC_DIR)/misc/docker-ci.mk 4 | CMAKE_ARGS= 5 | CHECK_ENVS= 6 | DOCKER_RUN_OPTS=--privileged \ 7 | -v `pwd`:$(SRC_DIR) \ 8 | -it 9 | 10 | ALL: 11 | docker run $(DOCKER_RUN_OPTS) $(CONTAINER_NAME) make -f $(CI_MK) _check CMAKE_ARGS='$(CMAKE_ARGS)' CHECK_ENVS='$(CHECK_ENVS)' 12 | 13 | _check: 14 | uname -a 15 | mkdir -p build 16 | sudo mount -t tmpfs tmpfs build -o size=3G 17 | sudo chown -R ci:ci build 18 | sudo chmod 0755 build 19 | $(MAKE) -f $(CI_MK) -C build _do-check CMAKE_ARGS='$(CMAKE_ARGS)' CHECK_ENVS='$(CHECK_ENVS)' 20 | 21 | _do-check: 22 | cmake $(CMAKE_ARGS) "-H$(SRC_DIR)" -B. 23 | $(MAKE) all VERBOSE=1 24 | env $(CHECK_ENVS) $(MAKE) check 25 | -------------------------------------------------------------------------------- /misc/find-cids.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | import json 4 | 5 | if len(sys.argv) != 2: 6 | print "Usage: find-cids.py inTrace.jsonl" 7 | sys.exit(1) 8 | 9 | cids = {} 10 | f = open(sys.argv[1], 'r') 11 | for line in f: 12 | event = json.loads(line) 13 | if event["type"] != "" and event["type"] == "accept": 14 | cids[event["conn"]] = None 15 | 16 | print "Connection IDs:", cids.keys() 17 | f.close() 18 | -------------------------------------------------------------------------------- /misc/quic-interop-runner/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM h2oserver/h2o-ci:ubuntu2004 as builder 2 | 3 | USER root 4 | WORKDIR / 5 | 6 | RUN apt-get update && \ 7 | apt-get install -y net-tools iputils-ping tcpdump ethtool iperf 8 | 9 | # build with --build-arg CACHEBUST=$(date +%s) 10 | ARG CACHEBUST=1 11 | 12 | # quicly 13 | RUN git clone https://github.com/h2o/quicly.git 14 | 15 | RUN cd quicly && git pull && git submodule update --init --recursive && cmake . && make 16 | 17 | 18 | FROM martenseemann/quic-network-simulator-endpoint:latest 19 | 20 | COPY --from=builder /quicly/cli quicly/cli 21 | 22 | # endpoint 23 | COPY run_endpoint.sh . 24 | RUN chmod +x run_endpoint.sh 25 | 26 | ENTRYPOINT [ "./run_endpoint.sh" ] 27 | -------------------------------------------------------------------------------- /misc/quic-interop-runner/deploy.sh: -------------------------------------------------------------------------------- 1 | docker build -t h2oserver/quicly-interop-runner:latest . --build-arg CACHEBUST=$(date +%s) 2 | docker push h2oserver/quicly-interop-runner:latest 3 | -------------------------------------------------------------------------------- /misc/quic-interop-runner/run_endpoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set up the routing needed for the simulation. 4 | /setup.sh 5 | cd quicly 6 | 7 | if [ ! -z "$TESTCASE" ]; then 8 | case "$TESTCASE" in 9 | "handshake"|"transfer"|"retry"|"goodput"|"resumption"|"multiconnect") ;; 10 | "http3") exit 127 ;; 11 | *) exit 127 ;; 12 | esac 13 | fi 14 | 15 | ### Client side ### 16 | if [ "$ROLE" == "client" ]; then 17 | # Wait for the simulator to start up. 18 | /wait-for-it.sh sim:57832 -s -t 30 19 | cd /downloads 20 | case "$TESTCASE" in 21 | "resumption") TEST_PARAMS="-s previous_sessions.bin" ;; 22 | *) ;; 23 | esac 24 | echo "Starting quicly client ..." 25 | if [ ! -z "$REQUESTS" ]; then 26 | echo "Requests: " $REQUESTS 27 | # Pull server and file names out of requests, generate file list for cli. 28 | for REQ in $REQUESTS; do 29 | SERVER=`echo $REQ | cut -f3 -d'/' | cut -f1 -d':'` 30 | FILE=`echo $REQ | cut -f4 -d'/'` 31 | FILES=${FILES}" "${FILE} 32 | CLI_LIST=${CLI_LIST}" -P /"${FILE} 33 | done 34 | 35 | if [ "$TESTCASE" == "resumption" ]; then 36 | # Client needs to be run twice. First, with one request. 37 | FILE=`echo $FILES | cut -f1 -d" "` 38 | echo "/quicly/cli -P /$FILE $SERVER 443" 39 | /quicly/cli -P "/"$FILE $TEST_PARAMS -a "hq-interop" -x x25519 -x secp256r1 -e /logs/$TESTCASE.out $SERVER 443 40 | 41 | # Second time, with rest of the requests. 42 | CLI_LIST=`echo $CLI_LIST | cut -f3- -d" "` 43 | echo "/quicly/cli $CLI_LIST $SERVER 443" 44 | /quicly/cli $CLI_LIST $TEST_PARAMS -a "hq-interop" -x x25519 -x secp256r1 -e /logs/$TESTCASE.out $SERVER 443 45 | rm -f previous_sessions.bin 46 | 47 | elif [ "$TESTCASE" == "multiconnect" ]; then 48 | # Client needs to be run once per file. 49 | for FILE in $FILES; do 50 | echo "/quicly/cli /$FILE $SERVER 443" 51 | /quicly/cli -P "/"$FILE $TEST_PARAMS -a "hq-interop" -x x25519 -x secp256r1 -e /logs/$TESTCASE.out $SERVER 443 52 | done 53 | 54 | else 55 | # Client is run once for all files. 56 | echo "/quicly/cli $CLI_LIST $SERVER 443" 57 | /quicly/cli $CLI_LIST $TEST_PARAMS -a "hq-interop" -x x25519 -x secp256r1 -e /logs/$TESTCASE.out $SERVER 443 58 | fi 59 | 60 | # Cleanup. 61 | for FILE in $FILES; do 62 | mv $FILE.downloaded $FILE 63 | done 64 | fi 65 | 66 | ### Server side ### 67 | elif [ "$ROLE" == "server" ]; then 68 | echo "Starting server for test:" $TESTCASE 69 | echo "Serving files:" 70 | cd /www && ls -l 71 | case "$TESTCASE" in 72 | "retry") TEST_PARAMS="-R" ;; 73 | *) ;; 74 | esac 75 | echo "Starting quicly server ..." 76 | echo "SERVER_PARAMS:" $SERVER_PARAMS "TEST_PARAMS:" $TEST_PARAMS 77 | echo "/quicly/cli $SERVER_PARAMS $TEST_PARAMS -k /certs/priv.key -c /certs/cert.pem -e /logs/$TESTCASE.out 0.0.0.0 443" 78 | /quicly/cli $SERVER_PARAMS $TEST_PARAMS -k /certs/priv.key -c /certs/cert.pem -x x25519 -x secp256r1 -a "hq-interop" -e /logs/$TESTCASE.out 0.0.0.0 443 79 | fi 80 | -------------------------------------------------------------------------------- /misc/quic-interop-runner/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDOjCCAiKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtIMk8g 3 | VGVzdCBDQTAeFw0xNDEyMTAxOTMzMDVaFw0yNDEyMDcxOTMzMDVaMBsxGTAXBgNV 4 | BAMTEDEyNy4wLjAuMS54aXAuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 5 | AoIBAQDvNmF5nimH3wlp50E2/2SqxUD0JKaF3r2QFz1kB9UUwDhVDCms6PdkavF/ 6 | bQcHcWS+oa97D1miBQXo2Ns+6Z6JQ5sak/bVjnBxiU8vhqiOWvAwH947E4Km5HJB 7 | NFJJ7WEM+90kAFB2ayEM/llIQEt1RKCs2fgpaEgOMWPUAdcgyp6pNd60W5GA3Md2 8 | 1tdDH5RYGKzYHqpkm6pICtvaaxU4LwPmA3Oc8+VDDsVt08Jos1dJvoacjQTS6PpC 9 | ZiUDD2zqeSA//PGN8WV2o81SmsZwSpPCYBvxVW13tdsA1ivO5tng2fr9ZesKtXFZ 10 | SaH/tKmB3Br8jg2vUke/0cfIvbP/AgMBAAGjgY0wgYowCQYDVR0TBAIwADAsBglg 11 | hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O 12 | BBYEFJXhddVQ68vtPvxoHWHsYkLnu3+4MDAGA1UdIwQpMCehGqQYMBYxFDASBgNV 13 | BAMTC0gyTyBUZXN0IENBggkAmqS1V7DvzbYwDQYJKoZIhvcNAQELBQADggEBAJQ2 14 | uvzL/lZnrsF4cvHhl/mg+s/RjHwvqFRrxOWUeWu2BQOGdd1Izqr8ZbF35pevPkXe 15 | j3zQL4Nf8OxO/gx4w0165KL4dYxEW7EaxsDQUI2aXSW0JNSvK2UGugG4+E4aT+9y 16 | cuBCtfWbL4/N6IMt2QW17B3DcigkreMoZavnnqRecQWkOx4nu0SmYg1g2QV4kRqT 17 | nvLt29daSWjNhP3dkmLTxn19umx26/JH6rqcgokDfHHO8tlDbc9JfyxYH01ZP2Ps 18 | esIiGa/LBXfKiPXxyHuNVQI+2cMmIWYf+Eu/1uNV3K55fA8806/FeklcQe/vvSCU 19 | Vw6RN5S/14SQnMYWr7E= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /misc/quic-interop-runner/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA7zZheZ4ph98JaedBNv9kqsVA9CSmhd69kBc9ZAfVFMA4VQwp 3 | rOj3ZGrxf20HB3FkvqGvew9ZogUF6NjbPumeiUObGpP21Y5wcYlPL4aojlrwMB/e 4 | OxOCpuRyQTRSSe1hDPvdJABQdmshDP5ZSEBLdUSgrNn4KWhIDjFj1AHXIMqeqTXe 5 | tFuRgNzHdtbXQx+UWBis2B6qZJuqSArb2msVOC8D5gNznPPlQw7FbdPCaLNXSb6G 6 | nI0E0uj6QmYlAw9s6nkgP/zxjfFldqPNUprGcEqTwmAb8VVtd7XbANYrzubZ4Nn6 7 | /WXrCrVxWUmh/7Spgdwa/I4Nr1JHv9HHyL2z/wIDAQABAoIBAEVPf2zKrAPnVwXt 8 | cJLr6xIj908GM43EXS6b3TjXoCDUFT5nOMgV9GCPMAwY3hmE/IjTtlG0v+bXB8BQ 9 | 3S3caQgio5VO3A1CqUfsXhpKLRqaNM/s2+pIG+oZdRV5gIJVGnK1o3yj7qxxG/F0 10 | 3Q+3OWXwDZIn0eTFh2M9YkxygA/KtkREZWv8Q8qZpdOpJSBYZyGE97Jqy/yGc+DQ 11 | Vpoa9B8WwnIdUn47TkZfsbzqGIYZxatJQDC1j7Y+F8So7zBbUhpz7YqATQwf5Efm 12 | K2xwvlwfdwykq6ffEr2M/Xna0220G2JZlGq3Cs2X9GT9Pt9OS86Bz+EL46ELo0tZ 13 | yfHQe/kCgYEA+zh4k2be6fhQG+ChiG3Ue5K/kH2prqyGBus61wHnt8XZavqBevEy 14 | 4pdmvJ6Q1Ta9Z2YCIqqNmlTdjZ6B35lvAK8YFITGy0MVV6K5NFYVfhALWCQC2r3B 15 | 6uH39FQ0mDo3gS5ZjYlUzbu67LGFnyX+pyMr2oxlhI1fCY3VchXQAOsCgYEA88Nt 16 | CwSOaZ1fWmyNAgXEAX1Jx4XLFYgjcA/YBXW9gfQ0AfufB346y53PsgjX1lB+Bbcg 17 | cY/o5W7F0b3A0R4K5LShlPCq8iB2DC+VnpKwTgo8ylh+VZCPy2BmMK0jrrmyqWeg 18 | PzwgP0lp+7l/qW8LDImeYi8nWoqd6f1ye4iJdD0CgYEAlIApJljk5EFYeWIrmk3y 19 | EKoKewsNRqfNAkICoh4KL2PQxaAW8emqPq9ol47T5nVZOMnf8UYINnZ8EL7l3psA 20 | NtNJ1Lc4G+cnsooKGJnaUo6BZjTDSzJocsPoopE0Fdgz/zS60yOe8Y5LTKcTaaQ4 21 | B+yOe74KNHSs/STOS4YBUskCgYAIqaRBZPsOo8oUs5DbRostpl8t2QJblIf13opF 22 | v2ZprN0ASQngwUqjm8sav5e0BQ5Fc7mSb5POO36KMp0ckV2/vO+VFGxuyFqJmlNN 23 | 3Fapn1GDu1tZ/RYvGxDmn/CJsA26WXVnaeKXfStoB7KSueCBpI5dXOGgJRbxjtE3 24 | tKV13QKBgQCtmLtTJPJ0Z+9n85C8kBonk2MCnD9JTYWoDQzNMYGabthzSqJqcEek 25 | dvhr82XkcHM+r6+cirjdQr4Qj7/2bfZesHl5XLvoJDB1YJIXnNJOELwbktrJrXLc 26 | dJ+MMvPvBAMah/tqr2DqgTGfWLDt9PJiCJVsuN2kD9toWHV08pY0Og== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /misc/quictrace-adapter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | import sys 4 | import json 5 | import base64 6 | import time 7 | import os 8 | from collections import OrderedDict 9 | from pprint import pprint 10 | 11 | def usage(): 12 | print(r""" 13 | Usage: 14 | quictrace-adapter.py inTrace.jsonl outTrace.json cid 15 | quictrace-adapter.py inTrace.jsonl outTraceDir 16 | """.strip()) 17 | 18 | 19 | epoch = ["ENCRYPTION_INITIAL", "ENCRYPTION_0RTT", "ENCRYPTION_HANDSHAKE", "ENCRYPTION_1RTT"] 20 | 21 | def transform(inf, outf, cid): 22 | start = -1 23 | qtr = {} 24 | qtr["protocolVersion"] = "AAAA" 25 | qtr["destinationConnectionId"] = base64.b64encode(str(cid)) 26 | qtr["events"] = [] 27 | packet = {} 28 | sframes = [] 29 | rframes = [] 30 | state = {} 31 | 32 | for line in inf: 33 | if line[0] != "{": 34 | continue 35 | trace = json.loads(line) 36 | type = trace["type"] 37 | if len(type) < 11 or type[:9] != "quictrace" or trace["conn"] != cid: 38 | continue 39 | 40 | event = type[10:] 41 | if event == "sent" or event == "recv" or event == "lost": 42 | # if last loss was not posted, do it now (TSNH) 43 | if packet and packet["eventType"] == "PACKET_LOST": 44 | qtr["events"].append(packet) 45 | packet = {} 46 | # multiple packet losses. TODO: store packet and post after congestion state read. 47 | # print "WARNING: Packet lost but no transport state posted" 48 | 49 | # close out previous received packet if it's still open 50 | if rframes: 51 | packet = {} 52 | packet["eventType"] = "PACKET_RECEIVED" 53 | packet["encryptionLevel"] = epoch[3] # hack 54 | packet["timeUs"] = str((rtime - start) * 1000) 55 | packet["packetNumber"] = str(rpn) 56 | packet["frames"] = rframes 57 | packet["transportState"] = state 58 | qtr["events"].append(packet) 59 | # reset state 60 | packet = {} 61 | rframes = [] 62 | state = {} 63 | 64 | # packet lost 65 | if event == "lost": 66 | # if last loss was not posted, do it now 67 | if packet and packet["eventType"] == "PACKET_LOST": 68 | qtr["events"].append(packet) 69 | # record new loss 70 | packet = {} 71 | packet["eventType"] = "PACKET_LOST" 72 | packet["encryptionLevel"] = epoch[3] 73 | if start == -1: start = trace["time"] 74 | packet["timeUs"] = str((trace["time"] - start) * 1000) 75 | packet["packetNumber"] = str(trace["pn"]) 76 | # don't post yet, wait to read transport state 77 | 78 | # transport state after loss 79 | if event == "cc-lost" and "eventType" in packet and packet["eventType"] == "PACKET_LOST": 80 | state = {} 81 | state["minRttUs"] = str(trace["min-rtt"] * 1000) 82 | state["smoothedRttUs"] = str(trace["smoothed-rtt"] * 1000) 83 | state["lastRttUs"] = str(trace["latest-rtt"] * 1000) 84 | state["inFlightBytes"] = str(trace["inflight"]) 85 | state["cwndBytes"] = str(trace["cwnd"]) 86 | # post last lost packet with state 87 | packet["transportState"] = state 88 | qtr["events"].append(packet) 89 | # reset state 90 | packet = {} 91 | 92 | # packet send event 93 | if event == "sent": 94 | packet = {} 95 | packet["eventType"] = "PACKET_SENT" 96 | if start == -1: start = trace["time"] 97 | packet["timeUs"] = str((trace["time"] - start) * 1000) 98 | packet["packetNumber"] = str(trace["pn"]) 99 | packet["packetSize"] = str(trace["len"]) 100 | packet["encryptionLevel"] = epoch[trace["packet-type"]] 101 | packet["frames"] = sframes 102 | qtr["events"].append(packet) 103 | # reset state 104 | packet = {} 105 | sframes = [] # empty sent frames list 106 | 107 | # STREAM frame sent 108 | if event == "send-stream": 109 | info = {} 110 | info["streamId"] = str(trace["stream-id"]) 111 | if (trace["stream-id"] < 0): 112 | info["streamId"] = str(31337 + trace["stream-id"]) 113 | if trace["fin"] == 0: 114 | info["fin"] = False 115 | else: 116 | info["fin"] = True 117 | info["length"] = str(trace["len"]) 118 | info["offset"] = str(trace["off"]) 119 | # create and populate new frame, add to frames list 120 | frame = {} 121 | frame["frameType"] = "STREAM" 122 | frame["streamFrameInfo"] = info 123 | sframes.append(frame) 124 | 125 | # packet received 126 | if event == "recv": 127 | if start == -1: start = trace["time"] 128 | rtime = trace["time"] 129 | rpn = trace["pn"] 130 | rframes = [] 131 | acked = [] 132 | state = {} 133 | 134 | # process ACK frame info 135 | if "recv-ack" in event: 136 | if "recv-ack-delay" not in event: 137 | # create ack block, add to list 138 | block = {"firstPacket": str(trace["ack-block-begin"]), 139 | "lastPacket": str(trace["ack-block-end"])} 140 | acked.append(block) 141 | continue 142 | # "ack-delay" line closes out ACK frame processing 143 | ack_info = {} 144 | ack_info["ackDelayUs"] = str(trace["ack-delay"]) 145 | ack_info["ackedPackets"] = acked 146 | frame = {} 147 | frame["frameType"] = "ACK" 148 | frame["ackInfo"] = ack_info 149 | rframes.append(frame) 150 | 151 | if event == "cc-ack": 152 | state = {} 153 | state["minRttUs"] = str(trace["min-rtt"] * 1000) 154 | state["smoothedRttUs"] = str(trace["smoothed-rtt"] * 1000) 155 | state["lastRttUs"] = str(trace["latest-rtt"] * 1000) 156 | state["inFlightBytes"] = str(trace["inflight"]) 157 | state["cwndBytes"] = str(trace["cwnd"]) 158 | # state["pacingRateBps"] = str(trace[""]) 159 | 160 | # close out last received packet if it's still open 161 | if rframes: 162 | # packet = {} 163 | packet["eventType"] = "PACKET_RECEIVED" 164 | packet["encryptionLevel"] = epoch[3] # hack 165 | packet["timeUs"] = str((rtime - start) * 1000) 166 | packet["packetNumber"] = str(rpn) 167 | packet["frames"] = rframes 168 | packet["transportState"] = state 169 | qtr["events"].append(packet) 170 | 171 | # finished processing 172 | json.dump(qtr, outf) 173 | 174 | 175 | def find_cids(infile): 176 | cids = OrderedDict() 177 | with open(infile, 'r') as f: 178 | for line in f: 179 | event = json.loads(line) 180 | if event["type"] == "accept": 181 | cids[event["conn"]] = event 182 | return cids 183 | 184 | def mkdir_p(dirname): 185 | try: 186 | os.makedirs(dirname) 187 | except OSError: 188 | pass 189 | 190 | def main(): 191 | if len(sys.argv) == 3: 192 | (_, infile, outdir) = sys.argv 193 | for cid, event in find_cids(infile).items(): 194 | timestamp = time.strftime('%FT%TZ', time.gmtime(event["time"] / 1000)) 195 | mkdir_p(outdir) 196 | outfile = os.path.join(outdir, '{timestamp}-{cid}.json'.format(timestamp=timestamp, cid=cid)) 197 | with open(infile, 'r') as inf, open(outfile, 'w') as outf: 198 | print("Transforming %s" % outfile, file = sys.stderr) 199 | transform(inf, outf, int(cid)) 200 | elif len(sys.argv) == 4: 201 | (_, infile, outfile, cid) = sys.argv 202 | with open(infile, 'r') as inf, open(outfile, 'w') as outf: 203 | print("Transforming %s" % outfile, file = sys.stderr) 204 | transform(inf, outf, int(cid)) 205 | else: 206 | usage() 207 | sys.exit(1) 208 | 209 | 210 | if __name__ == "__main__": 211 | main() 212 | 213 | -------------------------------------------------------------------------------- /parselog.py: -------------------------------------------------------------------------------- 1 | # Generates trace files with sent packet and ack information from quicly log. 2 | import sys 3 | import json 4 | 5 | def find_cids(f): 6 | cids = {} 7 | for line in f: 8 | event = json.loads(line) 9 | if event["type"] == "": continue 10 | cids[event["conn"]] = None 11 | return cids 12 | 13 | 14 | def gen_trace(f, cid, sent_trace, ack_trace): 15 | conn_start = 0 16 | for line in f: 17 | event = json.loads(line) 18 | if event["type"] == "" or event["conn"] != cid: continue 19 | 20 | # sent packet 21 | if event["type"] == "packet-sent": 22 | if conn_start == 0: conn_start = event["time"] 23 | outstr = str(event["time"] - conn_start) + " " + \ 24 | str(event["pn"]) + " " + \ 25 | str(event["len"]) 26 | sent_trace.write(outstr + "\n") 27 | 28 | # received ack 29 | if event["type"] == "packet-acked": 30 | outstr = str(event["time"] - conn_start) + " " + \ 31 | str(event["pn"]) 32 | ack_trace.write(outstr + "\n") 33 | 34 | 35 | def main(): 36 | if len(sys.argv) == 2: 37 | print "Connection IDs:", find_cids(open(sys.argv[1], 'r')).keys() 38 | return 1 39 | elif len(sys.argv) != 3: 40 | print "Usage: python parselog.py logfile [connection_id]" 41 | return 0 42 | 43 | f = open(sys.argv[1], 'r') 44 | cid = int(sys.argv[2]) 45 | sent_trace = open("trace." + str(cid) + ".str", 'w') 46 | ack_trace = open("trace." + str(cid) + ".atr", 'w') 47 | gen_trace(f, cid, sent_trace, ack_trace) 48 | f.close() 49 | sent_trace.close() 50 | ack_trace.close() 51 | 52 | 53 | main() 54 | -------------------------------------------------------------------------------- /plot.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | 3 | QT="/Users/jiyengar/quic-trace" 4 | 5 | if [ "$#" -ne 2 ]; then 6 | echo "Usage: $0 quicly-trace outputfile" 7 | exit 1 8 | fi 9 | 10 | python quictrace-adapter.py $1 $2.json &&\ 11 | $QT/bazel-bin/tools/transform_quic_trace --input_format=json < $2.json > $2.qtr &&\ 12 | $QT/bazel-bin/tools/render/render_trace $2.qtr 13 | -------------------------------------------------------------------------------- /quicly.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /t/assets/ec256-key-pair.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIBswGBU5+SsrFYQupnJ/GVf1bYhBEmJiBpxLL4jXZRrpoAoGCCqGSM49 3 | AwEHoUQDQgAEWF0BvlHl/ZVaoApefcN5+emI6cjSDbR3aP843VWgMLfxNqvmWut0 4 | KsoRQC2OHJ+Z8HoLZcNnA7Mc3/ypHSUqrw== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /t/assets/ec256-pub.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWF0BvlHl/ZVaoApefcN5+emI6cjS 3 | DbR3aP843VWgMLfxNqvmWut0KsoRQC2OHJ+Z8HoLZcNnA7Mc3/ypHSUqrw== 4 | -----END PUBLIC KEY----- 5 | -------------------------------------------------------------------------------- /t/assets/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDOjCCAiKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDEwtIMk8g 3 | VGVzdCBDQTAeFw0xNDEyMTAxOTMzMDVaFw0yNDEyMDcxOTMzMDVaMBsxGTAXBgNV 4 | BAMTEDEyNy4wLjAuMS54aXAuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 5 | AoIBAQDvNmF5nimH3wlp50E2/2SqxUD0JKaF3r2QFz1kB9UUwDhVDCms6PdkavF/ 6 | bQcHcWS+oa97D1miBQXo2Ns+6Z6JQ5sak/bVjnBxiU8vhqiOWvAwH947E4Km5HJB 7 | NFJJ7WEM+90kAFB2ayEM/llIQEt1RKCs2fgpaEgOMWPUAdcgyp6pNd60W5GA3Md2 8 | 1tdDH5RYGKzYHqpkm6pICtvaaxU4LwPmA3Oc8+VDDsVt08Jos1dJvoacjQTS6PpC 9 | ZiUDD2zqeSA//PGN8WV2o81SmsZwSpPCYBvxVW13tdsA1ivO5tng2fr9ZesKtXFZ 10 | SaH/tKmB3Br8jg2vUke/0cfIvbP/AgMBAAGjgY0wgYowCQYDVR0TBAIwADAsBglg 11 | hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O 12 | BBYEFJXhddVQ68vtPvxoHWHsYkLnu3+4MDAGA1UdIwQpMCehGqQYMBYxFDASBgNV 13 | BAMTC0gyTyBUZXN0IENBggkAmqS1V7DvzbYwDQYJKoZIhvcNAQELBQADggEBAJQ2 14 | uvzL/lZnrsF4cvHhl/mg+s/RjHwvqFRrxOWUeWu2BQOGdd1Izqr8ZbF35pevPkXe 15 | j3zQL4Nf8OxO/gx4w0165KL4dYxEW7EaxsDQUI2aXSW0JNSvK2UGugG4+E4aT+9y 16 | cuBCtfWbL4/N6IMt2QW17B3DcigkreMoZavnnqRecQWkOx4nu0SmYg1g2QV4kRqT 17 | nvLt29daSWjNhP3dkmLTxn19umx26/JH6rqcgokDfHHO8tlDbc9JfyxYH01ZP2Ps 18 | esIiGa/LBXfKiPXxyHuNVQI+2cMmIWYf+Eu/1uNV3K55fA8806/FeklcQe/vvSCU 19 | Vw6RN5S/14SQnMYWr7E= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /t/assets/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA7zZheZ4ph98JaedBNv9kqsVA9CSmhd69kBc9ZAfVFMA4VQwp 3 | rOj3ZGrxf20HB3FkvqGvew9ZogUF6NjbPumeiUObGpP21Y5wcYlPL4aojlrwMB/e 4 | OxOCpuRyQTRSSe1hDPvdJABQdmshDP5ZSEBLdUSgrNn4KWhIDjFj1AHXIMqeqTXe 5 | tFuRgNzHdtbXQx+UWBis2B6qZJuqSArb2msVOC8D5gNznPPlQw7FbdPCaLNXSb6G 6 | nI0E0uj6QmYlAw9s6nkgP/zxjfFldqPNUprGcEqTwmAb8VVtd7XbANYrzubZ4Nn6 7 | /WXrCrVxWUmh/7Spgdwa/I4Nr1JHv9HHyL2z/wIDAQABAoIBAEVPf2zKrAPnVwXt 8 | cJLr6xIj908GM43EXS6b3TjXoCDUFT5nOMgV9GCPMAwY3hmE/IjTtlG0v+bXB8BQ 9 | 3S3caQgio5VO3A1CqUfsXhpKLRqaNM/s2+pIG+oZdRV5gIJVGnK1o3yj7qxxG/F0 10 | 3Q+3OWXwDZIn0eTFh2M9YkxygA/KtkREZWv8Q8qZpdOpJSBYZyGE97Jqy/yGc+DQ 11 | Vpoa9B8WwnIdUn47TkZfsbzqGIYZxatJQDC1j7Y+F8So7zBbUhpz7YqATQwf5Efm 12 | K2xwvlwfdwykq6ffEr2M/Xna0220G2JZlGq3Cs2X9GT9Pt9OS86Bz+EL46ELo0tZ 13 | yfHQe/kCgYEA+zh4k2be6fhQG+ChiG3Ue5K/kH2prqyGBus61wHnt8XZavqBevEy 14 | 4pdmvJ6Q1Ta9Z2YCIqqNmlTdjZ6B35lvAK8YFITGy0MVV6K5NFYVfhALWCQC2r3B 15 | 6uH39FQ0mDo3gS5ZjYlUzbu67LGFnyX+pyMr2oxlhI1fCY3VchXQAOsCgYEA88Nt 16 | CwSOaZ1fWmyNAgXEAX1Jx4XLFYgjcA/YBXW9gfQ0AfufB346y53PsgjX1lB+Bbcg 17 | cY/o5W7F0b3A0R4K5LShlPCq8iB2DC+VnpKwTgo8ylh+VZCPy2BmMK0jrrmyqWeg 18 | PzwgP0lp+7l/qW8LDImeYi8nWoqd6f1ye4iJdD0CgYEAlIApJljk5EFYeWIrmk3y 19 | EKoKewsNRqfNAkICoh4KL2PQxaAW8emqPq9ol47T5nVZOMnf8UYINnZ8EL7l3psA 20 | NtNJ1Lc4G+cnsooKGJnaUo6BZjTDSzJocsPoopE0Fdgz/zS60yOe8Y5LTKcTaaQ4 21 | B+yOe74KNHSs/STOS4YBUskCgYAIqaRBZPsOo8oUs5DbRostpl8t2QJblIf13opF 22 | v2ZprN0ASQngwUqjm8sav5e0BQ5Fc7mSb5POO36KMp0ckV2/vO+VFGxuyFqJmlNN 23 | 3Fapn1GDu1tZ/RYvGxDmn/CJsA26WXVnaeKXfStoB7KSueCBpI5dXOGgJRbxjtE3 24 | tKV13QKBgQCtmLtTJPJ0Z+9n85C8kBonk2MCnD9JTYWoDQzNMYGabthzSqJqcEek 25 | dvhr82XkcHM+r6+cirjdQr4Qj7/2bfZesHl5XLvoJDB1YJIXnNJOELwbktrJrXLc 26 | dJ+MMvPvBAMah/tqr2DqgTGfWLDt9PJiCJVsuN2kD9toWHV08pY0Og== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /t/cplusplus.t: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | 7 | plan skip_all => "c++ not found" 8 | unless system("which c++ > /dev/null 2>&1") == 0; 9 | 10 | is system(qw(c++ -Iinclude -Ideps/picotls/include --include quicly.h -c -x c++ -Wall /dev/null)), 0; 11 | 12 | done_testing; 13 | -------------------------------------------------------------------------------- /t/frame.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/frame.h" 23 | #include "test.h" 24 | 25 | static void test_ack_decode_underflow(void) 26 | { 27 | quicly_ack_frame_t decoded; 28 | 29 | { /* ack pn=0 */ 30 | const uint8_t pat[] = {0, 0, 0, 0}, *src = pat; 31 | ok(quicly_decode_ack_frame(&src, pat + sizeof(pat), &decoded, 0) == 0); 32 | ok(src == pat + sizeof(pat)); 33 | ok(decoded.largest_acknowledged == 0); 34 | ok(decoded.num_gaps == 0); 35 | ok(decoded.ack_block_lengths[0] == 1); 36 | ok(decoded.smallest_acknowledged == 0); 37 | } 38 | { /* underflow in first block length */ 39 | const uint8_t pat[] = {0, 0, 0, 1}, *src = pat; 40 | ok(quicly_decode_ack_frame(&src, pat + sizeof(pat), &decoded, 0) != 0); 41 | } 42 | 43 | { /* frame with gap going down to pn=0 */ 44 | const uint8_t pat[] = {2, 0, 1, 0, 0, 0}, *src = pat; 45 | ok(quicly_decode_ack_frame(&src, pat + sizeof(pat), &decoded, 0) == 0); 46 | ok(src == pat + sizeof(pat)); 47 | ok(decoded.largest_acknowledged == 2); 48 | ok(decoded.num_gaps == 1); 49 | ok(decoded.ack_block_lengths[0] == 1); 50 | ok(decoded.ack_block_lengths[1] == 1); 51 | ok(decoded.smallest_acknowledged == 0); 52 | } 53 | 54 | { /* additional block length going negative */ 55 | const uint8_t pat[] = {2, 0, 1, 0, 0, 1}, *src = pat; 56 | ok(quicly_decode_ack_frame(&src, pat + sizeof(pat), &decoded, 0) != 0); 57 | } 58 | { /* gap going negative */ 59 | const uint8_t pat[] = {2, 0, 1, 0, 3, 0}, *src = pat; 60 | ok(quicly_decode_ack_frame(&src, pat + sizeof(pat), &decoded, 0) != 0); 61 | } 62 | } 63 | 64 | static void test_ack_decode(void) 65 | { 66 | { 67 | const uint8_t pat[] = {0x34, 0x00, 0x00, 0x11}, *src = pat; 68 | quicly_ack_frame_t decoded; 69 | ok(quicly_decode_ack_frame(&src, pat + sizeof(pat), &decoded, 0) == 0); 70 | ok(src == pat + sizeof(pat)); 71 | ok(decoded.largest_acknowledged == 0x34); 72 | ok(decoded.num_gaps == 0); 73 | ok(decoded.ack_block_lengths[0] == 0x12); 74 | ok(decoded.smallest_acknowledged == 0x34 - 0x12 + 1); 75 | } 76 | 77 | { 78 | const uint8_t pat[] = {0x34, 0x00, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04}, *src = pat; 79 | quicly_ack_frame_t decoded; 80 | ok(quicly_decode_ack_frame(&src, pat + sizeof(pat), &decoded, 0) == 0); 81 | ok(src == pat + sizeof(pat)); 82 | ok(decoded.largest_acknowledged == 0x34); 83 | ok(decoded.num_gaps == 2); 84 | ok(decoded.ack_block_lengths[0] == 1); 85 | ok(decoded.gaps[0] == 2); 86 | ok(decoded.ack_block_lengths[1] == 3); 87 | ok(decoded.gaps[1] == 4); 88 | ok(decoded.ack_block_lengths[2] == 5); 89 | ok(decoded.smallest_acknowledged == 0x34 - 1 - 2 - 3 - 4 - 5 + 1); 90 | } 91 | 92 | { /* Bogus ACK Frame larger than the internal buffer */ 93 | uint8_t pat[1024], *end = pat; 94 | const uint8_t *src = pat; 95 | uint64_t i, range_sum; 96 | quicly_ack_frame_t decoded; 97 | end = quicly_encodev(end, 0xFA00); 98 | end = quicly_encodev(end, 0); 99 | end = quicly_encodev(end, QUICLY_ACK_MAX_GAPS + 30); // with excess ranges 100 | end = quicly_encodev(end, 8); 101 | for (i = 0; i < QUICLY_ACK_MAX_GAPS + 30; ++i) { 102 | end = quicly_encodev(end, i); // gap 103 | end = quicly_encodev(end, i % 10); // ack-range 104 | } 105 | 106 | ok(quicly_decode_ack_frame(&src, end, &decoded, 0) == 0); 107 | ok(decoded.largest_acknowledged == 0xFA00); 108 | ok(decoded.ack_delay == 0); 109 | ok(decoded.num_gaps == QUICLY_ACK_MAX_GAPS); 110 | ok(decoded.ack_block_lengths[0] == 8 + 1); // first ack-range 111 | range_sum = decoded.ack_block_lengths[0]; 112 | for (i = 0; i < decoded.num_gaps; ++i) { 113 | ok(decoded.gaps[i] == i + 1); 114 | ok(decoded.ack_block_lengths[i + 1] == (i % 10) + 1); 115 | range_sum += decoded.gaps[i] + decoded.ack_block_lengths[i + 1]; 116 | } 117 | ok(src == end); // decoded the entire frame 118 | ok(decoded.smallest_acknowledged == 0xFA00 - range_sum + 1); 119 | } 120 | 121 | subtest("underflow", test_ack_decode_underflow); 122 | } 123 | 124 | static void test_ack_encode(void) 125 | { 126 | quicly_ranges_t ranges; 127 | uint8_t buf[256], *end; 128 | const uint8_t *src; 129 | quicly_ack_frame_t decoded; 130 | uint64_t ecn_counts[3] = {}; 131 | 132 | quicly_ranges_init(&ranges); 133 | quicly_ranges_add(&ranges, 0x12, 0x14); 134 | 135 | /* encode */ 136 | end = quicly_encode_ack_frame(buf, buf + sizeof(buf), &ranges, ecn_counts, 63); 137 | ok(end - buf == 5); 138 | /* decode */ 139 | src = buf + 1; 140 | ok(quicly_decode_ack_frame(&src, end, &decoded, 0) == 0); 141 | ok(src == end); 142 | ok(decoded.ack_delay == 63); 143 | ok(decoded.num_gaps == 0); 144 | ok(decoded.largest_acknowledged == 0x13); 145 | ok(decoded.ack_block_lengths[0] == 2); 146 | ok(decoded.ecn_counts[0] == 0); 147 | ok(decoded.ecn_counts[1] == 0); 148 | ok(decoded.ecn_counts[2] == 0); 149 | 150 | quicly_ranges_add(&ranges, 0x10, 0x11); 151 | ecn_counts[0] = 12; 152 | ecn_counts[1] = 34; 153 | ecn_counts[2] = 56; 154 | 155 | /* encode */ 156 | end = quicly_encode_ack_frame(buf, buf + sizeof(buf), &ranges, ecn_counts, 63); 157 | ok(end - buf == 10); 158 | /* decode */ 159 | src = buf + 1; 160 | ok(quicly_decode_ack_frame(&src, end, &decoded, 1) == 0); 161 | ok(src == end); 162 | ok(decoded.ack_delay == 63); 163 | ok(decoded.num_gaps == 1); 164 | ok(decoded.largest_acknowledged == 0x13); 165 | ok(decoded.ack_block_lengths[0] == 2); 166 | ok(decoded.gaps[0] == 1); 167 | ok(decoded.ack_block_lengths[1] == 1); 168 | ok(decoded.ecn_counts[0] == 12); 169 | ok(decoded.ecn_counts[1] == 34); 170 | ok(decoded.ecn_counts[2] == 56); 171 | 172 | quicly_ranges_clear(&ranges); 173 | } 174 | 175 | static void test_mozquic(void) 176 | { 177 | quicly_stream_frame_t frame; 178 | static const char *mess = "\xc5\0\0\0\0\0\0\xb6\x16\x03"; 179 | const uint8_t *p = (void *)mess, type_flags = *p++; 180 | quicly_decode_stream_frame(type_flags, &p, p + 9, &frame); 181 | } 182 | 183 | void test_frame(void) 184 | { 185 | subtest("ack-decode", test_ack_decode); 186 | subtest("ack-encode", test_ack_encode); 187 | subtest("mozquic", test_mozquic); 188 | } 189 | -------------------------------------------------------------------------------- /t/jumpstart.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017-2024 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "test.h" 23 | 24 | struct test_jumpstart_action { 25 | enum { TEST_JUMPSTART_ACTION_END, TEST_JUMPSTART_ACTION_SEND, TEST_JUMPSTART_ACTION_ACKED, TEST_JUMPSTART_ACTION_LOST } action; 26 | int64_t now; 27 | uint32_t packets; 28 | }; 29 | 30 | static void test_jumpstart_pattern(quicly_init_cc_t *init, const struct test_jumpstart_action *actions, uint32_t final_cwnd) 31 | { 32 | static const uint32_t mtu = 1200; 33 | quicly_loss_t loss = {.rtt = {.latest = 100, .smoothed = 100, .minimum = 100, .variance = 0}}; 34 | quicly_cc_t cc; 35 | int64_t now = 1; 36 | uint64_t next_pn = 0; 37 | uint32_t packets_acked = 0, packets_inflight = 0; 38 | size_t ackcnt = 0; 39 | 40 | init->cb(init, &cc, 10 * mtu, now); 41 | ok(cc.cwnd == 10 * mtu); 42 | ok(cc.num_loss_episodes == 0); 43 | 44 | for (const struct test_jumpstart_action *action = actions; action->action != TEST_JUMPSTART_ACTION_END; ++action) { 45 | switch (action->action) { 46 | case TEST_JUMPSTART_ACTION_SEND: 47 | cc.type->cc_on_sent(&cc, &loss, action->packets * mtu, action->now); 48 | packets_inflight += action->packets; 49 | next_pn += action->packets; 50 | break; 51 | case TEST_JUMPSTART_ACTION_ACKED: 52 | cc.type->cc_on_acked(&cc, &loss, action->packets * mtu, packets_acked + action->packets - 1, packets_inflight * mtu, 1, 53 | next_pn, action->now, mtu); 54 | packets_inflight -= action->packets; 55 | packets_acked += action->packets; 56 | ++ackcnt; 57 | /* enter jumpstart upon receiving the first ack */ 58 | if (ackcnt == 1 && cc.num_loss_episodes == 0) { 59 | cc.type->cc_jumpstart(&cc, 20 * mtu, next_pn); 60 | ok(cc.cwnd == 20 * mtu); 61 | } 62 | break; 63 | case TEST_JUMPSTART_ACTION_LOST: 64 | cc.type->cc_on_lost(&cc, &loss, action->packets * mtu, packets_acked + action->packets - 1, next_pn, action->now, mtu); 65 | packets_inflight -= action->packets; 66 | packets_acked += action->packets; 67 | ok(!quicly_cc_in_jumpstart(&cc)); 68 | ok(cc.ssthresh < UINT32_MAX); 69 | break; 70 | default: 71 | assert(!"FIXME"); 72 | } 73 | } 74 | 75 | ok(!quicly_cc_in_jumpstart(&cc)); 76 | ok(cc.cwnd == final_cwnd * mtu); 77 | } 78 | 79 | static void do_test_jumpstart(quicly_init_cc_t *init) 80 | { 81 | /* if all packets sent in the unvalidated phase are acked, final CWND is 2x jumpstart cwnd */ 82 | subtest("simple", test_jumpstart_pattern, init, 83 | (struct test_jumpstart_action[]){ 84 | {TEST_JUMPSTART_ACTION_SEND, 1000, 2}, /* send 2 packets */ 85 | {TEST_JUMPSTART_ACTION_ACKED, 1100, 2}, /* 2 packet acked, entering jumpstart */ 86 | {TEST_JUMPSTART_ACTION_SEND, 1100, 20}, /* use full jumpstart window */ 87 | {TEST_JUMPSTART_ACTION_ACKED, 1200, 20}, /* receive acks for all packets send in jumpstart */ 88 | {TEST_JUMPSTART_ACTION_END}, 89 | }, 90 | 40); 91 | 92 | /* if a packet is lost in the reconnaisance phase, jumpstart is not entered */ 93 | subtest("loss-inreconnaisance", test_jumpstart_pattern, init, 94 | (struct test_jumpstart_action[]){ 95 | {TEST_JUMPSTART_ACTION_SEND, 1000, 2}, /* send 2 packets */ 96 | {TEST_JUMPSTART_ACTION_LOST, 1100, 1}, /* 1st packet lost */ 97 | {TEST_JUMPSTART_ACTION_ACKED, 1100, 1}, /* 2nd packet acked */ 98 | {TEST_JUMPSTART_ACTION_END}, 99 | }, 100 | 5); 101 | 102 | /* if 25% of packets sent in the unvalidated phase are lost, final CWND is 75% the jumpstart cwnd */ 103 | subtest("proportional rate reduction", test_jumpstart_pattern, init, 104 | (struct test_jumpstart_action[]){ 105 | {TEST_JUMPSTART_ACTION_SEND, 1000, 2}, /* send 2 packets */ 106 | {TEST_JUMPSTART_ACTION_ACKED, 1100, 2}, /* 2 packet acked, entering jumpstart */ 107 | {TEST_JUMPSTART_ACTION_SEND, 1100, 20}, /* use full jumpstart window */ 108 | {TEST_JUMPSTART_ACTION_ACKED, 1200, 8}, 109 | {TEST_JUMPSTART_ACTION_LOST, 1200, 2}, 110 | {TEST_JUMPSTART_ACTION_ACKED, 1200, 7}, 111 | {TEST_JUMPSTART_ACTION_LOST, 1200, 3}, 112 | {TEST_JUMPSTART_ACTION_END}, 113 | }, 114 | 15); 115 | 116 | /* regardless of how much we lose, we never go down below 1/2 IW */ 117 | subtest("lower bound", test_jumpstart_pattern, init, 118 | (struct test_jumpstart_action[]){ 119 | {TEST_JUMPSTART_ACTION_SEND, 1000, 2}, /* send 2 packets */ 120 | {TEST_JUMPSTART_ACTION_ACKED, 1100, 2}, /* 2 packet acked, entering jumpstart */ 121 | {TEST_JUMPSTART_ACTION_SEND, 1100, 20}, /* use full jumpstart window */ 122 | {TEST_JUMPSTART_ACTION_ACKED, 1200, 1}, 123 | {TEST_JUMPSTART_ACTION_LOST, 1200, 9}, 124 | {TEST_JUMPSTART_ACTION_ACKED, 1200, 2}, 125 | {TEST_JUMPSTART_ACTION_LOST, 1200, 8}, 126 | {TEST_JUMPSTART_ACTION_END}, 127 | }, 128 | 5); 129 | 130 | /* When receiving ACK early in the reconnaisance phase before sending entire batch, CWND doubles per each RT from bytes_inflight 131 | * (of 10 packets, in the test case below). */ 132 | subtest("simple", test_jumpstart_pattern, init, 133 | (struct test_jumpstart_action[]){ 134 | {TEST_JUMPSTART_ACTION_SEND, 1000, 2}, /* send 2 packets */ 135 | {TEST_JUMPSTART_ACTION_ACKED, 1100, 2}, /* 2 packet acked, entering jumpstart */ 136 | {TEST_JUMPSTART_ACTION_SEND, 1100, 10}, /* use full jumpstart window */ 137 | {TEST_JUMPSTART_ACTION_ACKED, 1200, 2}, /* receive acks for all packets send in jumpstart */ 138 | {TEST_JUMPSTART_ACTION_SEND, 1200, 4}, /* use full jumpstart window */ 139 | {TEST_JUMPSTART_ACTION_ACKED, 1200, 8}, /* receive acks for all packets send in jumpstart */ 140 | {TEST_JUMPSTART_ACTION_END}, 141 | }, 142 | 20); 143 | } 144 | 145 | void test_jumpstart(void) 146 | { 147 | subtest("reno", do_test_jumpstart, &quicly_cc_reno_init); 148 | subtest("pico", do_test_jumpstart, &quicly_cc_pico_init); 149 | subtest("cubic", do_test_jumpstart, &quicly_cc_cubic_init); 150 | } 151 | -------------------------------------------------------------------------------- /t/maxsender.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/maxsender.h" 23 | #include "test.h" 24 | 25 | static void test_basic(void) 26 | { 27 | quicly_maxsender_t m; 28 | quicly_maxsender_sent_t ackargs; 29 | 30 | quicly_maxsender_init(&m, 100); 31 | 32 | /* basic checks */ 33 | ok(!quicly_maxsender_should_send_max(&m, 0, 100, 512)); 34 | ok(quicly_maxsender_should_send_max(&m, 0, 100, 1024)); 35 | ok(!quicly_maxsender_should_send_max(&m, 99, 100, 0)); 36 | ok(quicly_maxsender_should_send_max(&m, 100, 100, 0)); 37 | 38 | /* scenario */ 39 | ok(!quicly_maxsender_should_send_max(&m, 24, 100, 768)); 40 | ok(quicly_maxsender_should_send_max(&m, 25, 100, 768)); 41 | quicly_maxsender_record(&m, 125, &ackargs); 42 | ok(!quicly_maxsender_should_send_max(&m, 49, 100, 768)); 43 | ok(quicly_maxsender_should_send_max(&m, 50, 100, 768)); 44 | quicly_maxsender_acked(&m, &ackargs); 45 | ok(!quicly_maxsender_should_send_max(&m, 49, 100, 768)); 46 | ok(quicly_maxsender_should_send_max(&m, 50, 100, 768)); 47 | quicly_maxsender_record(&m, 150, &ackargs); 48 | ok(!quicly_maxsender_should_send_max(&m, 74, 100, 768)); 49 | quicly_maxsender_lost(&m, &ackargs); 50 | ok(quicly_maxsender_should_send_max(&m, 74, 100, 768)); 51 | } 52 | 53 | void test_maxsender(void) 54 | { 55 | subtest("basic", test_basic); 56 | } 57 | -------------------------------------------------------------------------------- /t/pacer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/pacer.h" 23 | #include "test.h" 24 | 25 | static void test_calc_rate(void) 26 | { 27 | uint64_t bytes_per_msec; 28 | 29 | bytes_per_msec = quicly_pacer_calc_send_rate(2, 50 * 1200, 10); 30 | ok(bytes_per_msec == 12000); /* send 60KB packets in 5ms */ 31 | 32 | bytes_per_msec = quicly_pacer_calc_send_rate(2, 100 * 1200, 10); 33 | ok(bytes_per_msec == 24000); /* 2x CWND, 2x flow rate */ 34 | 35 | bytes_per_msec = quicly_pacer_calc_send_rate(2, 50 * 1200, 100); 36 | ok(bytes_per_msec == 1200); /* 10x RTT, 1/10 flow rate */ 37 | 38 | /* half the rate as above, due to multiplier being 1x */ 39 | bytes_per_msec = quicly_pacer_calc_send_rate(1, 50 * 1200, 100); 40 | ok(bytes_per_msec == 600); 41 | } 42 | 43 | static const uint16_t mtu = 1200; 44 | 45 | struct pattern { 46 | int64_t at; 47 | size_t avail; 48 | size_t consume; 49 | }; 50 | 51 | static int64_t test_pattern(quicly_pacer_t *pacer, int64_t now, const uint32_t bytes_per_msec, const struct pattern *expected) 52 | { 53 | for (; expected->at != 0; ++expected) { 54 | int64_t send_at = quicly_pacer_can_send_at(pacer, bytes_per_msec, mtu); 55 | if (now == expected->at) { 56 | ok(send_at <= now); 57 | } else { 58 | ok(send_at == expected->at); 59 | now = send_at; 60 | } 61 | size_t window = quicly_pacer_get_window(pacer, now, bytes_per_msec, mtu); 62 | ok((window + mtu - 1) / mtu * mtu == expected->avail); 63 | quicly_pacer_consume_window(pacer, expected->consume); 64 | } 65 | 66 | return now; 67 | } 68 | 69 | static void test_medium(void) 70 | { 71 | const uint32_t bytes_per_msec = 4 * mtu; 72 | quicly_pacer_t pacer; 73 | int64_t now = 1; 74 | 75 | quicly_pacer_reset(&pacer); 76 | 77 | /* 3x pacer-restricted, then non-pacer-restricted */ 78 | now = test_pattern(&pacer, now, bytes_per_msec, 79 | (const struct pattern[]){ 80 | {1, 10 * mtu, 10 * mtu}, 81 | {2, 4 * mtu, 4 * mtu}, 82 | {3, 4 * mtu, 4 * mtu}, 83 | {4, 4 * mtu, 1 * mtu}, 84 | {0}, 85 | }); 86 | ok(now == 4); 87 | 88 | /* in the next millisecond, we have new data to send, and we borrow 3mtu from the previous millisec */ 89 | now = 5; 90 | now = test_pattern(&pacer, now, bytes_per_msec, 91 | (const struct pattern[]){ 92 | {5, 7 * mtu, 7 * mtu}, 93 | {6, 4 * mtu, 1 * mtu}, 94 | {0}, 95 | }); 96 | ok(now == 6); 97 | 98 | /* skip 2ms, and we can send a burst */ 99 | now = 8; 100 | now = test_pattern(&pacer, now, bytes_per_msec, 101 | (const struct pattern[]){ 102 | {8, 10 * mtu, 10 * mtu}, 103 | {9, 4 * mtu, 1 * mtu}, 104 | {0}, 105 | }); 106 | ok(now == 9); 107 | } 108 | 109 | static void test_slow(void) 110 | { 111 | const uint32_t bytes_per_msec = 700; 112 | quicly_pacer_t pacer; 113 | int64_t now = 1; 114 | 115 | quicly_pacer_reset(&pacer); 116 | 117 | now = test_pattern(&pacer, now, bytes_per_msec, 118 | (const struct pattern[]){ 119 | {1, 10 * mtu, 10 * mtu}, /* borrow 12000 bytes */ 120 | {5, 2 * mtu, 2 * mtu}, /* borrowing 11600 bytes after 4ms */ 121 | {8, 2 * mtu, 2 * mtu}, /* borrowing 11900 bytes after 3ms */ 122 | {12, 2 * mtu, 2 * mtu}, /* borrowing 11500 bytes after 4ms */ 123 | {15, 2 * mtu, 2 * mtu}, /* borrowing 11800 bytes after 3ms */ 124 | {19, 2 * mtu, 2 * mtu}, /* borrowing 11400 bytes after 4ms */ 125 | {22, 2 * mtu, 2 * mtu}, /* borrowing 11700 bytes after 3ms */ 126 | {25, 2 * mtu, 2 * mtu}, /* borrowing 12000 bytes after 3ms */ 127 | {29, 2 * mtu, 2 * mtu}, /* borrowing 11600 bytes after 4ms */ 128 | {0}, 129 | }); 130 | } 131 | 132 | static void test_fast(void) 133 | { 134 | const uint32_t bytes_per_msec = 100000; /* 83.3333 packets per msec */ 135 | quicly_pacer_t pacer; 136 | int64_t now = 1; 137 | 138 | quicly_pacer_reset(&pacer); 139 | 140 | now = test_pattern(&pacer, now, bytes_per_msec, 141 | (const struct pattern[]){ 142 | {1, 84 * mtu, 84 * mtu}, /* borrow 800 bytes */ 143 | {2, 83 * mtu, 83 * mtu}, /* borrowing 400 bytes */ 144 | {3, 83 * mtu, 83 * mtu}, /* borrowing 0 bytes */ 145 | {4, 84 * mtu, 84 * mtu}, /* borrowing 800 bytes */ 146 | {0}, 147 | }); 148 | } 149 | 150 | void test_pacer(void) 151 | { 152 | subtest("calc-rate", test_calc_rate); 153 | subtest("medium", test_medium); 154 | subtest("slow", test_slow); 155 | subtest("fast", test_fast); 156 | } 157 | -------------------------------------------------------------------------------- /t/ranges.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/ranges.h" 23 | #include "test.h" 24 | 25 | #define CHECK(...) \ 26 | do { \ 27 | static const struct st_quicly_range_t expected[] = {__VA_ARGS__}; \ 28 | ok(ranges.num_ranges == PTLS_ELEMENTSOF(expected)); \ 29 | size_t i; \ 30 | for (i = 0; i != ranges.num_ranges; ++i) { \ 31 | ok(ranges.ranges[i].start == expected[i].start); \ 32 | ok(ranges.ranges[i].end == expected[i].end); \ 33 | } \ 34 | } while (0) 35 | 36 | static void test_add(void) 37 | { 38 | quicly_ranges_t ranges; 39 | int ret; 40 | 41 | quicly_ranges_init(&ranges); 42 | ok(ranges.num_ranges == 0); 43 | 44 | ret = quicly_ranges_add(&ranges, 40, 100); 45 | ok(ret == 0); 46 | CHECK({40, 100}); 47 | 48 | ret = quicly_ranges_add(&ranges, 30, 40); 49 | ok(ret == 0); 50 | CHECK({30, 100}); 51 | 52 | ret = quicly_ranges_add(&ranges, 0, 10); 53 | ok(ret == 0); 54 | CHECK({0, 10}, {30, 100}); 55 | 56 | ret = quicly_ranges_add(&ranges, 10, 30); 57 | ok(ret == 0); 58 | CHECK({0, 100}); 59 | 60 | ret = quicly_ranges_add(&ranges, 200, 300); 61 | ok(ret == 0); 62 | CHECK({0, 100}, {200, 300}); 63 | 64 | ret = quicly_ranges_add(&ranges, 100, 110); 65 | ok(ret == 0); 66 | CHECK({0, 110}, {200, 300}); 67 | 68 | ret = quicly_ranges_add(&ranges, 190, 200); 69 | ok(ret == 0); 70 | CHECK({0, 110}, {190, 300}); 71 | 72 | ret = quicly_ranges_add(&ranges, 100, 120); 73 | ok(ret == 0); 74 | CHECK({0, 120}, {190, 300}); 75 | 76 | ret = quicly_ranges_add(&ranges, 180, 200); 77 | ok(ret == 0); 78 | CHECK({0, 120}, {180, 300}); 79 | 80 | ret = quicly_ranges_add(&ranges, 130, 150); 81 | ok(ret == 0); 82 | CHECK({0, 120}, {130, 150}, {180, 300}); 83 | 84 | ret = quicly_ranges_add(&ranges, 160, 170); 85 | ok(ret == 0); 86 | CHECK({0, 120}, {130, 150}, {160, 170}, {180, 300}); 87 | 88 | ret = quicly_ranges_add(&ranges, 170, 180); 89 | ok(ret == 0); 90 | CHECK({0, 120}, {130, 150}, {160, 300}); 91 | 92 | ret = quicly_ranges_add(&ranges, 110, 180); 93 | ok(ret == 0); 94 | CHECK({0, 300}); 95 | } 96 | 97 | static void test_subtract(void) 98 | { 99 | quicly_ranges_t ranges; 100 | int ret; 101 | 102 | quicly_ranges_init(&ranges); 103 | 104 | ret = quicly_ranges_add(&ranges, 100, 200); 105 | ok(ret == 0); 106 | 107 | ret = quicly_ranges_subtract(&ranges, 0, 50); 108 | ok(ret == 0); 109 | CHECK({100, 200}); 110 | 111 | ret = quicly_ranges_subtract(&ranges, 0, 100); 112 | ok(ret == 0); 113 | CHECK({100, 200}); 114 | 115 | ret = quicly_ranges_subtract(&ranges, 250, 300); 116 | ok(ret == 0); 117 | CHECK({100, 200}); 118 | 119 | ret = quicly_ranges_subtract(&ranges, 200, 300); 120 | ok(ret == 0); 121 | CHECK({100, 200}); 122 | 123 | ret = quicly_ranges_subtract(&ranges, 100, 110); 124 | ok(ret == 0); 125 | CHECK({110, 200}); 126 | 127 | ret = quicly_ranges_subtract(&ranges, 120, 130); 128 | ok(ret == 0); 129 | CHECK({110, 120}, {130, 200}); 130 | 131 | ret = quicly_ranges_subtract(&ranges, 120, 130); 132 | ok(ret == 0); 133 | CHECK({110, 120}, {130, 200}); 134 | 135 | ret = quicly_ranges_subtract(&ranges, 121, 129); 136 | ok(ret == 0); 137 | CHECK({110, 120}, {130, 200}); 138 | 139 | ret = quicly_ranges_subtract(&ranges, 119, 120); 140 | ok(ret == 0); 141 | CHECK({110, 119}, {130, 200}); 142 | 143 | ret = quicly_ranges_subtract(&ranges, 118, 120); 144 | ok(ret == 0); 145 | CHECK({110, 118}, {130, 200}); 146 | 147 | ret = quicly_ranges_subtract(&ranges, 130, 132); 148 | ok(ret == 0); 149 | CHECK({110, 118}, {132, 200}); 150 | 151 | ret = quicly_ranges_subtract(&ranges, 130, 133); 152 | ok(ret == 0); 153 | CHECK({110, 118}, {133, 200}); 154 | 155 | ret = quicly_ranges_subtract(&ranges, 118, 134); 156 | ok(ret == 0); 157 | CHECK({110, 118}, {134, 200}); 158 | 159 | ret = quicly_ranges_subtract(&ranges, 117, 134); 160 | ok(ret == 0); 161 | CHECK({110, 117}, {134, 200}); 162 | 163 | ret = quicly_ranges_subtract(&ranges, 116, 135); 164 | ok(ret == 0); 165 | CHECK({110, 116}, {135, 200}); 166 | 167 | ret = quicly_ranges_subtract(&ranges, 110, 140); 168 | ok(ret == 0); 169 | CHECK({140, 200}); 170 | 171 | ret = quicly_ranges_add(&ranges, 100, 120); 172 | ok(ret == 0); 173 | CHECK({100, 120}, {140, 200}); 174 | 175 | ret = quicly_ranges_subtract(&ranges, 80, 150); 176 | ok(ret == 0); 177 | CHECK({150, 200}); 178 | 179 | ret = quicly_ranges_add(&ranges, 100, 120); 180 | ok(ret == 0); 181 | CHECK({100, 120}, {150, 200}); 182 | 183 | ret = quicly_ranges_subtract(&ranges, 118, 200); 184 | ok(ret == 0); 185 | CHECK({100, 118}); 186 | 187 | ret = quicly_ranges_add(&ranges, 140, 200); 188 | ok(ret == 0); 189 | CHECK({100, 118}, {140, 200}); 190 | 191 | ret = quicly_ranges_subtract(&ranges, 118, 240); 192 | ok(ret == 0); 193 | CHECK({100, 118}); 194 | 195 | ret = quicly_ranges_add(&ranges, 140, 200); 196 | ok(ret == 0); 197 | CHECK({100, 118}, {140, 200}); 198 | 199 | ret = quicly_ranges_subtract(&ranges, 117, 240); 200 | ok(ret == 0); 201 | CHECK({100, 117}); 202 | } 203 | 204 | void test_ranges(void) 205 | { 206 | subtest("add", test_add); 207 | subtest("subtract", test_subtract); 208 | } 209 | -------------------------------------------------------------------------------- /t/rate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "quicly/rate.h" 24 | #include "test.h" 25 | 26 | #define CHECK_REPORT(el, es, ev) \ 27 | do { \ 28 | quicly_rate_t rate; \ 29 | quicly_ratemeter_report(&meter, &rate); \ 30 | ok(rate.latest == el); \ 31 | ok(rate.smoothed == es); \ 32 | ok(rate.stdev == ev); \ 33 | } while (0) 34 | 35 | static void test_basic(void) 36 | { 37 | quicly_ratemeter_t meter; 38 | 39 | quicly_ratemeter_init(&meter); 40 | CHECK_REPORT(0, 0, 0); 41 | 42 | uint64_t pn = 0, bytes_acked = 0; 43 | int64_t now = 1000; 44 | 45 | /* send 1KB packet every 20ms, in CWND-limited state */ 46 | quicly_ratemeter_enter_cc_limited(&meter, pn); 47 | for (; pn < 100; ++pn) { 48 | bytes_acked += 1000; 49 | now += 20; 50 | quicly_ratemeter_on_ack(&meter, now, bytes_acked, pn); 51 | } 52 | CHECK_REPORT(50000, 50000, 0); 53 | 54 | /* send at a slow rate, in application-limited state */ 55 | quicly_ratemeter_exit_cc_limited(&meter, pn); 56 | for (; pn < 200; ++pn) { 57 | bytes_acked += 10; 58 | now += 20; 59 | quicly_ratemeter_on_ack(&meter, now, bytes_acked, pn); 60 | } 61 | CHECK_REPORT(50000, 50000, 0); 62 | 63 | /* send 2KB packet every 20ms, in CWND-limited state */ 64 | quicly_ratemeter_enter_cc_limited(&meter, pn); 65 | for (; pn < 300; ++pn) { 66 | bytes_acked += 2000; 67 | now += 20; 68 | quicly_ratemeter_on_ack(&meter, now, bytes_acked, pn); 69 | } 70 | CHECK_REPORT(100000, 100000, 0); 71 | } 72 | 73 | static void test_burst(void) 74 | { 75 | quicly_ratemeter_t meter; 76 | 77 | quicly_ratemeter_init(&meter); 78 | CHECK_REPORT(0, 0, 0); 79 | 80 | /* send 10 packet burst (pn=1 to 10) */ 81 | quicly_ratemeter_enter_cc_limited(&meter, 1); 82 | quicly_ratemeter_exit_cc_limited(&meter, 11); 83 | 84 | /* ack every 2 packets up to pn=9, every 20ms */ 85 | uint64_t pn = 0, bytes_acked = 0; 86 | int64_t now = 1000; 87 | while (1) { 88 | pn += 2; 89 | bytes_acked += 2000; 90 | now += 20; 91 | quicly_ratemeter_on_ack(&meter, now, bytes_acked, pn); 92 | if (pn == 10) 93 | break; 94 | } 95 | CHECK_REPORT(100000, 100000, 0); 96 | 97 | ok(meter.current.sample.elapsed != 0); /* we have an active sample ... */ 98 | 99 | pn += 1; 100 | bytes_acked += 50; 101 | now += 20; 102 | quicly_ratemeter_on_ack(&meter, now, bytes_acked, pn); 103 | 104 | ok(meter.current.sample.elapsed == 0); /* that gets committed by the next pn out of the window */ 105 | 106 | CHECK_REPORT(100000, 100000, 0); 107 | } 108 | 109 | void test_rate(void) 110 | { 111 | subtest("basic", test_basic); 112 | subtest("burst", test_burst); 113 | } 114 | -------------------------------------------------------------------------------- /t/retire_cid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Fastly, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | #include "test.h" 24 | #include "picotls.h" 25 | #include "quicly/retire_cid.h" 26 | 27 | /** 28 | * verifies that expected sequence numbers are in that order at the front of the array. 29 | * Returns zero on success. 30 | */ 31 | static int verify(const quicly_retire_cid_set_t *set, const uint64_t expected_seqs[], size_t num_seqs) 32 | { 33 | size_t i; 34 | 35 | if (set->_num_pending != num_seqs) 36 | return 1; 37 | 38 | for (i = 0; i < num_seqs && i < PTLS_ELEMENTSOF(set->sequences); i++) { 39 | if (set->sequences[i] != expected_seqs[i]) 40 | return 1; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | void test_retire_cid(void) 47 | { 48 | uint64_t sequence = 0; 49 | 50 | quicly_retire_cid_set_t set; 51 | quicly_retire_cid_init(&set); 52 | 53 | /* should be empty */ 54 | ok(verify(&set, NULL, 0) == 0); 55 | 56 | /* push one sequence number */ 57 | quicly_retire_cid_push(&set, sequence); 58 | { 59 | uint64_t seqs[] = {sequence}; 60 | ok(verify(&set, seqs, PTLS_ELEMENTSOF(seqs)) == 0); 61 | } 62 | 63 | sequence++; 64 | 65 | /* shift one -- back to empty */ 66 | quicly_retire_cid_shift(&set, 1); 67 | ok(verify(&set, NULL, 0) == 0); 68 | 69 | { 70 | /* make the array full */ 71 | uint64_t seqs[PTLS_ELEMENTSOF(set.sequences)]; 72 | for (size_t i = 0; i < PTLS_ELEMENTSOF(set.sequences); i++) { 73 | seqs[i] = sequence; 74 | quicly_retire_cid_push(&set, sequence); 75 | sequence++; 76 | } 77 | ok(verify(&set, seqs, PTLS_ELEMENTSOF(seqs)) == 0); 78 | /* make sure duplicated push is ignored */ 79 | quicly_retire_cid_push(&set, seqs[0]); 80 | ok(verify(&set, seqs, PTLS_ELEMENTSOF(seqs)) == 0); 81 | /* make sure push is ignored when the array is already full */ 82 | quicly_retire_cid_push(&set, sequence + 1); 83 | ok(verify(&set, seqs, PTLS_ELEMENTSOF(seqs)) == 0); 84 | /* zero shift from a full array */ 85 | quicly_retire_cid_shift(&set, 0); 86 | /* test partial removal */ 87 | size_t num_shift = PTLS_ELEMENTSOF(seqs) / 2; 88 | quicly_retire_cid_shift(&set, num_shift); 89 | ok(verify(&set, seqs + num_shift, PTLS_ELEMENTSOF(seqs) - num_shift) == 0); 90 | /* test zero shift */ 91 | quicly_retire_cid_shift(&set, 0); 92 | ok(verify(&set, seqs + num_shift, PTLS_ELEMENTSOF(seqs) - num_shift) == 0); 93 | /* remove remaining sequence numbers */ 94 | quicly_retire_cid_shift(&set, PTLS_ELEMENTSOF(seqs) - num_shift); 95 | ok(verify(&set, NULL, 0) == 0); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /t/sentmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include "quicly/sentmap.h" 23 | #include "test.h" 24 | 25 | static int on_acked_callcnt, on_acked_ackcnt; 26 | 27 | static quicly_error_t on_acked(quicly_sentmap_t *map, const quicly_sent_packet_t *packet, int acked, quicly_sent_t *sent) 28 | { 29 | ++on_acked_callcnt; 30 | if (acked) 31 | ++on_acked_ackcnt; 32 | return 0; 33 | } 34 | 35 | static size_t num_blocks(quicly_sentmap_t *map) 36 | { 37 | struct st_quicly_sent_block_t *block; 38 | size_t n = 0; 39 | 40 | for (block = map->head; block != NULL; block = block->next) 41 | ++n; 42 | 43 | return n; 44 | } 45 | 46 | static void test_basic(void) 47 | { 48 | quicly_sentmap_t map; 49 | uint64_t at; 50 | size_t i; 51 | quicly_sentmap_iter_t iter; 52 | const quicly_sent_packet_t *sent; 53 | 54 | quicly_sentmap_init(&map); 55 | 56 | /* save 50 packets, with 2 frames each */ 57 | for (at = 0; at < 10; ++at) { 58 | for (i = 1; i <= 5; ++i) { 59 | quicly_sentmap_prepare(&map, at * 5 + i, at, QUICLY_EPOCH_INITIAL); 60 | quicly_sentmap_allocate(&map, on_acked); 61 | quicly_sentmap_allocate(&map, on_acked); 62 | quicly_sentmap_commit(&map, 1, 0, 0); 63 | } 64 | } 65 | 66 | /* check all acks */ 67 | quicly_sentmap_init_iter(&map, &iter); 68 | for (at = 0; at < 10; ++at) { 69 | for (i = 1; i <= 5; ++i) { 70 | const quicly_sent_packet_t *sent = quicly_sentmap_get(&iter); 71 | ok(sent->packet_number == at * 5 + i); 72 | ok(sent->sent_at == at); 73 | ok(sent->ack_epoch == QUICLY_EPOCH_INITIAL); 74 | ok(sent->cc_bytes_in_flight == 1); 75 | quicly_sentmap_skip(&iter); 76 | } 77 | } 78 | ok(quicly_sentmap_get(&iter)->packet_number == UINT64_MAX); 79 | ok(num_blocks(&map) == 150 / 16 + 1); 80 | 81 | /* pop acks between 11 <= packet_number <= 40 */ 82 | quicly_sentmap_init_iter(&map, &iter); 83 | while (quicly_sentmap_get(&iter)->packet_number <= 10) 84 | quicly_sentmap_skip(&iter); 85 | assert(quicly_sentmap_get(&iter)->packet_number == 11); 86 | while (quicly_sentmap_get(&iter)->packet_number <= 40) 87 | quicly_sentmap_update(&map, &iter, QUICLY_SENTMAP_EVENT_EXPIRED); 88 | ok(on_acked_callcnt == 30 * 2); 89 | ok(on_acked_ackcnt == 0); 90 | 91 | size_t cnt = 0; 92 | for (quicly_sentmap_init_iter(&map, &iter); (sent = quicly_sentmap_get(&iter))->packet_number != UINT64_MAX; 93 | quicly_sentmap_skip(&iter)) { 94 | ok(sent->cc_bytes_in_flight != 0); 95 | ok(sent->packet_number <= 10 || 40 < sent->packet_number); 96 | ++cnt; 97 | } 98 | ok(cnt == 20); 99 | ok(num_blocks(&map) == 30 / 16 + 1 + 1 + 30 / 16 + 1); 100 | 101 | quicly_sentmap_dispose(&map); 102 | } 103 | 104 | static void test_late_ack(void) 105 | { 106 | quicly_sentmap_t map; 107 | quicly_sentmap_iter_t iter; 108 | const quicly_sent_packet_t *sent; 109 | 110 | on_acked_callcnt = 0; 111 | on_acked_ackcnt = 0; 112 | 113 | quicly_sentmap_init(&map); 114 | 115 | /* commit pn 1, 2 */ 116 | quicly_sentmap_prepare(&map, 1, 0, QUICLY_EPOCH_INITIAL); 117 | quicly_sentmap_allocate(&map, on_acked); 118 | quicly_sentmap_commit(&map, 10, 0, 0); 119 | quicly_sentmap_prepare(&map, 2, 0, QUICLY_EPOCH_INITIAL); 120 | quicly_sentmap_allocate(&map, on_acked); 121 | quicly_sentmap_commit(&map, 20, 0, 0); 122 | ok(map.bytes_in_flight == 30); 123 | 124 | /* mark pn 1 as lost */ 125 | quicly_sentmap_init_iter(&map, &iter); 126 | sent = quicly_sentmap_get(&iter); 127 | assert(sent->packet_number == 1); 128 | ok(quicly_sentmap_update(&map, &iter, QUICLY_SENTMAP_EVENT_LOST) == 0); 129 | ok(on_acked_callcnt == 1); 130 | ok(on_acked_ackcnt == 0); 131 | ok(map.bytes_in_flight == 20); 132 | 133 | /* mark pn 1, 2 as acked */ 134 | quicly_sentmap_init_iter(&map, &iter); 135 | sent = quicly_sentmap_get(&iter); 136 | assert(sent->packet_number == 1); 137 | ok(quicly_sentmap_update(&map, &iter, QUICLY_SENTMAP_EVENT_ACKED) == 0); 138 | sent = quicly_sentmap_get(&iter); 139 | assert(sent->packet_number == 2); 140 | ok(quicly_sentmap_update(&map, &iter, QUICLY_SENTMAP_EVENT_ACKED) == 0); 141 | ok(on_acked_callcnt == 3); 142 | ok(on_acked_ackcnt == 2); 143 | ok(map.bytes_in_flight == 0); 144 | 145 | quicly_sentmap_dispose(&map); 146 | } 147 | 148 | static void test_pto(void) 149 | { 150 | quicly_sentmap_t map; 151 | quicly_sentmap_iter_t iter; 152 | const quicly_sent_packet_t *sent; 153 | 154 | on_acked_callcnt = 0; 155 | on_acked_ackcnt = 0; 156 | 157 | quicly_sentmap_init(&map); 158 | 159 | /* commit pn 1, 2 */ 160 | quicly_sentmap_prepare(&map, 1, 0, QUICLY_EPOCH_INITIAL); 161 | quicly_sentmap_allocate(&map, on_acked); 162 | quicly_sentmap_commit(&map, 10, 0, 0); 163 | quicly_sentmap_prepare(&map, 2, 0, QUICLY_EPOCH_INITIAL); 164 | quicly_sentmap_allocate(&map, on_acked); 165 | quicly_sentmap_commit(&map, 20, 0, 0); 166 | ok(map.bytes_in_flight == 30); 167 | 168 | /* mark pn 1 for PTO */ 169 | quicly_sentmap_init_iter(&map, &iter); 170 | sent = quicly_sentmap_get(&iter); 171 | assert(sent->packet_number == 1); 172 | ok(quicly_sentmap_update(&map, &iter, QUICLY_SENTMAP_EVENT_PTO) == 0); 173 | ok(on_acked_callcnt == 1); 174 | ok(on_acked_ackcnt == 0); 175 | ok(map.bytes_in_flight == 30); 176 | 177 | /* mark pn 1, 2 as acked */ 178 | quicly_sentmap_init_iter(&map, &iter); 179 | sent = quicly_sentmap_get(&iter); 180 | assert(sent->packet_number == 1); 181 | ok(quicly_sentmap_update(&map, &iter, QUICLY_SENTMAP_EVENT_ACKED) == 0); 182 | sent = quicly_sentmap_get(&iter); 183 | assert(sent->packet_number == 2); 184 | ok(quicly_sentmap_update(&map, &iter, QUICLY_SENTMAP_EVENT_ACKED) == 0); 185 | ok(on_acked_callcnt == 3); 186 | ok(on_acked_ackcnt == 2); 187 | ok(map.bytes_in_flight == 0); 188 | 189 | quicly_sentmap_dispose(&map); 190 | } 191 | 192 | void test_sentmap(void) 193 | { 194 | subtest("basic", test_basic); 195 | subtest("late-ack", test_late_ack); 196 | subtest("pto", test_pto); 197 | } 198 | -------------------------------------------------------------------------------- /t/stream-concurrency.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #include 23 | #include "quicly/streambuf.h" 24 | #include "test.h" 25 | 26 | void test_stream_concurrency(void) 27 | { 28 | quicly_conn_t *client, *server; 29 | size_t limit = quic_ctx.transport_params.max_streams_bidi; 30 | quicly_stream_t *client_streams[limit + 2], *server_stream; 31 | test_streambuf_t *client_streambufs[limit + 1], *server_streambuf; 32 | size_t i; 33 | quicly_error_t ret; 34 | 35 | { /* connect */ 36 | quicly_address_t dest, src; 37 | struct iovec raw; 38 | uint8_t rawbuf[quic_ctx.transport_params.max_udp_payload_size]; 39 | size_t num_packets; 40 | quicly_decoded_packet_t decoded; 41 | 42 | ret = quicly_connect(&client, &quic_ctx, "example.com", &fake_address.sa, NULL, new_master_id(), ptls_iovec_init(NULL, 0), 43 | NULL, NULL, NULL); 44 | ok(ret == 0); 45 | num_packets = 1; 46 | ret = quicly_send(client, &dest, &src, &raw, &num_packets, rawbuf, sizeof(rawbuf)); 47 | ok(ret == 0); 48 | ok(num_packets == 1); 49 | ok(decode_packets(&decoded, &raw, 1) == 1); 50 | ok(num_packets == 1); 51 | ret = quicly_accept(&server, &quic_ctx, NULL, &fake_address.sa, &decoded, NULL, new_master_id(), NULL, NULL); 52 | ok(ret == 0); 53 | transmit(server, client); 54 | } 55 | 56 | /* open as many streams as we can */ 57 | for (i = 0; i < limit + 1; ++i) { 58 | ret = quicly_open_stream(client, client_streams + i, 0); 59 | assert(ret == 0); 60 | client_streambufs[i] = client_streams[i]->data; 61 | if (client_streams[i]->streams_blocked) 62 | break; 63 | ret = quicly_streambuf_egress_write(client_streams[i], "hello", 5); 64 | assert(ret == 0); 65 | } 66 | ok(i == limit); 67 | 68 | transmit(client, server); 69 | transmit(server, client); 70 | 71 | /* the last stream is still ID-blocked after 1RT */ 72 | ok(client_streams[i]->streams_blocked); 73 | 74 | /* reset one stream in both directions and close on the client-side */ 75 | server_stream = quicly_get_stream(server, client_streams[i - 1]->stream_id); 76 | ok(server_stream != NULL); 77 | server_streambuf = server_stream->data; 78 | quicly_reset_stream(client_streams[i - 1], QUICLY_ERROR_FROM_APPLICATION_ERROR_CODE(123)); 79 | quicly_request_stop(client_streams[i - 1], QUICLY_ERROR_FROM_APPLICATION_ERROR_CODE(456)); 80 | transmit(client, server); 81 | quic_now += QUICLY_DELAYED_ACK_TIMEOUT; 82 | transmit(server, client); 83 | ok(server_streambuf->error_received.reset_stream == QUICLY_ERROR_FROM_APPLICATION_ERROR_CODE(123)); 84 | ok(server_streambuf->error_received.stop_sending == QUICLY_ERROR_FROM_APPLICATION_ERROR_CODE(456)); 85 | ok(!server_streambuf->is_detached); /* haven't gotten ACK for reset */ 86 | ok(client_streambufs[i - 1]->error_received.reset_stream == QUICLY_ERROR_FROM_APPLICATION_ERROR_CODE(456)); 87 | ok(client_streambufs[i - 1]->is_detached); 88 | 89 | /* the last stream is still ID-blocked */ 90 | ok(client_streams[i]->streams_blocked); 91 | 92 | quic_now += QUICLY_DELAYED_ACK_TIMEOUT; 93 | transmit(client, server); 94 | transmit(server, client); 95 | 96 | /* we would have free room now that RST of the server-sent side is ACKed */ 97 | ok(server_streambuf->is_detached); 98 | ok(!client_streams[i]->streams_blocked); 99 | ++i; 100 | 101 | /* but we cannot open one more */ 102 | ret = quicly_open_stream(client, client_streams + i, 0); 103 | ok(ret == 0); 104 | ok(client_streams[i]->streams_blocked); 105 | 106 | quicly_free(client); 107 | quicly_free(server); 108 | } 109 | -------------------------------------------------------------------------------- /t/test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Fastly, Kazuho Oku 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifndef test_h 23 | #define test_h 24 | 25 | #include "quicly.h" 26 | #include "quicly/streambuf.h" 27 | #include "picotest.h" 28 | 29 | typedef struct st_test_streambuf_t { 30 | quicly_streambuf_t super; 31 | struct { 32 | quicly_error_t stop_sending; 33 | quicly_error_t reset_stream; 34 | } error_received; 35 | int is_detached; 36 | } test_streambuf_t; 37 | 38 | extern quicly_address_t fake_address; 39 | extern int64_t quic_now; 40 | extern quicly_context_t quic_ctx; 41 | extern quicly_stream_callbacks_t stream_callbacks; 42 | extern size_t on_destroy_callcnt; 43 | 44 | const quicly_cid_plaintext_t *new_master_id(void); 45 | extern quicly_stream_open_t stream_open; 46 | size_t decode_packets(quicly_decoded_packet_t *decoded, struct iovec *raw, size_t cnt); 47 | int buffer_is(ptls_buffer_t *buf, const char *s); 48 | size_t transmit(quicly_conn_t *src, quicly_conn_t *dst); 49 | int max_data_is_equal(quicly_conn_t *client, quicly_conn_t *server); 50 | 51 | void test_ranges(void); 52 | void test_rate(void); 53 | void test_frame(void); 54 | void test_maxsender(void); 55 | void test_pacer(void); 56 | void test_sentmap(void); 57 | void test_loss(void); 58 | void test_simple(void); 59 | void test_lossy(void); 60 | void test_stream_concurrency(void); 61 | void test_received_cid(void); 62 | void test_local_cid(void); 63 | void test_retire_cid(void); 64 | void test_jumpstart(void); 65 | 66 | #endif 67 | --------------------------------------------------------------------------------