├── .github └── workflows │ ├── check.yml │ ├── interop.yml │ └── interop_tests.yml ├── COPYING ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── libquic ├── Makefile.am ├── client.c ├── handshake.c ├── libquic.pc.in ├── netinet │ └── quic.h ├── quic.man └── server.c ├── modules ├── Makefile.am ├── include │ ├── linux │ │ └── quic.h │ └── uapi │ │ └── linux │ │ └── quic.h └── net │ └── quic │ ├── Kconfig │ ├── Makefile │ ├── common.c │ ├── common.h │ ├── cong.c │ ├── cong.h │ ├── connid.c │ ├── connid.h │ ├── crypto.c │ ├── crypto.h │ ├── family.c │ ├── family.h │ ├── frame.c │ ├── frame.h │ ├── input.c │ ├── input.h │ ├── output.c │ ├── output.h │ ├── packet.c │ ├── packet.h │ ├── path.c │ ├── path.h │ ├── pnspace.c │ ├── pnspace.h │ ├── protocol.c │ ├── protocol.h │ ├── socket.c │ ├── socket.h │ ├── stream.c │ ├── stream.h │ ├── test │ ├── sample_test.c │ └── unit_test.c │ ├── timer.c │ └── timer.h └── tests ├── Makefile.am ├── alpn_test.c ├── func_test.c ├── http3_test.c ├── interop ├── Dockerfile ├── interop_test.c └── run_endpoint.sh ├── keys └── ca_cert_pkey_psk.sh ├── perf_test.c ├── runtest.sh ├── sample_test.c ├── syzkaller ├── net_quic_syscall.list ├── socket_inet_quic.txt └── socket_inet_quic.txt.const └── ticket_test.c /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: check 2 | 3 | on: [ push, pull_request, workflow_dispatch ] 4 | 5 | jobs: 6 | check: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 10 9 | steps: 10 | - name: Install dependencies 11 | run: | 12 | sudo apt-get update 13 | sudo apt-get -y install make autoconf automake libtool pkg-config \ 14 | gnutls-dev libglib2.0-dev libnl-genl-3-dev keyutils libkeyutils-dev \ 15 | linux-headers-$(uname -r) 16 | 17 | - name: Checkout linuxquic 18 | uses: actions/checkout@v4 19 | - name: Install quic module and libquic from linuxquic 20 | run: | 21 | ./autogen.sh 22 | ./configure --prefix=/usr 23 | make 24 | sudo make install 25 | 26 | # nghttp3 27 | - name: Checkout nghttp3 28 | uses: actions/checkout@v4 29 | with: 30 | repository: ngtcp2/nghttp3 31 | submodules: recursive 32 | path: nghttp3 33 | - name: Install nghttp3 34 | run: | 35 | cd nghttp3 36 | autoreconf -i 37 | ./configure --prefix=/usr/ 38 | make 39 | sudo make install 40 | cd ../ 41 | 42 | # ktls-utils 43 | - name: Checkout ktls-utils 44 | uses: actions/checkout@v4 45 | with: 46 | repository: oracle/ktls-utils 47 | path: ktls-utils 48 | - name: Install ktls-utils 49 | run: | 50 | cd ktls-utils 51 | ./autogen.sh 52 | ./configure --with-systemd 53 | make 54 | sudo make install 55 | cd ../ 56 | - name: Start tlshd from ktls-utils 57 | run: | 58 | echo "[debug]" | sudo tee /etc/tlshd.conf 59 | echo "loglevel=0" | sudo tee -a /etc/tlshd.conf 60 | echo "tls=0" | sudo tee -a /etc/tlshd.conf 61 | echo "nl=0" | sudo tee -a /etc/tlshd.conf 62 | echo "" | sudo tee -a /etc/tlshd.conf 63 | echo "[authenticate]" | sudo tee -a /etc/tlshd.conf 64 | echo "keyrings=quic" | sudo tee -a /etc/tlshd.conf 65 | echo "" | sudo tee -a /etc/tlshd.conf 66 | echo "[authenticate.client]" | sudo tee -a /etc/tlshd.conf 67 | echo "x509.truststore= $(pwd)/tests/keys/ca-cert.pem" | sudo tee -a /etc/tlshd.conf 68 | echo "x509.certificate=$(pwd)/tests/keys/client-cert.pem" | sudo tee -a /etc/tlshd.conf 69 | echo "x509.private_key=$(pwd)/tests/keys/client-key.pem" | sudo tee -a /etc/tlshd.conf 70 | echo "" | sudo tee -a /etc/tlshd.conf 71 | echo "[authenticate.server]" | sudo tee -a /etc/tlshd.conf 72 | echo "x509.truststore= $(pwd)/tests/keys/ca-cert.pem" | sudo tee -a /etc/tlshd.conf 73 | echo "x509.certificate=$(pwd)/tests/keys/server-cert.pem" | sudo tee -a /etc/tlshd.conf 74 | echo "x509.private_key=$(pwd)/tests/keys/server-key.pem" | sudo tee -a /etc/tlshd.conf 75 | sudo systemctl enable --now tlshd 76 | 77 | - name: Run selftests from linuxquic 78 | run: | 79 | sudo ip link set lo mtu 1500 80 | sudo make check 81 | -------------------------------------------------------------------------------- /.github/workflows/interop.yml: -------------------------------------------------------------------------------- 1 | name: interop 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | implementation: 6 | description: "implementation name" 7 | required: true 8 | default: "linuxquic" 9 | type: choice 10 | options: 11 | - "linuxquic" 12 | - "all" 13 | 14 | jobs: 15 | config: 16 | runs-on: ubuntu-latest 17 | outputs: 18 | logname: ${{ steps.set-logname.outputs.logname }} 19 | starttime: ${{ steps.set-starttime.outputs.starttime }} 20 | servers: ${{ steps.set-servers.outputs.servers }} 21 | clients: ${{ steps.set-clients.outputs.clients }} 22 | images: ${{ steps.set-images.outputs.images }} 23 | steps: 24 | - name: Set log name 25 | id: set-logname 26 | run: | 27 | LOGNAME=$(date -u +"%Y-%m-%dT%H:%M") 28 | echo $LOGNAME 29 | echo "logname=$LOGNAME" >> $GITHUB_OUTPUT 30 | - name: Save start time 31 | id: set-starttime 32 | run: | 33 | STARTTIME=$(date +%s) 34 | echo $STARTTIME 35 | echo "starttime=$STARTTIME" >> $GITHUB_OUTPUT 36 | - uses: actions/checkout@v4 37 | with: 38 | repository: quic-interop/quic-interop-runner 39 | - uses: actions/setup-python@v5 40 | with: 41 | python-version: 3.8 42 | - name: Determine servers 43 | id: set-servers 44 | run: | 45 | jq '.linuxquic //= {"image": "quay.io/lxin/linuxquic-interop:latest", "url": "https://github.com/lxin/quic", "role": "both"}' implementations.json > temp.json 46 | mv -f temp.json implementations.json 47 | SERVERS=$(jq -c 'with_entries(select(.value.role == "server" or .value.role == "both")) | keys_unsorted' implementations.json) 48 | echo $SERVERS 49 | echo "servers=$SERVERS" >> $GITHUB_OUTPUT 50 | - name: Determine clients 51 | id: set-clients 52 | run: | 53 | CLIENTS=$(jq -c 'with_entries(select(.value.role == "client" or .value.role == "both")) | keys_unsorted' implementations.json) 54 | echo $CLIENTS 55 | echo "clients=$CLIENTS" >> $GITHUB_OUTPUT 56 | - name: Determine Docker images 57 | id: set-images 58 | run: | 59 | IMAGES=$(jq -c 'keys_unsorted' implementations.json) 60 | echo $IMAGES 61 | echo "images=$IMAGES" >> $GITHUB_OUTPUT 62 | docker-pull-tools: 63 | runs-on: ubuntu-latest 64 | strategy: 65 | matrix: 66 | image: [ 'quic-network-simulator', 'quic-interop-iperf-endpoint' ] 67 | steps: 68 | - uses: actions/checkout@v4 69 | with: 70 | repository: quic-interop/quic-interop-runner 71 | - name: Pull 72 | run: | 73 | URL="martenseemann/${{ matrix.image }}" 74 | docker pull $URL 75 | echo "URL=$URL" >> $GITHUB_ENV 76 | - name: Docker inspect 77 | run: docker image inspect $URL 78 | - name: Save Docker image 79 | run: | 80 | docker save $URL | gzip --best > ${{ matrix.image }}.tar.gz 81 | du -sh ${{ matrix.image }}.tar.gz 82 | - name: Upload result 83 | uses: actions/upload-artifact@v4 84 | with: 85 | name: images-${{ matrix.image }} 86 | path: ${{ matrix.image }}.tar.gz 87 | if-no-files-found: error 88 | docker-pull-images: 89 | needs: [ config ] 90 | runs-on: ubuntu-latest 91 | strategy: 92 | matrix: 93 | image: ${{ fromJson(needs.config.outputs.images) }} 94 | name: Pull ${{ matrix.image }} 95 | steps: 96 | - uses: actions/checkout@v4 97 | with: 98 | repository: quic-interop/quic-interop-runner 99 | - name: Run docker pull 100 | run: | 101 | jq '.linuxquic //= {"image": "quay.io/lxin/linuxquic-interop:latest", "url": "https://github.com/lxin/quic", "role": "both"}' implementations.json > temp.json 102 | mv -f temp.json implementations.json 103 | URL=$(jq -r '.["${{ matrix.image }}"].image' implementations.json) 104 | echo $URL 105 | docker pull $URL 106 | echo "URL=$URL" >> $GITHUB_ENV 107 | - name: Docker inspect 108 | run: docker image inspect $URL 109 | - name: Save Docker image 110 | run: | 111 | docker save $URL | gzip --best > ${{ matrix.image }}.tar.gz 112 | du -sh ${{ matrix.image }}.tar.gz 113 | - name: Upload result 114 | uses: actions/upload-artifact@v4 115 | with: 116 | name: image-${{ matrix.image }} 117 | path: ${{ matrix.image }}.tar.gz 118 | if-no-files-found: error 119 | client-tests: 120 | if: ${{ github.event.inputs.implementation == 'linuxquic' }} 121 | uses: ./.github/workflows/interop_tests.yml 122 | needs: [ config, docker-pull-tools, docker-pull-images ] 123 | name: client group (linuxquic) 124 | with: 125 | clients: '[ "linuxquic" ]' 126 | servers: ${{ needs.config.outputs.servers }} 127 | server-tests: 128 | if: ${{ github.event.inputs.implementation == 'linuxquic' }} 129 | uses: ./.github/workflows/interop_tests.yml 130 | needs: [ config, docker-pull-tools, docker-pull-images ] 131 | name: server group (linuxquic) 132 | with: 133 | clients: ${{ needs.config.outputs.clients }} 134 | servers: '[ "linuxquic" ]' 135 | tests: 136 | if: ${{ github.event.inputs.implementation == 'all' }} 137 | uses: ./.github/workflows/interop_tests.yml 138 | needs: [ config, docker-pull-tools, docker-pull-images ] 139 | strategy: 140 | fail-fast: false 141 | matrix: 142 | client: ${{ fromJson(needs.config.outputs.clients) }} 143 | name: client group (${{ matrix.client }}) 144 | with: 145 | clients: '[ "${{ matrix.client }}" ]' 146 | servers: ${{ needs.config.outputs.servers }} 147 | aggregate: 148 | needs: [ config, client-tests, server-tests, tests ] 149 | if: always() 150 | runs-on: ubuntu-latest 151 | env: 152 | LOGNAME: ${{ needs.config.outputs.logname }} 153 | steps: 154 | - uses: actions/checkout@v4 155 | with: 156 | repository: quic-interop/quic-interop-runner 157 | - uses: actions/setup-python@v5 158 | with: 159 | python-version: 3.8 160 | - name: Download results 161 | uses: actions/download-artifact@v4 162 | with: 163 | pattern: results-* 164 | - name: Aggregate results 165 | run: | 166 | mv results-*/*.json . 167 | python .github/workflows/aggregate.py \ 168 | --start-time ${{ needs.config.outputs.starttime }} \ 169 | --server ${{ join(fromJson(needs.config.outputs.servers), ',') }} \ 170 | --client ${{ join(fromJson(needs.config.outputs.clients), ',') }} \ 171 | --log-dir=$LOGNAME \ 172 | --output result.json 173 | - name: Print result 174 | run: jq '.' result.json 175 | - name: Upload result to artifacts 176 | uses: actions/upload-artifact@v4 177 | with: 178 | name: result-aggregated 179 | path: result.json 180 | - name: Upload logs to interop.seemann.io 181 | uses: burnett01/rsync-deployments@796cf0d5e4b535745ce49d7429f77cf39e25ef39 # v7.0.1 182 | if: ${{ github.event_name == 'schedule' }} 183 | with: 184 | switches: -avzr 185 | path: result.json 186 | remote_path: ${{ vars.LOG_DIR }}/${{ needs.config.outputs.logname }}/ 187 | remote_host: interop.seemann.io 188 | remote_user: ${{ secrets.INTEROP_SEEMANN_IO_USER }} 189 | remote_key: ${{ secrets.INTEROP_SEEMANN_IO_SSH_KEY }} 190 | - name: Point interop.seemann.io to the latest result 191 | uses: appleboy/ssh-action@7eaf76671a0d7eec5d98ee897acda4f968735a17 # v1.2.0 192 | if: ${{ github.event_name == 'schedule' }} 193 | with: 194 | host: interop.seemann.io 195 | username: ${{ secrets.INTEROP_SEEMANN_IO_USER }} 196 | key: ${{ secrets.INTEROP_SEEMANN_IO_SSH_KEY }} 197 | envs: LOGNAME 198 | script: | 199 | cd ${{ vars.LOG_DIR }} 200 | jq '. += [ "${{ needs.config.outputs.logname }}" ]' logs.json | sponge logs.json 201 | rm latest || true 202 | ln -s $LOGNAME latest 203 | -------------------------------------------------------------------------------- /.github/workflows/interop_tests.yml: -------------------------------------------------------------------------------- 1 | name: interop tests 2 | on: 3 | workflow_call: 4 | inputs: 5 | clients: 6 | description: "clients" 7 | required: true 8 | type: string 9 | servers: 10 | description: "servers" 11 | required: true 12 | type: string 13 | 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | continue-on-error: true 18 | timeout-minutes: 45 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | client: ${{ fromJson(inputs.clients) }} 23 | server: ${{ fromJson(inputs.servers) }} 24 | name: (${{ matrix.client }} - ${{ matrix.server }}) 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | repository: quic-interop/quic-interop-runner 29 | - uses: actions/setup-python@v5 30 | with: 31 | python-version: 3.8 32 | - name: Enable IPv6 support 33 | run: sudo modprobe ip6table_filter 34 | - run: docker image ls 35 | - name: Download quic-network-simulator image 36 | uses: actions/download-artifact@v4 37 | with: 38 | name: images-quic-network-simulator 39 | - name: Download quic-interop-iperf-endpoint image 40 | uses: actions/download-artifact@v4 41 | with: 42 | name: images-quic-interop-iperf-endpoint 43 | - name: Download ${{ matrix.server }} Docker image 44 | uses: actions/download-artifact@v4 45 | with: 46 | name: image-${{ matrix.server }} 47 | - name: Download ${{ matrix.client }} Docker image 48 | if: ${{ matrix.server != matrix.client }} 49 | uses: actions/download-artifact@v4 50 | with: 51 | name: image-${{ matrix.client }} 52 | - name: Load docker images 53 | run: | 54 | docker load --input quic-network-simulator.tar.gz 55 | docker load --input quic-interop-iperf-endpoint.tar.gz 56 | docker load --input ${{ matrix.server }}.tar.gz 57 | docker load --input ${{ matrix.client }}.tar.gz 58 | - run: docker image ls 59 | - name: Install Wireshark 60 | run: | 61 | sudo add-apt-repository ppa:wireshark-dev/nightly 62 | sudo apt-get update 63 | sudo apt-get install -y --no-install-recommends tshark 64 | - name: Checkout linuxquic 65 | if: ${{ matrix.server == 'linuxquic' || matrix.client == 'linuxquic' }} 66 | uses: actions/checkout@v4 67 | with: 68 | path: quic 69 | - name: Install Linux QUIC module 70 | if: ${{ matrix.server == 'linuxquic' || matrix.client == 'linuxquic' }} 71 | run: | 72 | cd quic && sed -i '/LIBGNUTLS/d' configure.ac 73 | ./autogen.sh && ./configure --prefix=/usr 74 | sudo make -C modules install 75 | cd ../ && rm -r quic 76 | - name: Install Python packages 77 | run: | 78 | pip install -U pip 79 | pip install -r requirements.txt 80 | - name: Run tests 81 | env: 82 | CRON: "true" 83 | run: | 84 | jq '.linuxquic //= {"image": "quay.io/lxin/linuxquic-interop:latest", "url": "https://github.com/lxin/quic", "role": "both"}' implementations.json > temp.json 85 | mv -f temp.json implementations.json 86 | (python run.py --client ${{ matrix.client }} --server ${{ matrix.server }} --log-dir logs --json ${{ matrix.server }}_${{ matrix.client }}_results.json -t onlyTests || true) | tee output.txt 87 | mkdir -p logs/${{ matrix.server }}_${{ matrix.client }} 88 | mv output.txt logs/${{ matrix.server }}_${{ matrix.client }}/ 89 | - name: Run measurements 90 | env: 91 | CRON: "true" 92 | run: | 93 | python run.py --client ${{ matrix.client }} --server ${{ matrix.server }} --log-dir logs_measurement --json ${{ matrix.server }}_${{ matrix.client }}_measurements.json -t onlyMeasurements || true 94 | if [ ! -d "logs_measurement" ]; then exit 0; fi 95 | find logs_measurement -depth -name "sim" -type d -exec rm -r "{}" \; 96 | find logs_measurement -depth -name "client" -type d -exec rm -r "{}" \; 97 | find logs_measurement -depth -name "server" -type d -exec rm -r "{}" \; 98 | mv logs_measurement/${{ matrix.server }}_${{ matrix.client }}/* logs/${{ matrix.server }}_${{ matrix.client }}/ 99 | - name: Upload logs to interop.seemann.io 100 | uses: burnett01/rsync-deployments@796cf0d5e4b535745ce49d7429f77cf39e25ef39 # v7.0.1 101 | if: ${{ github.event_name == 'schedule' }} 102 | with: 103 | switches: -avzr --relative 104 | path: logs/./${{ matrix.server }}_${{ matrix.client }}/ 105 | remote_path: ${{ vars.LOG_DIR }}/${{ needs.config.outputs.logname }} 106 | remote_host: interop.seemann.io 107 | remote_user: ${{ secrets.INTEROP_SEEMANN_IO_USER }} 108 | remote_key: ${{ secrets.INTEROP_SEEMANN_IO_SSH_KEY }} 109 | - name: Upload result 110 | uses: actions/upload-artifact@v4 111 | continue-on-error: true 112 | with: 113 | name: results-${{ matrix.server }}-${{ matrix.client }} 114 | if-no-files-found: error 115 | path: | 116 | ${{ matrix.server }}_${{ matrix.client }}_results.json 117 | ${{ matrix.server }}_${{ matrix.client }}_measurements.json 118 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | This project contains components licensed under different open-source 2 | licenses. 3 | 4 | - modules/net/quic/, modules/include/linux/ 5 | 6 | These directories are licensed under the GNU General Public License, 7 | version 2 or later (GPL-2.0+). 8 | 9 | License text: 10 | 11 | https://www.gnu.org/licenses/old-licenses/gpl-2.0.html 12 | 13 | - modules/include/uapi/linux/ 14 | 15 | This directory is licensed under the GNU General Public License, 16 | version 2 or later, with the Linux-syscall-note exception (GPL-2.0+ 17 | WITH Linux-syscall-note). 18 | 19 | The Linux-syscall-note clarifies that user-space programs including 20 | these headers are not considered derivative works. 21 | 22 | GPL-2.0 license text: 23 | 24 | https://www.gnu.org/licenses/old-licenses/gpl-2.0.html 25 | 26 | Linux-syscall-note documentation: 27 | 28 | https://www.kernel.org/doc/html/latest/process/license-rules.html#kernel-headers 29 | 30 | - libquic/ 31 | 32 | This directory is licensed under the GNU Lesser General Public License, 33 | version 2.1 or later (LGPL-2.1+). 34 | 35 | License text: 36 | 37 | https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html 38 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = COPYING README.md 2 | SUBDIRS = $(MODULES) libquic tests 3 | 4 | MAINTAINERCLEANFILES = Makefile.in quic*.tar.gz 5 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | GEN="compile config.guess config.sub depcomp install-sh missing aclocal.m4 \ 2 | configure config.h.in autom4te.cache ltmain.sh ar-lib INSTALL \ 3 | Makefile.in libquic/Makefile.in modules/Makefile.in tests/Makefile.in" 4 | 5 | rm -rf $GEN 6 | 7 | [ "$1" = "clean" ] && exit 0 8 | 9 | libtoolize --force --copy 10 | aclocal 11 | autoheader 12 | automake --add-missing --copy --gnu 13 | autoconf 14 | 15 | exit 0 16 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([quic], [1.0], [lucien.xin@gmail.com]) 2 | AM_INIT_AUTOMAKE([foreign]) 3 | 4 | AC_PROG_CC 5 | AM_PROG_AR 6 | LT_INIT 7 | 8 | PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= 3.7.0]) 9 | AC_SUBST([LIBGNUTLS_CFLAGS]) 10 | AC_SUBST([LIBGNUTLS_LIBS]) 11 | 12 | AC_ARG_WITH([modules], AS_HELP_STRING([--without-modules], [skip building kernel modules])) 13 | 14 | if test "x$with_modules" != "xno"; then 15 | kernel="/lib/modules/`uname -r`" 16 | if test -d "$kernel/kernel/net/quic" && test "x$with_modules" != "xyes" ; then 17 | BUILTIN_MODULES=yes 18 | AC_MSG_NOTICE([quic module found in kernel, skipping build]) 19 | test -f /usr/include/linux/quic.h || AC_MSG_ERROR([no kernel-headers found]) 20 | else 21 | AC_SUBST([MODULES], [modules]) 22 | AC_SUBST([KERNEL_BUILD], [$kernel/build]) 23 | AC_SUBST([KERNEL_EXTRA], [$kernel/extra]) 24 | test -f "$kernel/build/Makefile" || AC_MSG_ERROR([no kernel-devel found]) 25 | AC_CONFIG_FILES([modules/Makefile]) 26 | fi 27 | else 28 | AC_MSG_NOTICE([--without-modules specified, skipping kernel module build]) 29 | fi 30 | 31 | AM_CONDITIONAL([BUILTIN_MODULES], [test "x$BUILTIN_MODULES" = "xyes"]) 32 | 33 | AC_CONFIG_HEADERS([config.h]) 34 | AC_CONFIG_FILES([Makefile libquic/Makefile libquic/libquic.pc tests/Makefile]) 35 | AC_OUTPUT 36 | -------------------------------------------------------------------------------- /libquic/Makefile.am: -------------------------------------------------------------------------------- 1 | man7_MANS = quic.man 2 | EXTRA_DIST = $(man7_MANS) 3 | 4 | lib_LTLIBRARIES = libquic.la 5 | libquic_la_SOURCES = client.c handshake.c server.c 6 | libquic_la_CPPFLAGS = -I$(top_builddir)/libquic/ 7 | libquic_la_CFLAGS = -Werror -Wall $(LIBGNUTLS_CFLAGS) 8 | libquic_la_LIBADD = $(LIBGNUTLS_LIBS) 9 | libquic_la_LDFLAGS = -version-info 1:0:0 10 | 11 | libcnetinetdir = $(includedir)/netinet 12 | libcnetinet_HEADERS = netinet/quic.h 13 | 14 | if !BUILTIN_MODULES 15 | libquic_la_CPPFLAGS += -I$(top_builddir)/modules/include/uapi/ 16 | 17 | libclinuxdir = $(includedir)/linux 18 | libclinux_HEADERS = $(top_builddir)/modules/include/uapi/linux/quic.h 19 | endif 20 | 21 | pkgconfigdir = $(libdir)/pkgconfig 22 | pkgconfig_DATA = libquic.pc 23 | 24 | MAINTAINERCLEANFILES = Makefile.in 25 | -------------------------------------------------------------------------------- /libquic/client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Perform a QUIC client-side handshake. 3 | * 4 | * Copyright (c) 2024 Red Hat, Inc. 5 | * 6 | * libquic is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2.1 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program. If not, see 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "netinet/quic.h" 28 | 29 | static int quic_client_psk_handshake(int sockfd, const char *identity, 30 | const gnutls_datum_t *key, const char *alpns) 31 | { 32 | gnutls_psk_client_credentials_t cred; 33 | gnutls_session_t session; 34 | size_t alpn_len; 35 | char alpn[64]; 36 | int ret; 37 | 38 | ret = gnutls_psk_allocate_client_credentials(&cred); 39 | if (ret) 40 | goto err; 41 | ret = gnutls_psk_set_client_credentials(cred, identity, key, GNUTLS_PSK_KEY_RAW); 42 | if (ret) 43 | goto err_cred; 44 | 45 | ret = gnutls_init(&session, GNUTLS_CLIENT); 46 | if (ret) 47 | goto err_cred; 48 | ret = gnutls_credentials_set(session, GNUTLS_CRD_PSK, cred); 49 | if (ret) 50 | goto err_session; 51 | 52 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL); 53 | if (ret) 54 | goto err_session; 55 | 56 | if (alpns) { 57 | ret = quic_session_set_alpn(session, alpns, strlen(alpns)); 58 | if (ret) 59 | goto err_session; 60 | } 61 | 62 | gnutls_transport_set_int(session, sockfd); 63 | 64 | ret = quic_handshake(session); 65 | if (ret) 66 | goto err_session; 67 | 68 | if (alpns) { 69 | alpn_len = sizeof(alpn); 70 | ret = quic_session_get_alpn(session, alpn, &alpn_len); 71 | } 72 | 73 | err_session: 74 | gnutls_deinit(session); 75 | err_cred: 76 | gnutls_psk_free_client_credentials(cred); 77 | err: 78 | return ret; 79 | } 80 | 81 | static int quic_client_x509_handshake(int sockfd, const char *alpns, const char *host) 82 | { 83 | gnutls_certificate_credentials_t cred; 84 | gnutls_session_t session; 85 | size_t alpn_len; 86 | char alpn[64]; 87 | int ret; 88 | 89 | ret = gnutls_certificate_allocate_credentials(&cred); 90 | if (ret) 91 | goto err; 92 | ret = gnutls_certificate_set_x509_system_trust(cred); 93 | if (ret < 0) 94 | goto err_cred; 95 | 96 | ret = gnutls_init(&session, GNUTLS_CLIENT); 97 | if (ret) 98 | goto err_cred; 99 | ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); 100 | if (ret) 101 | goto err_session; 102 | 103 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL); 104 | if (ret) 105 | goto err_session; 106 | 107 | if (alpns) { 108 | ret = quic_session_set_alpn(session, alpns, strlen(alpns)); 109 | if (ret) 110 | goto err_session; 111 | } 112 | 113 | if (host) { 114 | ret = gnutls_server_name_set(session, GNUTLS_NAME_DNS, host, strlen(host)); 115 | if (ret) 116 | goto err_session; 117 | } 118 | 119 | gnutls_transport_set_int(session, sockfd); 120 | 121 | ret = quic_handshake(session); 122 | if (ret) 123 | goto err_session; 124 | 125 | if (alpns) { 126 | alpn_len = sizeof(alpn); 127 | ret = quic_session_get_alpn(session, alpn, &alpn_len); 128 | } 129 | 130 | err_session: 131 | gnutls_deinit(session); 132 | err_cred: 133 | gnutls_certificate_free_credentials(cred); 134 | err: 135 | return ret; 136 | } 137 | 138 | static int quic_file_read_psk(const char *file, char *identity[], gnutls_datum_t *pkey) 139 | { 140 | unsigned char *end, *head, *key, *buf; 141 | int fd, err = -1, i = 0; 142 | struct stat statbuf; 143 | gnutls_datum_t gkey; 144 | unsigned int size; 145 | 146 | fd = open(file, O_RDONLY); 147 | if (fd == -1) 148 | return -1; 149 | if (fstat(fd, &statbuf)) 150 | goto out; 151 | 152 | size = (unsigned int)statbuf.st_size; 153 | head = malloc(size); 154 | if (!head) 155 | goto out; 156 | if (read(fd, head, size) == -1) { 157 | free(head); 158 | goto out; 159 | } 160 | 161 | buf = head; 162 | end = buf + size - 1; 163 | do { 164 | key = (unsigned char *)strchr((char *)buf, ':'); 165 | if (!key) { 166 | free(head); 167 | goto out; 168 | } 169 | *key = '\0'; 170 | identity[i] = (char *)buf; 171 | 172 | key++; 173 | gkey.data = key; 174 | 175 | buf = (unsigned char *)strchr((char *)key, '\n'); 176 | if (!buf) { 177 | gkey.size = end - gkey.data; 178 | buf = end; 179 | goto decode; 180 | } 181 | *buf = '\0'; 182 | buf++; 183 | gkey.size = strlen((char *)gkey.data); 184 | decode: 185 | if (gnutls_hex_decode2(&gkey, &pkey[i])) { 186 | free(head); 187 | goto out; 188 | } 189 | i++; 190 | } while (buf < end && i < 5); 191 | 192 | err = i; 193 | out: 194 | close(fd); 195 | return err; 196 | } 197 | 198 | /** 199 | * quic_client_handshake - start a QUIC handshake with Certificate or PSK mode on client side 200 | * @sockfd: IPPROTO_QUIC type socket 201 | * @psk_file: pre-shared key file for PSK mode 202 | * @hostname: server name for Certificate mode 203 | * @alpns: ALPNs supported and split by ',' 204 | * 205 | * Return values: 206 | * - On success, 0 is returned. 207 | * - On error, a negative error value is returned. 208 | */ 209 | int quic_client_handshake(int sockfd, const char *pkey_file, 210 | const char *hostname, const char *alpns) 211 | { 212 | gnutls_datum_t keys[5]; 213 | char *identities[5]; 214 | int ret; 215 | 216 | if (!pkey_file) 217 | return quic_client_x509_handshake(sockfd, alpns, hostname); 218 | 219 | ret = quic_file_read_psk(pkey_file, identities, keys); 220 | if (ret <= 0) 221 | return -EINVAL; 222 | 223 | ret = quic_client_psk_handshake(sockfd, identities[0], &keys[0], alpns); 224 | 225 | free(identities[0]); 226 | return ret; 227 | } 228 | -------------------------------------------------------------------------------- /libquic/libquic.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: quic 7 | Description: User-level API library for in-kernel QUIC 8 | Version: 1.0 9 | Libs: -L${libdir} -lquic 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /libquic/netinet/quic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Provide APIs for QUIC handshake. 3 | * 4 | * Copyright (c) 2024 Red Hat, Inc. 5 | * 6 | * libquic is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2.1 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program. If not, see 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | /* Socket option layer for QUIC */ 25 | #ifndef SOL_QUIC 26 | #define SOL_QUIC 288 27 | #endif 28 | 29 | #ifndef IPPROTO_QUIC 30 | #define IPPROTO_QUIC 261 31 | #endif 32 | 33 | #define QUIC_PRIORITY \ 34 | "NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+ECDHE-PSK:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ 35 | "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ 36 | "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ 37 | "%DISABLE_TLS13_COMPAT_MODE" 38 | 39 | int quic_client_handshake(int sockfd, const char *pkey_file, 40 | const char *hostname, const char *alpns); 41 | int quic_server_handshake(int sockfd, const char *pkey_file, 42 | const char *cert_file, const char *alpns); 43 | 44 | enum quic_handshake_step_op { 45 | QUIC_HANDSHAKE_STEP_OP_SENDMSG = 1, 46 | QUIC_HANDSHAKE_STEP_OP_RECVMSG, 47 | }; 48 | 49 | struct quic_handshake_step_sendmsg { 50 | const struct msghdr *msg; 51 | int flags; 52 | ssize_t retval; 53 | }; 54 | 55 | struct quic_handshake_step_recvmsg { 56 | struct msghdr *msg; 57 | int flags; 58 | ssize_t retval; 59 | }; 60 | 61 | struct quic_handshake_step { 62 | enum quic_handshake_step_op op; 63 | 64 | union { 65 | struct quic_handshake_step_sendmsg s_sendmsg; 66 | struct quic_handshake_step_recvmsg s_recvmsg; 67 | }; 68 | }; 69 | 70 | int quic_handshake_init(gnutls_session_t session, 71 | struct quic_handshake_step **pstep); 72 | int quic_handshake_step(gnutls_session_t session, 73 | struct quic_handshake_step **pstep); 74 | void quic_handshake_deinit(gnutls_session_t session); 75 | 76 | int quic_handshake(gnutls_session_t session); 77 | 78 | int quic_session_get_data(gnutls_session_t session, 79 | void *data, size_t *size); 80 | int quic_session_set_data(gnutls_session_t session, 81 | const void *data, size_t size); 82 | 83 | int quic_session_get_alpn(gnutls_session_t session, 84 | void *data, size_t *size); 85 | int quic_session_set_alpn(gnutls_session_t session, 86 | const void *data, size_t size); 87 | 88 | ssize_t quic_sendmsg(int sockfd, const void *msg, size_t len, 89 | int64_t sid, uint32_t flags); 90 | ssize_t quic_recvmsg(int sockfd, void *msg, size_t len, 91 | int64_t *sid, uint32_t *flags); 92 | 93 | typedef void (*quic_set_log_func_t)(int level, const char *msg); 94 | quic_set_log_func_t quic_set_log_func(quic_set_log_func_t func); 95 | int quic_set_log_level(int level); 96 | -------------------------------------------------------------------------------- /libquic/server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Perform a QUIC server-side handshake. 3 | * 4 | * Copyright (c) 2024 Red Hat, Inc. 5 | * 6 | * libquic is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License as 8 | * published by the Free Software Foundation; either version 2.1 of 9 | * the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with this program. If not, see 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "netinet/quic.h" 25 | 26 | static int quic_server_psk_handshake(int sockfd, const char *psk, const char *alpns) 27 | { 28 | gnutls_psk_server_credentials_t cred; 29 | gnutls_session_t session; 30 | size_t alpn_len; 31 | char alpn[64]; 32 | int ret; 33 | 34 | ret = gnutls_psk_allocate_server_credentials(&cred); 35 | if (ret) 36 | goto err; 37 | ret = gnutls_psk_set_server_credentials_file(cred, psk); 38 | if (ret) 39 | goto err_cred; 40 | ret = gnutls_init(&session, GNUTLS_SERVER | GNUTLS_NO_AUTO_SEND_TICKET); 41 | if (ret) 42 | goto err_cred; 43 | ret = gnutls_credentials_set(session, GNUTLS_CRD_PSK, cred); 44 | if (ret) 45 | goto err_session; 46 | 47 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL); 48 | if (ret) 49 | goto err_session; 50 | 51 | if (alpns) { 52 | ret = quic_session_set_alpn(session, alpns, strlen(alpns)); 53 | if (ret) 54 | goto err_session; 55 | } 56 | 57 | gnutls_transport_set_int(session, sockfd); 58 | 59 | ret = quic_handshake(session); 60 | if (ret) 61 | goto err_session; 62 | 63 | if (alpns) { 64 | alpn_len = sizeof(alpn); 65 | ret = quic_session_get_alpn(session, alpn, &alpn_len); 66 | } 67 | 68 | err_session: 69 | gnutls_deinit(session); 70 | err_cred: 71 | gnutls_psk_free_server_credentials(cred); 72 | err: 73 | return ret; 74 | } 75 | 76 | static int quic_server_x509_handshake(int sockfd, const char *pkey, 77 | const char *cert, const char *alpns) 78 | { 79 | gnutls_certificate_credentials_t cred; 80 | gnutls_session_t session; 81 | size_t alpn_len; 82 | char alpn[64]; 83 | int ret; 84 | 85 | ret = gnutls_certificate_allocate_credentials(&cred); 86 | if (ret) 87 | goto err; 88 | ret = gnutls_certificate_set_x509_system_trust(cred); 89 | if (ret < 0) 90 | goto err_cred; 91 | ret = gnutls_certificate_set_x509_key_file(cred, cert, pkey, GNUTLS_X509_FMT_PEM); 92 | if (ret) 93 | goto err_cred; 94 | ret = gnutls_init(&session, GNUTLS_SERVER | GNUTLS_NO_AUTO_SEND_TICKET); 95 | if (ret) 96 | goto err_cred; 97 | ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); 98 | if (ret) 99 | goto err_session; 100 | 101 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL); 102 | if (ret) 103 | goto err_session; 104 | 105 | if (alpns) { 106 | ret = quic_session_set_alpn(session, alpns, strlen(alpns)); 107 | if (ret) 108 | goto err_session; 109 | } 110 | 111 | gnutls_transport_set_int(session, sockfd); 112 | 113 | ret = quic_handshake(session); 114 | if (ret) 115 | goto err_session; 116 | 117 | if (alpns) { 118 | alpn_len = sizeof(alpn); 119 | ret = quic_session_get_alpn(session, alpn, &alpn_len); 120 | } 121 | 122 | err_session: 123 | gnutls_deinit(session); 124 | err_cred: 125 | gnutls_certificate_free_credentials(cred); 126 | err: 127 | return ret; 128 | } 129 | 130 | /** 131 | * quic_server_handshake - start a QUIC handshake with Certificate or PSK mode on server side 132 | * @sockfd: IPPROTO_QUIC type socket 133 | * @pkey_file: private key file for Certificate mode or pre-shared key file for PSK mode 134 | * @cert_file: certificate file for Certificate mode or null for PSK mode 135 | * @alpns: ALPNs supported and split by ',' 136 | * 137 | * Return values: 138 | * - On success, 0 is returned. 139 | * - On error, a negative error value is returned. 140 | */ 141 | int quic_server_handshake(int sockfd, const char *pkey_file, 142 | const char *cert_file, const char *alpns) 143 | { 144 | if (cert_file) 145 | return quic_server_x509_handshake(sockfd, pkey_file, cert_file, alpns); 146 | 147 | return quic_server_psk_handshake(sockfd, pkey_file, alpns); 148 | } 149 | -------------------------------------------------------------------------------- /modules/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = include net 2 | MODULES = quic_unit_test quic_sample_test quic 3 | 4 | all: 5 | $(MAKE) -C $(KERNEL_BUILD) M=$(CURDIR)/net/quic modules \ 6 | ROOTDIR=$(CURDIR) CONFIG_IP_QUIC=m CONFIG_IP_QUIC_TEST=m 7 | 8 | install: uninstall_modules install_headers install_modules depmod 9 | 10 | uninstall: uninstall_modules uninstall_headers depmod 11 | 12 | clean: 13 | $(MAKE) -C $(KERNEL_BUILD) M=$(CURDIR)/net/quic clean 14 | 15 | install_headers: 16 | install -m 644 include/uapi/linux/quic.h /usr/include/linux 17 | 18 | install_modules: all $(KERNEL_EXTRA) 19 | $(foreach module, $(MODULES), \ 20 | ! [ -f net/quic/$(module).ko ] || \ 21 | install -m 644 net/quic/$(module).ko $(KERNEL_EXTRA);) 22 | 23 | uninstall_modules: 24 | $(foreach module, $(MODULES), \ 25 | ! [ -d /sys/module/$(module) ] || rmmod $(module); \ 26 | rm -f $(KERNEL_EXTRA)/$(module).ko;) 27 | 28 | uninstall_headers: 29 | rm -f /usr/include/linux/quic.h 30 | 31 | $(KERNEL_EXTRA): 32 | mkdir -p $@ 33 | 34 | depmod: 35 | depmod -a 36 | 37 | MAINTAINERCLEANFILES = Makefile.in 38 | -------------------------------------------------------------------------------- /modules/include/linux/quic.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #ifndef __linux_quic_h__ 12 | #define __linux_quic_h__ 13 | 14 | #include 15 | 16 | int quic_sock_setopt(struct sock *sk, int optname, void *optval, unsigned int optlen); 17 | int quic_sock_getopt(struct sock *sk, int optname, void *optval, unsigned int *optlen); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /modules/include/uapi/linux/quic.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #ifndef __uapi_quic_h__ 12 | #define __uapi_quic_h__ 13 | 14 | #ifdef __KERNEL__ 15 | #include 16 | #include 17 | #else 18 | #include 19 | #include 20 | #endif 21 | 22 | enum { 23 | IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed and Secure Transport */ 24 | #define IPPROTO_QUIC IPPROTO_QUIC 25 | }; 26 | 27 | #define SOL_QUIC 288 28 | 29 | /* Send or Receive Options APIs */ 30 | enum quic_cmsg_type { 31 | QUIC_STREAM_INFO, 32 | QUIC_HANDSHAKE_INFO, 33 | }; 34 | 35 | #define QUIC_STREAM_TYPE_SERVER_MASK 0x01 36 | #define QUIC_STREAM_TYPE_UNI_MASK 0x02 37 | #define QUIC_STREAM_TYPE_MASK 0x03 38 | 39 | enum quic_msg_flags { 40 | /* flags for stream_flags */ 41 | MSG_STREAM_NEW = MSG_SYN, 42 | MSG_STREAM_FIN = MSG_FIN, 43 | MSG_STREAM_UNI = MSG_CONFIRM, 44 | MSG_STREAM_DONTWAIT = MSG_WAITFORONE, 45 | MSG_STREAM_SNDBLOCK = MSG_ERRQUEUE, 46 | 47 | /* extented flags for msg_flags */ 48 | MSG_DATAGRAM = MSG_RST, 49 | MSG_NOTIFICATION = MSG_MORE, 50 | }; 51 | 52 | enum quic_crypto_level { 53 | QUIC_CRYPTO_APP, 54 | QUIC_CRYPTO_INITIAL, 55 | QUIC_CRYPTO_HANDSHAKE, 56 | QUIC_CRYPTO_EARLY, 57 | QUIC_CRYPTO_MAX, 58 | }; 59 | 60 | struct quic_handshake_info { 61 | uint8_t crypto_level; 62 | }; 63 | 64 | struct quic_stream_info { 65 | int64_t stream_id; 66 | uint32_t stream_flags; 67 | }; 68 | 69 | /* Socket Options APIs */ 70 | #define QUIC_SOCKOPT_EVENT 0 71 | #define QUIC_SOCKOPT_STREAM_OPEN 1 72 | #define QUIC_SOCKOPT_STREAM_RESET 2 73 | #define QUIC_SOCKOPT_STREAM_STOP_SENDING 3 74 | #define QUIC_SOCKOPT_CONNECTION_ID 4 75 | #define QUIC_SOCKOPT_CONNECTION_CLOSE 5 76 | #define QUIC_SOCKOPT_CONNECTION_MIGRATION 6 77 | #define QUIC_SOCKOPT_KEY_UPDATE 7 78 | #define QUIC_SOCKOPT_TRANSPORT_PARAM 8 79 | #define QUIC_SOCKOPT_CONFIG 9 80 | #define QUIC_SOCKOPT_TOKEN 10 81 | #define QUIC_SOCKOPT_ALPN 11 82 | #define QUIC_SOCKOPT_SESSION_TICKET 12 83 | #define QUIC_SOCKOPT_CRYPTO_SECRET 13 84 | #define QUIC_SOCKOPT_TRANSPORT_PARAM_EXT 14 85 | 86 | #define QUIC_VERSION_V1 0x1 87 | #define QUIC_VERSION_V2 0x6b3343cf 88 | 89 | struct quic_transport_param { 90 | uint8_t remote; 91 | uint8_t disable_active_migration; 92 | uint8_t grease_quic_bit; 93 | uint8_t stateless_reset; 94 | uint8_t disable_1rtt_encryption; 95 | uint8_t disable_compatible_version; 96 | uint8_t active_connection_id_limit; 97 | uint8_t ack_delay_exponent; 98 | uint16_t max_datagram_frame_size; 99 | uint16_t max_udp_payload_size; 100 | uint32_t max_idle_timeout; 101 | uint32_t max_ack_delay; 102 | uint16_t max_streams_bidi; 103 | uint16_t max_streams_uni; 104 | uint64_t max_data; 105 | uint64_t max_stream_data_bidi_local; 106 | uint64_t max_stream_data_bidi_remote; 107 | uint64_t max_stream_data_uni; 108 | uint64_t reserved; 109 | }; 110 | 111 | struct quic_config { 112 | uint32_t version; 113 | uint32_t plpmtud_probe_interval; 114 | uint32_t initial_smoothed_rtt; 115 | uint32_t payload_cipher_type; 116 | uint8_t congestion_control_algo; 117 | uint8_t validate_peer_address; 118 | uint8_t stream_data_nodelay; 119 | uint8_t receive_session_ticket; 120 | uint8_t certificate_request; 121 | uint8_t reserved[3]; 122 | }; 123 | 124 | struct quic_crypto_secret { 125 | uint8_t send; /* send or recv */ 126 | uint8_t level; /* crypto level */ 127 | uint32_t type; /* TLS_CIPHER_* */ 128 | #define QUIC_CRYPTO_SECRET_BUFFER_SIZE 48 129 | uint8_t secret[QUIC_CRYPTO_SECRET_BUFFER_SIZE]; 130 | }; 131 | 132 | enum quic_cong_algo { 133 | QUIC_CONG_ALG_RENO, 134 | QUIC_CONG_ALG_CUBIC, 135 | QUIC_CONG_ALG_MAX, 136 | }; 137 | 138 | struct quic_errinfo { 139 | int64_t stream_id; 140 | uint32_t errcode; 141 | }; 142 | 143 | struct quic_connection_id_info { 144 | uint8_t dest; 145 | uint32_t active; 146 | uint32_t prior_to; 147 | }; 148 | 149 | struct quic_event_option { 150 | uint8_t type; 151 | uint8_t on; 152 | }; 153 | 154 | /* Event APIs */ 155 | enum quic_event_type { 156 | QUIC_EVENT_NONE, 157 | QUIC_EVENT_STREAM_UPDATE, 158 | QUIC_EVENT_STREAM_MAX_DATA, 159 | QUIC_EVENT_STREAM_MAX_STREAM, 160 | QUIC_EVENT_CONNECTION_ID, 161 | QUIC_EVENT_CONNECTION_CLOSE, 162 | QUIC_EVENT_CONNECTION_MIGRATION, 163 | QUIC_EVENT_KEY_UPDATE, 164 | QUIC_EVENT_NEW_TOKEN, 165 | QUIC_EVENT_NEW_SESSION_TICKET, 166 | QUIC_EVENT_END, 167 | QUIC_EVENT_MAX = QUIC_EVENT_END - 1, 168 | }; 169 | 170 | enum { 171 | QUIC_STREAM_SEND_STATE_READY, 172 | QUIC_STREAM_SEND_STATE_SEND, 173 | QUIC_STREAM_SEND_STATE_SENT, 174 | QUIC_STREAM_SEND_STATE_RECVD, 175 | QUIC_STREAM_SEND_STATE_RESET_SENT, 176 | QUIC_STREAM_SEND_STATE_RESET_RECVD, 177 | 178 | QUIC_STREAM_RECV_STATE_RECV, 179 | QUIC_STREAM_RECV_STATE_SIZE_KNOWN, 180 | QUIC_STREAM_RECV_STATE_RECVD, 181 | QUIC_STREAM_RECV_STATE_READ, 182 | QUIC_STREAM_RECV_STATE_RESET_RECVD, 183 | QUIC_STREAM_RECV_STATE_RESET_READ, 184 | }; 185 | 186 | struct quic_stream_update { 187 | int64_t id; 188 | uint8_t state; 189 | uint32_t errcode; 190 | uint64_t finalsz; 191 | }; 192 | 193 | struct quic_stream_max_data { 194 | int64_t id; 195 | uint64_t max_data; 196 | }; 197 | 198 | struct quic_connection_close { 199 | uint32_t errcode; 200 | uint8_t frame; 201 | uint8_t phrase[]; 202 | }; 203 | 204 | union quic_event { 205 | struct quic_stream_update update; 206 | struct quic_stream_max_data max_data; 207 | struct quic_connection_close close; 208 | struct quic_connection_id_info info; 209 | uint64_t max_stream; 210 | uint8_t local_migration; 211 | uint8_t key_update_phase; 212 | }; 213 | 214 | enum { 215 | QUIC_TRANSPORT_ERROR_NONE, 216 | QUIC_TRANSPORT_ERROR_INTERNAL, 217 | QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED, 218 | QUIC_TRANSPORT_ERROR_FLOW_CONTROL, 219 | QUIC_TRANSPORT_ERROR_STREAM_LIMIT, 220 | QUIC_TRANSPORT_ERROR_STREAM_STATE, 221 | QUIC_TRANSPORT_ERROR_FINAL_SIZE, 222 | QUIC_TRANSPORT_ERROR_FRAME_ENCODING, 223 | QUIC_TRANSPORT_ERROR_TRANSPORT_PARAM, 224 | QUIC_TRANSPORT_ERROR_CONNECTION_ID_LIMIT, 225 | QUIC_TRANSPORT_ERROR_PROTOCOL_VIOLATION, 226 | QUIC_TRANSPORT_ERROR_INVALID_TOKEN, 227 | QUIC_TRANSPORT_ERROR_APPLICATION, 228 | QUIC_TRANSPORT_ERROR_CRYPTO_BUF_EXCEEDED, 229 | QUIC_TRANSPORT_ERROR_KEY_UPDATE, 230 | QUIC_TRANSPORT_ERROR_AEAD_LIMIT_REACHED, 231 | QUIC_TRANSPORT_ERROR_NO_VIABLE_PATH, 232 | 233 | /* The cryptographic handshake failed. A range of 256 values is reserved 234 | * for carrying error codes specific to the cryptographic handshake that 235 | * is used. Codes for errors occurring when TLS is used for the 236 | * cryptographic handshake are described in Section 4.8 of [QUIC-TLS]. 237 | */ 238 | QUIC_TRANSPORT_ERROR_CRYPTO = 0x0100, 239 | }; 240 | 241 | #endif /* __uapi_quic_h__ */ 242 | -------------------------------------------------------------------------------- /modules/net/quic/Kconfig: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # 3 | # QUIC configuration 4 | # 5 | 6 | menuconfig IP_QUIC 7 | tristate "QUIC: A UDP-Based Multiplexed and Secure Transport (Experimental)" 8 | depends on INET 9 | depends on IPV6 10 | select NET_UDP_TUNNEL 11 | help 12 | QUIC: A UDP-Based Multiplexed and Secure Transport 13 | 14 | From rfc9000 . 15 | 16 | QUIC provides applications with flow-controlled streams for structured 17 | communication, low-latency connection establishment, and network path 18 | migration. QUIC includes security measures that ensure 19 | confidentiality, integrity, and availability in a range of deployment 20 | circumstances. Accompanying documents describe the integration of 21 | TLS for key negotiation, loss detection, and an exemplary congestion 22 | control algorithm. 23 | 24 | To compile this protocol support as a module, choose M here: the 25 | module will be called quic. Debug messages are handled by the 26 | kernel's dynamic debugging framework. 27 | 28 | If in doubt, say N. 29 | 30 | if IP_QUIC 31 | config IP_QUIC_TEST 32 | depends on NET_HANDSHAKE || KUNIT 33 | def_tristate m 34 | endif 35 | -------------------------------------------------------------------------------- /modules/net/quic/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later 2 | # 3 | # Makefile for QUIC support code. 4 | # 5 | 6 | LINUXINCLUDE += -I$(ROOTDIR)/include 7 | 8 | obj-$(CONFIG_IP_QUIC) += quic.o 9 | 10 | quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \ 11 | cong.o pnspace.o crypto.o timer.o frame.o packet.o output.o \ 12 | input.o 13 | 14 | ifdef CONFIG_KUNIT 15 | obj-$(CONFIG_IP_QUIC_TEST) += quic_unit_test.o 16 | quic_unit_test-y := test/unit_test.o 17 | endif 18 | 19 | ifdef CONFIG_NET_HANDSHAKE 20 | obj-$(CONFIG_IP_QUIC_TEST) += quic_sample_test.o 21 | quic_sample_test-y := test/sample_test.o 22 | endif 23 | -------------------------------------------------------------------------------- /modules/net/quic/common.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Initialization/cleanup for QUIC protocol support. 8 | * 9 | * Written or modified by: 10 | * Xin Long 11 | */ 12 | 13 | #include "common.h" 14 | 15 | #define QUIC_VARINT_1BYTE_MAX 0x3fULL 16 | #define QUIC_VARINT_2BYTE_MAX 0x3fffULL 17 | #define QUIC_VARINT_4BYTE_MAX 0x3fffffffULL 18 | 19 | #define QUIC_VARINT_2BYTE_PREFIX 0x40 20 | #define QUIC_VARINT_4BYTE_PREFIX 0x80 21 | #define QUIC_VARINT_8BYTE_PREFIX 0xc0 22 | 23 | #define QUIC_VARINT_LENGTH(p) BIT((*(p)) >> 6) 24 | #define QUIC_VARINT_VALUE_MASK 0x3f 25 | 26 | static struct quic_hash_table quic_hash_tables[QUIC_HT_MAX_TABLES]; 27 | 28 | struct quic_hash_head *quic_sock_hash(u32 hash) 29 | { 30 | return &quic_hash_tables[QUIC_HT_SOCK].hash[hash]; 31 | } 32 | 33 | struct quic_hash_head *quic_sock_head(struct net *net, union quic_addr *s, union quic_addr *d) 34 | { 35 | struct quic_hash_table *ht = &quic_hash_tables[QUIC_HT_SOCK]; 36 | 37 | return &ht->hash[quic_ahash(net, s, d) & (ht->size - 1)]; 38 | } 39 | 40 | struct quic_hash_head *quic_listen_sock_head(struct net *net, u16 port) 41 | { 42 | struct quic_hash_table *ht = &quic_hash_tables[QUIC_HT_SOCK]; 43 | 44 | return &ht->hash[port & (ht->size - 1)]; 45 | } 46 | 47 | struct quic_hash_head *quic_bind_port_head(struct net *net, u16 port) 48 | { 49 | struct quic_hash_table *ht = &quic_hash_tables[QUIC_HT_BIND_PORT]; 50 | 51 | return &ht->hash[port & (ht->size - 1)]; 52 | } 53 | 54 | struct quic_hash_head *quic_source_conn_id_head(struct net *net, u8 *scid) 55 | { 56 | struct quic_hash_table *ht = &quic_hash_tables[QUIC_HT_CONNECTION_ID]; 57 | 58 | return &ht->hash[jhash(scid, 4, 0) & (ht->size - 1)]; 59 | } 60 | 61 | struct quic_hash_head *quic_udp_sock_head(struct net *net, u16 port) 62 | { 63 | struct quic_hash_table *ht = &quic_hash_tables[QUIC_HT_UDP_SOCK]; 64 | 65 | return &ht->hash[port & (ht->size - 1)]; 66 | } 67 | 68 | struct quic_hash_head *quic_stream_head(struct quic_hash_table *ht, s64 stream_id) 69 | { 70 | return &ht->hash[stream_id & (ht->size - 1)]; 71 | } 72 | 73 | void quic_hash_tables_destroy(void) 74 | { 75 | struct quic_hash_table *ht; 76 | int table; 77 | 78 | for (table = 0; table < QUIC_HT_MAX_TABLES; table++) { 79 | ht = &quic_hash_tables[table]; 80 | ht->size = QUIC_HT_SIZE; 81 | kfree(ht->hash); 82 | } 83 | } 84 | 85 | int quic_hash_tables_init(void) 86 | { 87 | struct quic_hash_head *head; 88 | struct quic_hash_table *ht; 89 | int table, i; 90 | 91 | for (table = 0; table < QUIC_HT_MAX_TABLES; table++) { 92 | ht = &quic_hash_tables[table]; 93 | ht->size = QUIC_HT_SIZE; 94 | head = kmalloc_array(ht->size, sizeof(*head), GFP_KERNEL); 95 | if (!head) { 96 | quic_hash_tables_destroy(); 97 | return -ENOMEM; 98 | } 99 | for (i = 0; i < ht->size; i++) { 100 | spin_lock_init(&head[i].lock); 101 | INIT_HLIST_HEAD(&head[i].head); 102 | } 103 | ht->hash = head; 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | union quic_var { 110 | u8 u8; 111 | __be16 be16; 112 | __be32 be32; 113 | __be64 be64; 114 | }; 115 | 116 | u8 quic_var_len(u64 n) 117 | { 118 | if (n <= QUIC_VARINT_1BYTE_MAX) 119 | return 1; 120 | if (n <= QUIC_VARINT_2BYTE_MAX) 121 | return 2; 122 | if (n <= QUIC_VARINT_4BYTE_MAX) 123 | return 4; 124 | return 8; 125 | } 126 | 127 | u8 quic_get_var(u8 **pp, u32 *plen, u64 *val) 128 | { 129 | union quic_var n = {}; 130 | u8 *p = *pp, len; 131 | u64 v = 0; 132 | 133 | if (!*plen) 134 | return 0; 135 | 136 | len = QUIC_VARINT_LENGTH(p); 137 | if (*plen < len) 138 | return 0; 139 | 140 | switch (len) { 141 | case 1: 142 | v = *p; 143 | break; 144 | case 2: 145 | memcpy(&n.be16, p, 2); 146 | n.u8 &= QUIC_VARINT_VALUE_MASK; 147 | v = be16_to_cpu(n.be16); 148 | break; 149 | case 4: 150 | memcpy(&n.be32, p, 4); 151 | n.u8 &= QUIC_VARINT_VALUE_MASK; 152 | v = be32_to_cpu(n.be32); 153 | break; 154 | case 8: 155 | memcpy(&n.be64, p, 8); 156 | n.u8 &= QUIC_VARINT_VALUE_MASK; 157 | v = be64_to_cpu(n.be64); 158 | break; 159 | default: 160 | return 0; 161 | } 162 | 163 | *plen -= len; 164 | *pp = p + len; 165 | *val = v; 166 | return len; 167 | } 168 | 169 | u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len) 170 | { 171 | union quic_var n; 172 | u8 *p = *pp; 173 | u64 v = 0; 174 | 175 | if (*plen < len) 176 | return 0; 177 | *plen -= len; 178 | 179 | switch (len) { 180 | case 1: 181 | v = *p; 182 | break; 183 | case 2: 184 | memcpy(&n.be16, p, 2); 185 | v = be16_to_cpu(n.be16); 186 | break; 187 | case 3: 188 | n.be32 = 0; 189 | memcpy(((u8 *)&n.be32) + 1, p, 3); 190 | v = be32_to_cpu(n.be32); 191 | break; 192 | case 4: 193 | memcpy(&n.be32, p, 4); 194 | v = be32_to_cpu(n.be32); 195 | break; 196 | case 8: 197 | memcpy(&n.be64, p, 8); 198 | v = be64_to_cpu(n.be64); 199 | break; 200 | default: 201 | return 0; 202 | } 203 | *pp = p + len; 204 | *val = v; 205 | return len; 206 | } 207 | 208 | u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len) 209 | { 210 | if (*plen < len) 211 | return 0; 212 | 213 | memcpy(data, *pp, len); 214 | *pp += len; 215 | *plen -= len; 216 | 217 | return len; 218 | } 219 | 220 | u8 *quic_put_var(u8 *p, u64 num) 221 | { 222 | union quic_var n; 223 | 224 | if (num <= QUIC_VARINT_1BYTE_MAX) { 225 | *p++ = (u8)(num & 0xff); 226 | return p; 227 | } 228 | if (num <= QUIC_VARINT_2BYTE_MAX) { 229 | n.be16 = cpu_to_be16((u16)num); 230 | *((__be16 *)p) = n.be16; 231 | *p |= QUIC_VARINT_2BYTE_PREFIX; 232 | return p + 2; 233 | } 234 | if (num <= QUIC_VARINT_4BYTE_MAX) { 235 | n.be32 = cpu_to_be32((u32)num); 236 | *((__be32 *)p) = n.be32; 237 | *p |= QUIC_VARINT_4BYTE_PREFIX; 238 | return p + 4; 239 | } 240 | n.be64 = cpu_to_be64(num); 241 | *((__be64 *)p) = n.be64; 242 | *p |= QUIC_VARINT_8BYTE_PREFIX; 243 | return p + 8; 244 | } 245 | 246 | u8 *quic_put_int(u8 *p, u64 num, u8 len) 247 | { 248 | union quic_var n; 249 | 250 | switch (len) { 251 | case 1: 252 | *p++ = (u8)(num & 0xff); 253 | return p; 254 | case 2: 255 | n.be16 = cpu_to_be16((u16)(num & 0xffff)); 256 | *((__be16 *)p) = n.be16; 257 | return p + 2; 258 | case 4: 259 | n.be32 = cpu_to_be32((u32)num); 260 | *((__be32 *)p) = n.be32; 261 | return p + 4; 262 | default: 263 | return NULL; 264 | } 265 | } 266 | 267 | u8 *quic_put_varint(u8 *p, u64 num, u8 len) 268 | { 269 | union quic_var n; 270 | 271 | switch (len) { 272 | case 1: 273 | *p++ = (u8)(num & 0xff); 274 | return p; 275 | case 2: 276 | n.be16 = cpu_to_be16((u16)(num & 0xffff)); 277 | *((__be16 *)p) = n.be16; 278 | *p |= QUIC_VARINT_2BYTE_PREFIX; 279 | return p + 2; 280 | case 4: 281 | n.be32 = cpu_to_be32((u32)num); 282 | *((__be32 *)p) = n.be32; 283 | *p |= QUIC_VARINT_4BYTE_PREFIX; 284 | return p + 4; 285 | default: 286 | return NULL; 287 | } 288 | } 289 | 290 | u8 *quic_put_data(u8 *p, u8 *data, u32 len) 291 | { 292 | if (!len) 293 | return p; 294 | 295 | memcpy(p, data, len); 296 | return p + len; 297 | } 298 | 299 | u8 *quic_put_param(u8 *p, u16 id, u64 value) 300 | { 301 | p = quic_put_var(p, id); 302 | p = quic_put_var(p, quic_var_len(value)); 303 | return quic_put_var(p, value); 304 | } 305 | 306 | u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen) 307 | { 308 | u64 valuelen; 309 | 310 | if (!quic_get_var(pp, plen, &valuelen)) 311 | return 0; 312 | 313 | if (*plen < valuelen) 314 | return 0; 315 | 316 | if (!quic_get_var(pp, plen, pdest)) 317 | return 0; 318 | 319 | return (u8)valuelen; 320 | } 321 | 322 | s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n) 323 | { 324 | s64 expected = max_pkt_num + 1; 325 | s64 win = BIT_ULL(n * 8); 326 | s64 hwin = win / 2; 327 | s64 mask = win - 1; 328 | s64 cand; 329 | 330 | cand = (expected & ~mask) | pkt_num; 331 | if (cand <= expected - hwin) 332 | return cand + win; 333 | if (cand > expected + hwin && cand >= win) 334 | return cand - win; 335 | return cand; 336 | } 337 | 338 | int quic_data_dup(struct quic_data *to, u8 *data, u32 len) 339 | { 340 | if (!len) 341 | return 0; 342 | 343 | data = kmemdup(data, len, GFP_ATOMIC); 344 | if (!data) 345 | return -ENOMEM; 346 | 347 | kfree(to->data); 348 | to->data = data; 349 | to->len = len; 350 | return 0; 351 | } 352 | 353 | int quic_data_append(struct quic_data *to, u8 *data, u32 len) 354 | { 355 | u8 *p; 356 | 357 | if (!len) 358 | return 0; 359 | 360 | p = kzalloc(to->len + len, GFP_ATOMIC); 361 | if (!p) 362 | return -ENOMEM; 363 | p = quic_put_data(p, to->data, to->len); 364 | p = quic_put_data(p, data, len); 365 | 366 | kfree(to->data); 367 | to->len = to->len + len; 368 | to->data = p - to->len; 369 | return 0; 370 | } 371 | 372 | int quic_data_has(struct quic_data *d1, struct quic_data *d2) 373 | { 374 | struct quic_data d; 375 | u64 length; 376 | u32 len; 377 | u8 *p; 378 | 379 | for (p = d1->data, len = d1->len; len; len -= length, p += length) { 380 | quic_get_int(&p, &len, &length, 1); 381 | quic_data(&d, p, length); 382 | if (!quic_data_cmp(&d, d2)) 383 | return 1; 384 | } 385 | return 0; 386 | } 387 | 388 | int quic_data_match(struct quic_data *d1, struct quic_data *d2) 389 | { 390 | struct quic_data d; 391 | u64 length; 392 | u32 len; 393 | u8 *p; 394 | 395 | for (p = d1->data, len = d1->len; len; len -= length, p += length) { 396 | quic_get_int(&p, &len, &length, 1); 397 | quic_data(&d, p, length); 398 | if (quic_data_has(d2, &d)) 399 | return 1; 400 | } 401 | return 0; 402 | } 403 | 404 | void quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from) 405 | { 406 | struct quic_data d; 407 | u8 *data = to, *p; 408 | u64 length; 409 | u32 len; 410 | 411 | for (p = from->data, len = from->len; len; len -= length, p += length) { 412 | quic_get_int(&p, &len, &length, 1); 413 | quic_data(&d, p, length); 414 | data = quic_put_data(data, d.data, d.len); 415 | if (len - length) 416 | data = quic_put_int(data, ',', 1); 417 | } 418 | *plen = data - to; 419 | } 420 | 421 | void quic_data_from_string(struct quic_data *to, u8 *from, u32 len) 422 | { 423 | struct quic_data d; 424 | u8 *p = to->data; 425 | 426 | to->len = 0; 427 | while (len) { 428 | d.data = p++; 429 | d.len = 1; 430 | while (len && *from == ' ') { 431 | from++; 432 | len--; 433 | } 434 | while (len) { 435 | if (*from == ',') { 436 | from++; 437 | len--; 438 | break; 439 | } 440 | *p++ = *from++; 441 | len--; 442 | d.len++; 443 | } 444 | *d.data = (u8)(d.len - 1); 445 | to->len += d.len; 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /modules/net/quic/common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #define QUIC_HT_SIZE 64 15 | 16 | #define QUIC_MAX_ACK_DELAY (16384 * 1000) 17 | #define QUIC_DEF_ACK_DELAY 25000 18 | 19 | #define QUIC_STREAM_BIT_FIN 0x01 20 | #define QUIC_STREAM_BIT_LEN 0x02 21 | #define QUIC_STREAM_BIT_OFF 0x04 22 | #define QUIC_STREAM_BIT_MASK 0x08 23 | 24 | #define QUIC_CONN_ID_MAX_LEN 20 25 | #define QUIC_CONN_ID_DEF_LEN 8 26 | 27 | struct quic_conn_id { 28 | u8 data[QUIC_CONN_ID_MAX_LEN]; 29 | u8 len; 30 | }; 31 | 32 | static inline void quic_conn_id_update(struct quic_conn_id *conn_id, u8 *data, u32 len) 33 | { 34 | memcpy(conn_id->data, data, len); 35 | conn_id->len = (u8)len; 36 | } 37 | 38 | struct quic_skb_cb { 39 | void (*crypto_done)(struct sk_buff *skb, int err); 40 | union { 41 | struct quic_conn_id *conn_id; 42 | struct sk_buff *last; 43 | }; 44 | s64 number_max; 45 | s64 number; 46 | u16 errcode; 47 | u16 length; 48 | u32 time; 49 | 50 | u16 number_offset; 51 | u16 udph_offset; 52 | u8 number_len; 53 | u8 level; 54 | 55 | u8 key_update:1; 56 | u8 key_phase:1; 57 | u8 resume:1; 58 | u8 path:1; 59 | u8 ecn:2; 60 | }; 61 | 62 | #define QUIC_SKB_CB(skb) ((struct quic_skb_cb *)&((skb)->cb[0])) 63 | 64 | static inline struct udphdr *quic_udphdr(const struct sk_buff *skb) 65 | { 66 | return (struct udphdr *)(skb->head + QUIC_SKB_CB(skb)->udph_offset); 67 | } 68 | 69 | struct quichdr { 70 | #if defined(__LITTLE_ENDIAN_BITFIELD) 71 | __u8 pnl:2, 72 | key:1, 73 | reserved:2, 74 | spin:1, 75 | fixed:1, 76 | form:1; 77 | #elif defined(__BIG_ENDIAN_BITFIELD) 78 | __u8 form:1, 79 | fixed:1, 80 | spin:1, 81 | reserved:2, 82 | key:1, 83 | pnl:2; 84 | #endif 85 | }; 86 | 87 | static inline struct quichdr *quic_hdr(struct sk_buff *skb) 88 | { 89 | return (struct quichdr *)skb_transport_header(skb); 90 | } 91 | 92 | struct quichshdr { 93 | #if defined(__LITTLE_ENDIAN_BITFIELD) 94 | __u8 pnl:2, 95 | reserved:2, 96 | type:2, 97 | fixed:1, 98 | form:1; 99 | #elif defined(__BIG_ENDIAN_BITFIELD) 100 | __u8 form:1, 101 | fixed:1, 102 | type:2, 103 | reserved:2, 104 | pnl:2; 105 | #endif 106 | }; 107 | 108 | static inline struct quichshdr *quic_hshdr(struct sk_buff *skb) 109 | { 110 | return (struct quichshdr *)skb_transport_header(skb); 111 | } 112 | 113 | union quic_addr { 114 | struct sockaddr_in6 v6; 115 | struct sockaddr_in v4; 116 | struct sockaddr sa; 117 | }; 118 | 119 | static inline union quic_addr *quic_addr(const void *addr) 120 | { 121 | return (union quic_addr *)addr; 122 | } 123 | 124 | struct quic_hash_head { 125 | spinlock_t lock; /* protect the 'head' member access */ 126 | struct hlist_head head; 127 | }; 128 | 129 | struct quic_hash_table { 130 | struct quic_hash_head *hash; 131 | int size; 132 | }; 133 | 134 | enum { 135 | QUIC_HT_SOCK, 136 | QUIC_HT_UDP_SOCK, 137 | QUIC_HT_CONNECTION_ID, 138 | QUIC_HT_BIND_PORT, 139 | QUIC_HT_MAX_TABLES, 140 | }; 141 | 142 | static inline u32 quic_shash(const struct net *net, const union quic_addr *a) 143 | { 144 | u32 addr = (a->sa.sa_family == AF_INET6) ? jhash(&a->v6.sin6_addr, 16, 0) 145 | : (__force u32)a->v4.sin_addr.s_addr; 146 | 147 | return jhash_3words(addr, (__force u32)a->v4.sin_port, net_hash_mix(net), 0); 148 | } 149 | 150 | static inline u32 quic_ahash(const struct net *net, const union quic_addr *s, 151 | const union quic_addr *d) 152 | { 153 | u32 ports = ((__force u32)s->v4.sin_port) << 16 | (__force u32)d->v4.sin_port; 154 | u32 saddr = (s->sa.sa_family == AF_INET6) ? jhash(&s->v6.sin6_addr, 16, 0) 155 | : (__force u32)s->v4.sin_addr.s_addr; 156 | u32 daddr = (d->sa.sa_family == AF_INET6) ? jhash(&d->v6.sin6_addr, 16, 0) 157 | : (__force u32)d->v4.sin_addr.s_addr; 158 | 159 | return jhash_3words(saddr, ports, net_hash_mix(net), daddr); 160 | } 161 | 162 | struct quic_data { 163 | u8 *data; 164 | u32 len; 165 | }; 166 | 167 | static inline struct quic_data *quic_data(struct quic_data *d, u8 *data, u32 len) 168 | { 169 | d->data = data; 170 | d->len = len; 171 | return d; 172 | } 173 | 174 | static inline int quic_data_cmp(struct quic_data *d1, struct quic_data *d2) 175 | { 176 | return d1->len != d2->len || memcmp(d1->data, d2->data, d1->len); 177 | } 178 | 179 | static inline void quic_data_free(struct quic_data *d) 180 | { 181 | kfree(d->data); 182 | d->data = NULL; 183 | d->len = 0; 184 | } 185 | 186 | struct quic_hash_head *quic_sock_head(struct net *net, union quic_addr *s, union quic_addr *d); 187 | struct quic_hash_head *quic_listen_sock_head(struct net *net, u16 port); 188 | struct quic_hash_head *quic_bind_port_head(struct net *net, u16 port); 189 | 190 | struct quic_hash_head *quic_stream_head(struct quic_hash_table *ht, s64 stream_id); 191 | struct quic_hash_head *quic_source_conn_id_head(struct net *net, u8 *scid); 192 | struct quic_hash_head *quic_udp_sock_head(struct net *net, u16 port); 193 | 194 | struct quic_hash_head *quic_sock_hash(u32 hash); 195 | void quic_hash_tables_destroy(void); 196 | int quic_hash_tables_init(void); 197 | 198 | u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len); 199 | u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len); 200 | s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n); 201 | u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen); 202 | u8 quic_get_var(u8 **pp, u32 *plen, u64 *val); 203 | u8 quic_var_len(u64 n); 204 | 205 | u8 *quic_put_param(u8 *p, u16 id, u64 value); 206 | u8 *quic_put_data(u8 *p, u8 *data, u32 len); 207 | u8 *quic_put_varint(u8 *p, u64 num, u8 len); 208 | u8 *quic_put_int(u8 *p, u64 num, u8 len); 209 | u8 *quic_put_var(u8 *p, u64 num); 210 | 211 | void quic_data_from_string(struct quic_data *to, u8 *from, u32 len); 212 | void quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from); 213 | 214 | int quic_data_match(struct quic_data *d1, struct quic_data *d2); 215 | int quic_data_append(struct quic_data *to, u8 *data, u32 len); 216 | int quic_data_has(struct quic_data *d1, struct quic_data *d2); 217 | int quic_data_dup(struct quic_data *to, u8 *data, u32 len); 218 | -------------------------------------------------------------------------------- /modules/net/quic/cong.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_RTT_INIT 333000U 12 | #define QUIC_RTO_MIN 30000U 13 | #define QUIC_RTO_MAX 6000000U 14 | 15 | enum quic_cong_state { 16 | QUIC_CONG_SLOW_START, 17 | QUIC_CONG_RECOVERY_PERIOD, 18 | QUIC_CONG_CONGESTION_AVOIDANCE, 19 | }; 20 | 21 | struct quic_cong { 22 | u32 smoothed_rtt; 23 | u32 latest_rtt; 24 | u32 min_rtt; 25 | u32 rttvar; 26 | u32 pto; 27 | 28 | u32 recovery_time; 29 | u32 max_ack_delay; 30 | u32 pacing_rate; 31 | u64 pacing_time; /* planned time to send next packet */ 32 | u32 time; /* current time cache */ 33 | 34 | u32 max_window; 35 | u32 min_window; 36 | u32 ssthresh; 37 | u32 window; 38 | u32 mss; 39 | 40 | struct quic_cong_ops *ops; 41 | u64 priv[8]; 42 | 43 | u8 min_rtt_valid; 44 | u8 is_rtt_set; 45 | u8 state; 46 | }; 47 | 48 | struct quic_cong_ops { 49 | /* required */ 50 | void (*on_packet_acked)(struct quic_cong *cong, u32 time, u32 bytes, s64 number); 51 | void (*on_packet_lost)(struct quic_cong *cong, u32 time, u32 bytes, s64 number); 52 | void (*on_process_ecn)(struct quic_cong *cong); 53 | void (*on_init)(struct quic_cong *cong); 54 | 55 | /* optional */ 56 | void (*on_packet_sent)(struct quic_cong *cong, u32 time, u32 bytes, s64 number); 57 | void (*on_ack_recv)(struct quic_cong *cong, u32 bytes, u32 max_rate); 58 | void (*on_rtt_update)(struct quic_cong *cong); 59 | }; 60 | 61 | static inline void quic_cong_set_mss(struct quic_cong *cong, u32 mss) 62 | { 63 | if (cong->mss == mss) 64 | return; 65 | 66 | /* rfc9002#section-7.2: Initial and Minimum Congestion Window */ 67 | cong->mss = mss; 68 | cong->min_window = max(min(mss * 10, 14720U), mss * 2); 69 | 70 | if (cong->window < cong->min_window) 71 | cong->window = cong->min_window; 72 | } 73 | 74 | static inline void *quic_cong_priv(struct quic_cong *cong) 75 | { 76 | return (void *)cong->priv; 77 | } 78 | 79 | void quic_cong_on_packet_acked(struct quic_cong *cong, u32 time, u32 bytes, s64 number); 80 | void quic_cong_on_packet_lost(struct quic_cong *cong, u32 time, u32 bytes, s64 number); 81 | void quic_cong_on_process_ecn(struct quic_cong *cong); 82 | 83 | void quic_cong_on_packet_sent(struct quic_cong *cong, u32 time, u32 bytes, s64 number); 84 | void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u32 max_rate); 85 | void quic_cong_rtt_update(struct quic_cong *cong, u32 time, u32 ack_delay); 86 | 87 | void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt); 88 | void quic_cong_set_algo(struct quic_cong *cong, u8 algo); 89 | void quic_cong_init(struct quic_cong *cong); 90 | -------------------------------------------------------------------------------- /modules/net/quic/connid.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Initialization/cleanup for QUIC protocol support. 8 | * 9 | * Written or modified by: 10 | * Xin Long 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #include "common.h" 17 | #include "connid.h" 18 | 19 | struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len) 20 | { 21 | struct quic_hash_head *head = quic_source_conn_id_head(net, scid); 22 | struct quic_source_conn_id *s_conn_id; 23 | 24 | spin_lock(&head->lock); 25 | hlist_for_each_entry(s_conn_id, &head->head, node) { 26 | if (net == sock_net(s_conn_id->sk) && s_conn_id->common.id.len == len && 27 | !memcmp(scid, &s_conn_id->common.id.data, s_conn_id->common.id.len)) 28 | break; 29 | } 30 | 31 | spin_unlock(&head->lock); 32 | return &s_conn_id->common.id; 33 | } 34 | 35 | struct quic_conn_id *quic_conn_id_get(struct quic_conn_id_set *id_set, u8 *scid, u32 len) 36 | { 37 | struct quic_common_conn_id *common; 38 | 39 | list_for_each_entry(common, &id_set->head, list) 40 | if (common->id.len == len && !memcmp(scid, &common->id.data, common->id.len)) 41 | return &common->id; 42 | return NULL; 43 | } 44 | 45 | bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token) 46 | { 47 | struct quic_common_conn_id *common; 48 | struct quic_dest_conn_id *dcid; 49 | 50 | dcid = (struct quic_dest_conn_id *)id_set->active; 51 | if (!memcmp(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN)) /* fast path */ 52 | return true; 53 | 54 | list_for_each_entry(common, &id_set->head, list) { 55 | dcid = (struct quic_dest_conn_id *)common; 56 | if (common == id_set->active) 57 | continue; 58 | if (!memcmp(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN)) 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | static void quic_source_conn_id_free_rcu(struct rcu_head *head) 65 | { 66 | struct quic_source_conn_id *s_conn_id; 67 | 68 | s_conn_id = container_of(head, struct quic_source_conn_id, rcu); 69 | kfree(s_conn_id); 70 | } 71 | 72 | static void quic_source_conn_id_free(struct quic_source_conn_id *s_conn_id) 73 | { 74 | u8 *data = s_conn_id->common.id.data; 75 | struct quic_hash_head *head; 76 | 77 | if (!hlist_unhashed(&s_conn_id->node)) { 78 | head = quic_source_conn_id_head(sock_net(s_conn_id->sk), data); 79 | spin_lock(&head->lock); 80 | hlist_del_init(&s_conn_id->node); 81 | spin_unlock(&head->lock); 82 | } 83 | 84 | call_rcu(&s_conn_id->rcu, quic_source_conn_id_free_rcu); 85 | } 86 | 87 | static void quic_conn_id_del(struct quic_common_conn_id *common) 88 | { 89 | list_del(&common->list); 90 | if (!common->hashed) { 91 | kfree(common); 92 | return; 93 | } 94 | quic_source_conn_id_free((struct quic_source_conn_id *)common); 95 | } 96 | 97 | int quic_conn_id_add(struct quic_conn_id_set *id_set, 98 | struct quic_conn_id *conn_id, u32 number, void *data) 99 | { 100 | struct quic_source_conn_id *s_conn_id; 101 | struct quic_dest_conn_id *d_conn_id; 102 | struct quic_common_conn_id *common; 103 | struct quic_hash_head *head; 104 | struct list_head *list; 105 | 106 | /* find the position */ 107 | list = &id_set->head; 108 | list_for_each_entry(common, list, list) { 109 | if (number == common->number) 110 | return 0; 111 | if (number < common->number) { 112 | list = &common->list; 113 | break; 114 | } 115 | } 116 | 117 | /* create and insert the node */ 118 | if (conn_id->len > QUIC_CONN_ID_MAX_LEN) 119 | return -EINVAL; 120 | common = kzalloc(id_set->entry_size, GFP_ATOMIC); 121 | if (!common) 122 | return -ENOMEM; 123 | common->id = *conn_id; 124 | common->number = number; 125 | if (id_set->entry_size == sizeof(struct quic_dest_conn_id)) { 126 | if (data) { 127 | d_conn_id = (struct quic_dest_conn_id *)common; 128 | memcpy(d_conn_id->token, data, QUIC_CONN_ID_TOKEN_LEN); 129 | } 130 | } else { 131 | common->hashed = 1; 132 | s_conn_id = (struct quic_source_conn_id *)common; 133 | s_conn_id->sk = data; 134 | 135 | head = quic_source_conn_id_head(sock_net(s_conn_id->sk), common->id.data); 136 | spin_lock(&head->lock); 137 | hlist_add_head(&s_conn_id->node, &head->head); 138 | spin_unlock(&head->lock); 139 | } 140 | list_add_tail(&common->list, list); 141 | 142 | /* increase count with the out-of-order node considered */ 143 | if (number == quic_conn_id_last_number(id_set) + 1) { 144 | if (!id_set->active) 145 | id_set->active = common; 146 | id_set->count++; 147 | 148 | list_for_each_entry_continue(common, &id_set->head, list) { 149 | if (common->number != ++number) 150 | break; 151 | id_set->count++; 152 | } 153 | } 154 | return 0; 155 | } 156 | 157 | void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number) 158 | { 159 | struct quic_common_conn_id *common, *tmp; 160 | struct list_head *list; 161 | 162 | list = &id_set->head; 163 | list_for_each_entry_safe(common, tmp, list, list) { 164 | if (common->number <= number) { 165 | if (id_set->active == common) 166 | id_set->active = tmp; 167 | quic_conn_id_del(common); 168 | id_set->count--; 169 | } 170 | } 171 | } 172 | 173 | struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set, u32 number) 174 | { 175 | struct quic_common_conn_id *common; 176 | 177 | list_for_each_entry(common, &id_set->head, list) 178 | if (common->number == number) 179 | return &common->id; 180 | return NULL; 181 | } 182 | 183 | void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source) 184 | { 185 | id_set->entry_size = source ? sizeof(struct quic_source_conn_id) 186 | : sizeof(struct quic_dest_conn_id); 187 | INIT_LIST_HEAD(&id_set->head); 188 | } 189 | 190 | void quic_conn_id_set_free(struct quic_conn_id_set *id_set) 191 | { 192 | struct quic_common_conn_id *common, *tmp; 193 | 194 | list_for_each_entry_safe(common, tmp, &id_set->head, list) 195 | quic_conn_id_del(common); 196 | id_set->count = 0; 197 | id_set->active = NULL; 198 | } 199 | 200 | void quic_conn_id_get_param(struct quic_conn_id_set *id_set, struct quic_transport_param *p) 201 | { 202 | p->active_connection_id_limit = id_set->max_count; 203 | } 204 | 205 | void quic_conn_id_set_param(struct quic_conn_id_set *id_set, struct quic_transport_param *p) 206 | { 207 | id_set->max_count = p->active_connection_id_limit; 208 | } 209 | -------------------------------------------------------------------------------- /modules/net/quic/connid.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_CONN_ID_LIMIT 8 12 | #define QUIC_CONN_ID_DEF 7 13 | #define QUIC_CONN_ID_LEAST 2 14 | 15 | #define QUIC_CONN_ID_TOKEN_LEN 16 16 | 17 | struct quic_common_conn_id { 18 | struct quic_conn_id id; 19 | struct list_head list; 20 | u32 number; 21 | u8 hashed; 22 | }; 23 | 24 | struct quic_source_conn_id { 25 | struct quic_common_conn_id common; 26 | struct hlist_node node; 27 | struct rcu_head rcu; 28 | struct sock *sk; 29 | }; 30 | 31 | struct quic_dest_conn_id { 32 | struct quic_common_conn_id common; 33 | u8 token[QUIC_CONN_ID_TOKEN_LEN]; 34 | }; 35 | 36 | struct quic_conn_id_set { 37 | struct quic_common_conn_id *active; 38 | struct quic_common_conn_id *alt; 39 | struct list_head head; 40 | u8 entry_size; 41 | u8 max_count; 42 | u8 count; 43 | }; 44 | 45 | static inline u32 quic_conn_id_first_number(struct quic_conn_id_set *id_set) 46 | { 47 | struct quic_common_conn_id *common; 48 | 49 | common = list_first_entry(&id_set->head, struct quic_common_conn_id, list); 50 | return common->number; 51 | } 52 | 53 | static inline u32 quic_conn_id_last_number(struct quic_conn_id_set *id_set) 54 | { 55 | return quic_conn_id_first_number(id_set) + id_set->count - 1; 56 | } 57 | 58 | static inline void quic_conn_id_generate(struct quic_conn_id *conn_id) 59 | { 60 | get_random_bytes(conn_id->data, QUIC_CONN_ID_DEF_LEN); 61 | conn_id->len = QUIC_CONN_ID_DEF_LEN; 62 | } 63 | 64 | static inline bool quic_conn_id_select_alt(struct quic_conn_id_set *id_set, bool active) 65 | { 66 | if (id_set->alt) 67 | return true; 68 | if (active) { 69 | id_set->alt = id_set->active; 70 | return true; 71 | } 72 | if (id_set->active->number != quic_conn_id_last_number(id_set)) { 73 | id_set->alt = list_next_entry(id_set->active, list); 74 | return true; 75 | } 76 | if (id_set->active->number == quic_conn_id_first_number(id_set)) { 77 | id_set->alt = id_set->active; 78 | return true; 79 | } 80 | return false; 81 | } 82 | 83 | static inline void quic_conn_id_set_alt(struct quic_conn_id_set *id_set, struct quic_conn_id *alt) 84 | { 85 | id_set->alt = (struct quic_common_conn_id *)alt; 86 | } 87 | 88 | static inline void quic_conn_id_swap_active(struct quic_conn_id_set *id_set) 89 | { 90 | void *active = id_set->active; 91 | 92 | id_set->active = id_set->alt; 93 | id_set->alt = active; 94 | } 95 | 96 | static inline struct quic_conn_id *quic_conn_id_choose(struct quic_conn_id_set *id_set, u8 alt) 97 | { 98 | return (alt && id_set->alt) ? &id_set->alt->id : &id_set->active->id; 99 | } 100 | 101 | static inline struct quic_conn_id *quic_conn_id_active(struct quic_conn_id_set *id_set) 102 | { 103 | return &id_set->active->id; 104 | } 105 | 106 | static inline void quic_conn_id_set_active(struct quic_conn_id_set *id_set, 107 | struct quic_conn_id *active) 108 | { 109 | id_set->active = (struct quic_common_conn_id *)active; 110 | } 111 | 112 | static inline u32 quic_conn_id_number(struct quic_conn_id *conn_id) 113 | { 114 | return ((struct quic_common_conn_id *)conn_id)->number; 115 | } 116 | 117 | static inline struct sock *quic_conn_id_sk(struct quic_conn_id *conn_id) 118 | { 119 | return ((struct quic_source_conn_id *)conn_id)->sk; 120 | } 121 | 122 | static inline void quic_conn_id_set_token(struct quic_conn_id *conn_id, u8 *token) 123 | { 124 | memcpy(((struct quic_dest_conn_id *)conn_id)->token, token, QUIC_CONN_ID_TOKEN_LEN); 125 | } 126 | 127 | static inline int quic_conn_id_cmp(struct quic_conn_id *a, struct quic_conn_id *b) 128 | { 129 | return a->len != b->len || memcmp(a->data, b->data, a->len); 130 | } 131 | 132 | int quic_conn_id_add(struct quic_conn_id_set *id_set, struct quic_conn_id *conn_id, 133 | u32 number, void *data); 134 | bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token); 135 | void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number); 136 | 137 | struct quic_conn_id *quic_conn_id_get(struct quic_conn_id_set *id_set, u8 *scid, u32 len); 138 | struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set, u32 number); 139 | struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len); 140 | 141 | void quic_conn_id_get_param(struct quic_conn_id_set *id_set, struct quic_transport_param *p); 142 | void quic_conn_id_set_param(struct quic_conn_id_set *id_set, struct quic_transport_param *p); 143 | void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source); 144 | void quic_conn_id_set_free(struct quic_conn_id_set *id_set); 145 | -------------------------------------------------------------------------------- /modules/net/quic/crypto.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_TAG_LEN 16 12 | #define QUIC_IV_LEN 12 13 | #define QUIC_KEY_LEN 32 14 | #define QUIC_SECRET_LEN 48 15 | 16 | #define QUIC_TOKEN_FLAG_REGULAR 0 17 | #define QUIC_TOKEN_FLAG_RETRY 1 18 | #define QUIC_TOKEN_TIMEOUT_REGULAR 3000000 19 | #define QUIC_TOKEN_TIMEOUT_RETRY 600000000 20 | 21 | struct quic_cipher { 22 | u32 secretlen; 23 | u32 keylen; 24 | 25 | char *shash; 26 | char *aead; 27 | char *skc; 28 | }; 29 | 30 | struct quic_crypto { 31 | struct crypto_skcipher *tx_hp_tfm; 32 | struct crypto_skcipher *rx_hp_tfm; 33 | struct crypto_shash *secret_tfm; 34 | struct crypto_aead *tx_tfm[2]; 35 | struct crypto_aead *rx_tfm[2]; 36 | struct crypto_aead *tag_tfm; 37 | struct quic_cipher *cipher; 38 | u32 cipher_type; 39 | 40 | u8 tx_secret[QUIC_SECRET_LEN]; 41 | u8 rx_secret[QUIC_SECRET_LEN]; 42 | u8 tx_iv[2][QUIC_IV_LEN]; 43 | u8 rx_iv[2][QUIC_IV_LEN]; 44 | 45 | u32 key_update_send_time; 46 | u32 key_update_time; 47 | u32 version; 48 | 49 | u8 ticket_ready:1; 50 | u8 key_pending:1; 51 | u8 send_ready:1; 52 | u8 recv_ready:1; 53 | u8 key_phase:1; 54 | 55 | u64 send_offset; 56 | u64 recv_offset; 57 | }; 58 | 59 | int quic_crypto_set_secret(struct quic_crypto *crypto, struct quic_crypto_secret *srt, 60 | u32 version, u8 flag); 61 | int quic_crypto_get_secret(struct quic_crypto *crypto, struct quic_crypto_secret *srt); 62 | int quic_crypto_encrypt(struct quic_crypto *crypto, struct sk_buff *skb); 63 | int quic_crypto_decrypt(struct quic_crypto *crypto, struct sk_buff *skb); 64 | int quic_crypto_key_update(struct quic_crypto *crypto); 65 | 66 | int quic_crypto_initial_keys_install(struct quic_crypto *crypto, struct quic_conn_id *conn_id, 67 | u32 version, bool is_serv); 68 | int quic_crypto_generate_session_ticket_key(struct quic_crypto *crypto, void *data, 69 | u32 len, u8 *key, u32 key_len); 70 | int quic_crypto_generate_stateless_reset_token(struct quic_crypto *crypto, void *data, 71 | u32 len, u8 *key, u32 key_len); 72 | 73 | int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr, u32 addrlen, 74 | struct quic_conn_id *conn_id, u8 *token, u32 *tlen); 75 | int quic_crypto_get_retry_tag(struct quic_crypto *crypto, struct sk_buff *skb, 76 | struct quic_conn_id *odcid, u32 version, u8 *tag); 77 | int quic_crypto_verify_token(struct quic_crypto *crypto, void *addr, u32 addrlen, 78 | struct quic_conn_id *conn_id, u8 *token, u32 len); 79 | 80 | void quic_crypto_free(struct quic_crypto *crypto); 81 | void quic_crypto_init(void); 82 | -------------------------------------------------------------------------------- /modules/net/quic/family.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_PORT_LEN 2 12 | #define QUIC_ADDR4_LEN 4 13 | #define QUIC_ADDR6_LEN 16 14 | 15 | #define QUIC_PREF_ADDR_LEN (QUIC_ADDR4_LEN + QUIC_PORT_LEN + QUIC_ADDR6_LEN + QUIC_PORT_LEN) 16 | 17 | int quic_common_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, 18 | unsigned int optlen); 19 | int quic_common_getsockopt(struct sock *sk, int level, int optname, char __user *optval, 20 | int __user *optlen); 21 | int quic_is_any_addr(union quic_addr *a); 22 | u32 quic_encap_len(union quic_addr *a); 23 | 24 | void quic_get_msg_addrs(union quic_addr *da, union quic_addr *sa, struct sk_buff *skb); 25 | void quic_get_pref_addr(struct sock *sk, union quic_addr *addr, u8 **pp, u32 *plen); 26 | void quic_set_pref_addr(struct sock *sk, u8 *p, union quic_addr *addr); 27 | void quic_seq_dump_addr(struct seq_file *seq, union quic_addr *addr); 28 | 29 | int quic_get_user_addr(struct sock *sk, union quic_addr *a, struct sockaddr *addr, int addr_len); 30 | bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a, union quic_addr *addr); 31 | int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, bool peer); 32 | void quic_set_sk_addr(struct sock *sk, union quic_addr *a, bool src); 33 | 34 | void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da, struct flowi *fl); 35 | int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa, struct flowi *fl); 36 | 37 | void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf, union quic_addr *a); 38 | int quic_get_mtu_info(struct sk_buff *skb, u32 *info); 39 | void quic_set_sk_ecn(struct sock *sk, u8 ecn); 40 | u8 quic_get_msg_ecn(struct sk_buff *skb); 41 | -------------------------------------------------------------------------------- /modules/net/quic/frame.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_CLOSE_PHRASE_MAX_LEN 80 12 | 13 | #define QUIC_TOKEN_MAX_LEN 120 14 | 15 | #define QUIC_TICKET_MIN_LEN 64 16 | #define QUIC_TICKET_MAX_LEN 4096 17 | 18 | #define QUIC_FRAME_BUF_SMALL 20 19 | #define QUIC_FRAME_BUF_LARGE 100 20 | 21 | enum { 22 | QUIC_FRAME_PADDING = 0x00, 23 | QUIC_FRAME_PING = 0x01, 24 | QUIC_FRAME_ACK = 0x02, 25 | QUIC_FRAME_ACK_ECN = 0x03, 26 | QUIC_FRAME_RESET_STREAM = 0x04, 27 | QUIC_FRAME_STOP_SENDING = 0x05, 28 | QUIC_FRAME_CRYPTO = 0x06, 29 | QUIC_FRAME_NEW_TOKEN = 0x07, 30 | QUIC_FRAME_STREAM = 0x08, 31 | QUIC_FRAME_MAX_DATA = 0x10, 32 | QUIC_FRAME_MAX_STREAM_DATA = 0x11, 33 | QUIC_FRAME_MAX_STREAMS_BIDI = 0x12, 34 | QUIC_FRAME_MAX_STREAMS_UNI = 0x13, 35 | QUIC_FRAME_DATA_BLOCKED = 0x14, 36 | QUIC_FRAME_STREAM_DATA_BLOCKED = 0x15, 37 | QUIC_FRAME_STREAMS_BLOCKED_BIDI = 0x16, 38 | QUIC_FRAME_STREAMS_BLOCKED_UNI = 0x17, 39 | QUIC_FRAME_NEW_CONNECTION_ID = 0x18, 40 | QUIC_FRAME_RETIRE_CONNECTION_ID = 0x19, 41 | QUIC_FRAME_PATH_CHALLENGE = 0x1a, 42 | QUIC_FRAME_PATH_RESPONSE = 0x1b, 43 | QUIC_FRAME_CONNECTION_CLOSE = 0x1c, 44 | QUIC_FRAME_CONNECTION_CLOSE_APP = 0x1d, 45 | QUIC_FRAME_HANDSHAKE_DONE = 0x1e, 46 | QUIC_FRAME_DATAGRAM = 0x30, /* RFC 9221 */ 47 | QUIC_FRAME_DATAGRAM_LEN = 0x31, 48 | QUIC_FRAME_MAX = QUIC_FRAME_DATAGRAM_LEN, 49 | }; 50 | 51 | enum { 52 | QUIC_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000, 53 | QUIC_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, 54 | QUIC_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, 55 | QUIC_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003, 56 | QUIC_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, 57 | QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, 58 | QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, 59 | QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, 60 | QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, 61 | QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, 62 | QUIC_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, 63 | QUIC_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, 64 | QUIC_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, 65 | QUIC_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, 66 | QUIC_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, 67 | QUIC_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, 68 | QUIC_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010, 69 | QUIC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020, 70 | QUIC_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2, 71 | QUIC_TRANSPORT_PARAM_VERSION_INFORMATION = 0x11, 72 | QUIC_TRANSPORT_PARAM_DISABLE_1RTT_ENCRYPTION = 0xbaad, 73 | }; 74 | 75 | struct quic_msginfo { 76 | struct quic_stream *stream; 77 | struct iov_iter *msg; 78 | u32 flags; 79 | u8 level; 80 | }; 81 | 82 | struct quic_probeinfo { 83 | u16 size; 84 | u8 level; 85 | }; 86 | 87 | struct quic_frame_ops { 88 | struct quic_frame *(*frame_create)(struct sock *sk, void *data, u8 type); 89 | int (*frame_process)(struct sock *sk, struct quic_frame *frame, u8 type); 90 | void (*frame_ack)(struct sock *sk, struct quic_frame *frame); 91 | u8 ack_eliciting; 92 | }; 93 | 94 | struct quic_frame_frag { 95 | struct quic_frame_frag *next; 96 | u16 size; 97 | u8 data[]; 98 | }; 99 | 100 | struct quic_frame { 101 | union { 102 | struct quic_frame_frag *flist; 103 | struct sk_buff *skb; 104 | }; 105 | struct quic_stream *stream; 106 | struct list_head list; 107 | s64 offset; /* stream/crypto/read offset or first packet number */ 108 | u8 *data; 109 | 110 | refcount_t refcnt; 111 | u16 errcode; 112 | u8 level; 113 | u8 type; 114 | u16 bytes; /* user data bytes */ 115 | u16 size; /* alloc data size */ 116 | u16 len; /* frame length including data in flist */ 117 | 118 | u8 ack_eliciting:1; 119 | u8 transmitted:1; 120 | u8 stream_fin:1; 121 | u8 nodelay:1; 122 | u8 padding:1; 123 | u8 dgram:1; 124 | u8 event:1; 125 | u8 path:1; 126 | }; 127 | 128 | static inline bool quic_frame_new_conn_id(u8 type) 129 | { 130 | return type == QUIC_FRAME_NEW_CONNECTION_ID; 131 | } 132 | 133 | static inline bool quic_frame_dgram(u8 type) 134 | { 135 | return type == QUIC_FRAME_DATAGRAM || type == QUIC_FRAME_DATAGRAM_LEN; 136 | } 137 | 138 | static inline bool quic_frame_stream(u8 type) 139 | { 140 | return type >= QUIC_FRAME_STREAM && type < QUIC_FRAME_MAX_DATA; 141 | } 142 | 143 | static inline bool quic_frame_sack(u8 type) 144 | { 145 | return type == QUIC_FRAME_ACK || type == QUIC_FRAME_ACK_ECN; 146 | } 147 | 148 | static inline bool quic_frame_ping(u8 type) 149 | { 150 | return type == QUIC_FRAME_PING; 151 | } 152 | 153 | static inline int quic_frame_level_check(u8 level, u8 type) 154 | { 155 | if (level == QUIC_CRYPTO_APP) 156 | return 0; 157 | 158 | if (level == QUIC_CRYPTO_EARLY) { 159 | if (type == QUIC_FRAME_ACK || type == QUIC_FRAME_ACK_ECN || 160 | type == QUIC_FRAME_CRYPTO || type == QUIC_FRAME_HANDSHAKE_DONE || 161 | type == QUIC_FRAME_NEW_TOKEN || type == QUIC_FRAME_PATH_RESPONSE || 162 | type == QUIC_FRAME_RETIRE_CONNECTION_ID) 163 | return 1; 164 | return 0; 165 | } 166 | 167 | if (type != QUIC_FRAME_ACK && type != QUIC_FRAME_ACK_ECN && 168 | type != QUIC_FRAME_PADDING && type != QUIC_FRAME_PING && 169 | type != QUIC_FRAME_CRYPTO && type != QUIC_FRAME_CONNECTION_CLOSE) 170 | return 1; 171 | return 0; 172 | } 173 | 174 | int quic_frame_build_transport_params_ext(struct sock *sk, struct quic_transport_param *params, 175 | u8 *data, u32 *len); 176 | int quic_frame_parse_transport_params_ext(struct sock *sk, struct quic_transport_param *params, 177 | u8 *data, u32 len); 178 | int quic_frame_stream_append(struct sock *sk, struct quic_frame *frame, 179 | struct quic_msginfo *info, u8 pack); 180 | 181 | struct quic_frame *quic_frame_alloc(u32 size, u8 *data, gfp_t gfp); 182 | struct quic_frame *quic_frame_get(struct quic_frame *frame); 183 | void quic_frame_put(struct quic_frame *frame); 184 | 185 | struct quic_frame *quic_frame_create(struct sock *sk, u8 type, void *data); 186 | int quic_frame_process(struct sock *sk, struct quic_frame *frame); 187 | void quic_frame_ack(struct sock *sk, struct quic_frame *frame); 188 | -------------------------------------------------------------------------------- /modules/net/quic/input.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_MAX_ACK_DELAY_EXPONENT 20 12 | #define QUIC_DEF_ACK_DELAY_EXPONENT 3 13 | 14 | enum { 15 | QUIC_SACK_FLAG_NONE, 16 | QUIC_SACK_FLAG_XMIT, 17 | QUIC_SACK_FLAG_APP, 18 | }; 19 | 20 | struct quic_inqueue { 21 | struct sk_buff_head backlog_list; 22 | struct list_head handshake_list; 23 | struct list_head stream_list; 24 | struct list_head early_list; 25 | struct list_head recv_list; 26 | struct work_struct work; 27 | u64 max_bytes; 28 | u64 max_data; 29 | u64 highest; 30 | u64 bytes; 31 | 32 | u16 max_datagram_frame_size; 33 | u16 max_udp_payload_size; 34 | u8 ack_delay_exponent; 35 | u32 max_idle_timeout; 36 | u32 max_ack_delay; 37 | u32 timeout; 38 | u32 events; 39 | 40 | u8 disable_compatible_version:1; 41 | u8 disable_1rtt_encryption:1; 42 | u8 grease_quic_bit:1; 43 | u8 stateless_reset:1; 44 | u8 sack_flag:2; 45 | }; 46 | 47 | int quic_inq_handshake_recv(struct sock *sk, struct quic_frame *frame); 48 | int quic_inq_stream_recv(struct sock *sk, struct quic_frame *frame); 49 | int quic_inq_dgram_recv(struct sock *sk, struct quic_frame *frame); 50 | int quic_inq_event_recv(struct sock *sk, u8 event, void *args); 51 | 52 | void quic_inq_stream_list_purge(struct sock *sk, struct quic_stream *stream); 53 | void quic_inq_decrypted_tail(struct sock *sk, struct sk_buff *skb); 54 | void quic_inq_backlog_tail(struct sock *sk, struct sk_buff *skb); 55 | void quic_inq_data_read(struct sock *sk, u32 bytes); 56 | 57 | void quic_inq_flow_control(struct sock *sk, struct quic_stream *stream, u32 bytes); 58 | void quic_inq_get_param(struct sock *sk, struct quic_transport_param *p); 59 | void quic_inq_set_param(struct sock *sk, struct quic_transport_param *p); 60 | void quic_inq_init(struct sock *sk); 61 | void quic_inq_free(struct sock *sk); 62 | -------------------------------------------------------------------------------- /modules/net/quic/output.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | struct quic_outqueue { 12 | struct list_head packet_sent_list; 13 | struct list_head transmitted_list; 14 | struct list_head datagram_list; 15 | struct list_head control_list; 16 | struct list_head stream_list; 17 | struct work_struct work; 18 | u64 last_max_bytes; 19 | u64 max_bytes; 20 | u64 max_data; 21 | u64 bytes; 22 | 23 | u16 max_datagram_frame_size; 24 | u16 max_udp_payload_size; 25 | u8 ack_delay_exponent; 26 | u32 max_idle_timeout; 27 | u32 stream_list_len; /* all frames len in stream list */ 28 | u32 max_ack_delay; 29 | u32 unsent_bytes; 30 | u32 inflight; /* all inflight ack_eliciting frames len */ 31 | u32 window; 32 | u16 count; 33 | 34 | /* Use for 0-RTT/1-RTT DATA (re)transmit, 35 | * as QUIC_SKB_CB(skb)->level is always QUIC_CRYPTO_APP. 36 | * Set this level to QUIC_CRYPTO_EARLY or QUIC_CRYPTO_APP 37 | * when the corresponding crypto is ready for send. 38 | */ 39 | u8 data_level; 40 | u8 *close_phrase; 41 | u32 close_errcode; 42 | u8 close_frame; 43 | u8 pto_count; 44 | 45 | u8 disable_compatible_version:1; 46 | u8 disable_1rtt_encryption:1; 47 | u8 grease_quic_bit:1; 48 | u8 stateless_reset:1; 49 | u8 token_pending:1; 50 | u8 data_blocked:1; 51 | u8 force_delay:1; 52 | u8 single:1; 53 | }; 54 | 55 | void quic_outq_stream_tail(struct sock *sk, struct quic_frame *frame, bool cork); 56 | void quic_outq_dgram_tail(struct sock *sk, struct quic_frame *frame, bool cork); 57 | void quic_outq_ctrl_tail(struct sock *sk, struct quic_frame *frame, bool cork); 58 | void quic_outq_update_path(struct sock *sk, u8 path); 59 | void quic_outq_transmit_pto(struct sock *sk); 60 | 61 | int quic_outq_transmit_frame(struct sock *sk, u8 type, void *data, u8 path, u8 cork); 62 | int quic_outq_transmit_retire_conn_id(struct sock *sk, u64 prior, u8 path, u8 cork); 63 | int quic_outq_transmit_new_conn_id(struct sock *sk, u64 prior, u8 path, u8 cork); 64 | int quic_outq_stream_append(struct sock *sk, struct quic_msginfo *info, u8 pack); 65 | int quic_outq_probe_path_alt(struct sock *sk, u8 cork); 66 | int quic_outq_transmit(struct sock *sk); 67 | 68 | void quic_outq_transmitted_sack(struct sock *sk, u8 level, s64 largest, 69 | s64 smallest, s64 ack_largest, u32 ack_delay); 70 | void quic_outq_packet_sent_tail(struct sock *sk, struct quic_packet_sent *info); 71 | void quic_outq_transmitted_tail(struct sock *sk, struct quic_frame *frame); 72 | void quic_outq_retransmit_list(struct sock *sk, struct list_head *head); 73 | void quic_outq_retransmit_mark(struct sock *sk, u8 level, u8 immediate); 74 | void quic_outq_update_loss_timer(struct sock *sk); 75 | 76 | void quic_outq_transmit_close(struct sock *sk, u8 frame, u32 errcode, u8 level); 77 | void quic_outq_stream_list_purge(struct sock *sk, struct quic_stream *stream); 78 | void quic_outq_encrypted_tail(struct sock *sk, struct sk_buff *skb); 79 | void quic_outq_transmit_app_close(struct sock *sk); 80 | void quic_outq_transmit_probe(struct sock *sk); 81 | 82 | void quic_outq_get_param(struct sock *sk, struct quic_transport_param *p); 83 | void quic_outq_set_param(struct sock *sk, struct quic_transport_param *p); 84 | void quic_outq_sync_window(struct sock *sk, u32 window); 85 | void quic_outq_init(struct sock *sk); 86 | void quic_outq_free(struct sock *sk); 87 | 88 | int quic_outq_flow_control(struct sock *sk, struct quic_stream *stream, u16 bytes, u8 sndblock); 89 | u64 quic_outq_wspace(struct sock *sk, struct quic_stream *stream); 90 | -------------------------------------------------------------------------------- /modules/net/quic/packet.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | struct quic_packet { 12 | struct quic_conn_id dcid; 13 | struct quic_conn_id scid; 14 | union quic_addr daddr; 15 | union quic_addr saddr; 16 | 17 | struct list_head frame_list; 18 | struct sk_buff *head; 19 | u16 frame_len; 20 | u8 taglen[2]; 21 | u32 version; 22 | u8 errframe; 23 | u8 overhead; 24 | u16 errcode; 25 | u16 frames; 26 | u16 mss[2]; 27 | u16 hlen; 28 | u16 len; 29 | 30 | u8 ack_eliciting:1; 31 | u8 ack_requested:1; 32 | u8 ack_immediate:1; 33 | u8 non_probing:1; 34 | u8 has_sack:1; 35 | u8 ipfragok:1; 36 | u8 padding:1; 37 | u8 path:1; 38 | u8 level; 39 | }; 40 | 41 | struct quic_packet_sent { 42 | struct list_head list; 43 | u32 sent_time; 44 | u16 frame_len; 45 | u16 frames; 46 | 47 | s64 number; 48 | u8 level; 49 | u8 ecn:2; 50 | 51 | struct quic_frame *frame_array[]; 52 | }; 53 | 54 | #define QUIC_PACKET_INITIAL_V1 0 55 | #define QUIC_PACKET_0RTT_V1 1 56 | #define QUIC_PACKET_HANDSHAKE_V1 2 57 | #define QUIC_PACKET_RETRY_V1 3 58 | 59 | #define QUIC_PACKET_INITIAL_V2 1 60 | #define QUIC_PACKET_0RTT_V2 2 61 | #define QUIC_PACKET_HANDSHAKE_V2 3 62 | #define QUIC_PACKET_RETRY_V2 0 63 | 64 | #define QUIC_PACKET_INITIAL QUIC_PACKET_INITIAL_V1 65 | #define QUIC_PACKET_0RTT QUIC_PACKET_0RTT_V1 66 | #define QUIC_PACKET_HANDSHAKE QUIC_PACKET_HANDSHAKE_V1 67 | #define QUIC_PACKET_RETRY QUIC_PACKET_RETRY_V1 68 | 69 | #define QUIC_VERSION_LEN 4 70 | 71 | static inline u8 quic_packet_taglen(struct quic_packet *packet) 72 | { 73 | return packet->taglen[!!packet->level]; 74 | } 75 | 76 | static inline void quic_packet_set_taglen(struct quic_packet *packet, u8 taglen) 77 | { 78 | packet->taglen[0] = taglen; 79 | } 80 | 81 | static inline u32 quic_packet_mss(struct quic_packet *packet) 82 | { 83 | return packet->mss[0] - packet->taglen[!!packet->level]; 84 | } 85 | 86 | static inline u32 quic_packet_max_payload(struct quic_packet *packet) 87 | { 88 | return packet->mss[0] - packet->overhead - packet->taglen[!!packet->level]; 89 | } 90 | 91 | static inline u32 quic_packet_max_payload_dgram(struct quic_packet *packet) 92 | { 93 | return packet->mss[1] - packet->overhead - packet->taglen[!!packet->level]; 94 | } 95 | 96 | static inline int quic_packet_empty(struct quic_packet *packet) 97 | { 98 | return list_empty(&packet->frame_list); 99 | } 100 | 101 | static inline void quic_packet_reset(struct quic_packet *packet) 102 | { 103 | packet->level = 0; 104 | packet->errcode = 0; 105 | packet->errframe = 0; 106 | packet->has_sack = 0; 107 | packet->non_probing = 0; 108 | packet->ack_requested = 0; 109 | packet->ack_immediate = 0; 110 | } 111 | 112 | int quic_packet_tail(struct sock *sk, struct quic_frame *frame); 113 | int quic_packet_process(struct sock *sk, struct sk_buff *skb); 114 | int quic_packet_config(struct sock *sk, u8 level, u8 path); 115 | 116 | int quic_packet_xmit(struct sock *sk, struct sk_buff *skb); 117 | int quic_packet_create(struct sock *sk); 118 | int quic_packet_route(struct sock *sk); 119 | 120 | void quic_packet_mss_update(struct sock *sk, u32 mss); 121 | void quic_packet_flush(struct sock *sk); 122 | void quic_packet_init(struct sock *sk); 123 | 124 | int quic_packet_get_dcid(struct quic_conn_id *dcid, struct sk_buff *skb); 125 | int quic_packet_select_version(struct sock *sk, u32 *versions, u8 count); 126 | int quic_packet_parse_alpn(struct sk_buff *skb, struct quic_data *alpn); 127 | u32 *quic_packet_compatible_versions(u32 version); 128 | 129 | void quic_packet_rcv_err_pmtu(struct sock *sk); 130 | int quic_packet_rcv(struct sk_buff *skb, u8 err); 131 | -------------------------------------------------------------------------------- /modules/net/quic/path.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Initialization/cleanup for QUIC protocol support. 8 | * 9 | * Written or modified by: 10 | * Xin Long 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #include "common.h" 17 | #include "family.h" 18 | #include "path.h" 19 | 20 | static int (*quic_path_rcv)(struct sk_buff *skb, u8 err); 21 | static struct workqueue_struct *quic_wq __read_mostly; 22 | 23 | static int quic_udp_rcv(struct sock *sk, struct sk_buff *skb) 24 | { 25 | if (skb_linearize(skb)) 26 | return 0; 27 | 28 | memset(skb->cb, 0, sizeof(skb->cb)); 29 | QUIC_SKB_CB(skb)->udph_offset = skb->transport_header; 30 | QUIC_SKB_CB(skb)->time = jiffies_to_usecs(jiffies); 31 | skb_set_transport_header(skb, sizeof(struct udphdr)); 32 | quic_path_rcv(skb, 0); 33 | return 0; 34 | } 35 | 36 | static int quic_udp_err(struct sock *sk, struct sk_buff *skb) 37 | { 38 | if (skb_linearize(skb)) 39 | return 0; 40 | 41 | QUIC_SKB_CB(skb)->udph_offset = skb->transport_header; 42 | return quic_path_rcv(skb, 1); 43 | } 44 | 45 | static void quic_udp_sock_destroy(struct work_struct *work) 46 | { 47 | struct quic_udp_sock *us = container_of(work, struct quic_udp_sock, work); 48 | 49 | udp_tunnel_sock_release(us->sk->sk_socket); 50 | kfree(us); 51 | } 52 | 53 | static struct quic_udp_sock *quic_udp_sock_create(struct sock *sk, union quic_addr *a) 54 | { 55 | struct udp_tunnel_sock_cfg tuncfg = {}; 56 | struct udp_port_cfg udp_conf = {0}; 57 | struct net *net = sock_net(sk); 58 | struct quic_hash_head *head; 59 | struct quic_udp_sock *us; 60 | struct socket *sock; 61 | 62 | us = kzalloc(sizeof(*us), GFP_ATOMIC); 63 | if (!us) 64 | return NULL; 65 | 66 | quic_udp_conf_init(sk, &udp_conf, a); 67 | if (udp_sock_create(net, &udp_conf, &sock)) { 68 | pr_debug("%s: failed to create udp sock\n", __func__); 69 | kfree(us); 70 | return NULL; 71 | } 72 | 73 | tuncfg.encap_type = 1; 74 | tuncfg.encap_rcv = quic_udp_rcv; 75 | tuncfg.encap_err_lookup = quic_udp_err; 76 | setup_udp_tunnel_sock(net, sock, &tuncfg); 77 | 78 | refcount_set(&us->refcnt, 1); 79 | us->sk = sock->sk; 80 | memcpy(&us->addr, a, sizeof(*a)); 81 | 82 | head = quic_udp_sock_head(net, ntohs(a->v4.sin_port)); 83 | spin_lock(&head->lock); 84 | hlist_add_head(&us->node, &head->head); 85 | spin_unlock(&head->lock); 86 | INIT_WORK(&us->work, quic_udp_sock_destroy); 87 | 88 | return us; 89 | } 90 | 91 | static struct quic_udp_sock *quic_udp_sock_get(struct quic_udp_sock *us) 92 | { 93 | if (us) 94 | refcount_inc(&us->refcnt); 95 | return us; 96 | } 97 | 98 | static void quic_udp_sock_put(struct quic_udp_sock *us) 99 | { 100 | struct quic_hash_head *head; 101 | 102 | if (us && refcount_dec_and_test(&us->refcnt)) { 103 | head = quic_udp_sock_head(sock_net(us->sk), ntohs(us->addr.v4.sin_port)); 104 | 105 | spin_lock(&head->lock); 106 | __hlist_del(&us->node); 107 | spin_unlock(&head->lock); 108 | 109 | queue_work(quic_wq, &us->work); 110 | } 111 | } 112 | 113 | static struct quic_udp_sock *quic_udp_sock_lookup(struct sock *sk, union quic_addr *a) 114 | { 115 | struct quic_udp_sock *tmp, *us = NULL; 116 | struct net *net = sock_net(sk); 117 | struct quic_hash_head *head; 118 | 119 | head = quic_udp_sock_head(net, ntohs(a->v4.sin_port)); 120 | spin_lock(&head->lock); 121 | hlist_for_each_entry(tmp, &head->head, node) { 122 | if (net != sock_net(tmp->sk)) 123 | continue; 124 | 125 | if (quic_cmp_sk_addr(tmp->sk, &tmp->addr, a)) { 126 | us = quic_udp_sock_get(tmp); 127 | break; 128 | } 129 | } 130 | spin_unlock(&head->lock); 131 | if (!us) 132 | us = quic_udp_sock_create(sk, a); 133 | return us; 134 | } 135 | 136 | static int quic_path_set_udp_sock(struct sock *sk, struct quic_path_group *paths, u8 path) 137 | { 138 | struct quic_udp_sock *usk; 139 | 140 | usk = quic_udp_sock_lookup(sk, quic_path_saddr(paths, path)); 141 | if (!usk) 142 | return -EINVAL; 143 | 144 | quic_udp_sock_put(paths->path[path].udp_sk); 145 | paths->path[path].udp_sk = usk; 146 | return 0; 147 | } 148 | 149 | static void quic_path_put_bind_port(struct sock *sk, struct quic_bind_port *pp) 150 | { 151 | struct net *net = sock_net(sk); 152 | struct quic_hash_head *head; 153 | 154 | if (hlist_unhashed(&pp->node)) 155 | return; 156 | 157 | head = quic_bind_port_head(net, pp->port); 158 | spin_lock(&head->lock); 159 | hlist_del_init(&pp->node); 160 | spin_unlock(&head->lock); 161 | } 162 | 163 | static int quic_path_set_bind_port(struct sock *sk, struct quic_path_group *paths, u8 path) 164 | { 165 | struct quic_bind_port *port = quic_path_bind_port(paths, path); 166 | union quic_addr *addr = quic_path_saddr(paths, path); 167 | int rover, low, high, remaining; 168 | struct net *net = sock_net(sk); 169 | struct quic_hash_head *head; 170 | struct quic_bind_port *pp; 171 | u16 snum; 172 | 173 | quic_path_put_bind_port(sk, port); 174 | 175 | rover = ntohs(addr->v4.sin_port); 176 | if (rover) { 177 | head = quic_bind_port_head(net, (u16)rover); 178 | spin_lock_bh(&head->lock); 179 | port->net = net; 180 | port->port = (u16)rover; 181 | hlist_add_head(&port->node, &head->head); 182 | spin_unlock_bh(&head->lock); 183 | return 0; 184 | } 185 | 186 | inet_get_local_port_range(net, &low, &high); 187 | remaining = (high - low) + 1; 188 | rover = (int)(((u64)get_random_u32() * remaining) >> 32) + low; 189 | do { 190 | rover++; 191 | if (rover < low || rover > high) 192 | rover = low; 193 | snum = (u16)rover; 194 | if (inet_is_local_reserved_port(net, snum)) 195 | continue; 196 | head = quic_bind_port_head(net, snum); 197 | spin_lock_bh(&head->lock); 198 | hlist_for_each_entry(pp, &head->head, node) 199 | if (pp->port == snum && net_eq(net, pp->net)) 200 | goto next; 201 | addr->v4.sin_port = htons(snum); 202 | port->net = net; 203 | port->port = snum; 204 | __sk_dst_reset(sk); 205 | hlist_add_head(&port->node, &head->head); 206 | spin_unlock_bh(&head->lock); 207 | return 0; 208 | next: 209 | spin_unlock_bh(&head->lock); 210 | cond_resched(); 211 | } while (--remaining > 0); 212 | 213 | return -EADDRINUSE; 214 | } 215 | 216 | int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path) 217 | { 218 | int err; 219 | 220 | err = quic_path_set_bind_port(sk, paths, path); 221 | if (err) 222 | return err; 223 | err = quic_path_set_udp_sock(sk, paths, path); 224 | if (err) 225 | quic_path_free(sk, paths, path); 226 | return err; 227 | } 228 | 229 | void quic_path_swap(struct quic_path_group *paths) 230 | { 231 | struct quic_path path = paths->path[0]; 232 | 233 | paths->alt_probes = 0; 234 | paths->alt_state = QUIC_PATH_ALT_SWAPPED; 235 | 236 | if (paths->path[1].udp_sk) { 237 | paths->path[0] = paths->path[1]; 238 | paths->path[1] = path; 239 | return; 240 | } 241 | 242 | /* both paths have the same saddr, so swap the daddr only */ 243 | paths->path[0].daddr = paths->path[1].daddr; 244 | paths->path[1].daddr = path.daddr; 245 | } 246 | 247 | void quic_path_free(struct sock *sk, struct quic_path_group *paths, u8 path) 248 | { 249 | paths->alt_probes = 0; 250 | paths->alt_state = QUIC_PATH_ALT_NONE; 251 | 252 | memset(quic_path_daddr(paths, path), 0, sizeof(union quic_addr)); 253 | 254 | quic_udp_sock_put(paths->path[path].udp_sk); 255 | paths->path[path].udp_sk = NULL; 256 | quic_path_put_bind_port(sk, quic_path_bind_port(paths, path)); 257 | memset(quic_path_saddr(paths, path), 0, sizeof(union quic_addr)); 258 | } 259 | 260 | int quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa, union quic_addr *da, 261 | struct sock *sk) 262 | { 263 | if ((!quic_cmp_sk_addr(sk, quic_path_saddr(paths, 0), sa) && !paths->disable_saddr_alt) || 264 | (!quic_cmp_sk_addr(sk, quic_path_daddr(paths, 0), da) && !paths->disable_daddr_alt)) { 265 | if (!quic_path_saddr(paths, 1)->v4.sin_port) 266 | quic_path_set_saddr(paths, 1, sa); 267 | 268 | if (!quic_cmp_sk_addr(sk, quic_path_saddr(paths, 1), sa)) 269 | return 0; 270 | 271 | if (!quic_path_daddr(paths, 1)->v4.sin_port) 272 | quic_path_set_daddr(paths, 1, da); 273 | 274 | return quic_cmp_sk_addr(sk, quic_path_daddr(paths, 1), da); 275 | } 276 | return 0; 277 | } 278 | 279 | void quic_path_get_param(struct quic_path_group *paths, struct quic_transport_param *p) 280 | { 281 | if (p->remote) { 282 | p->disable_active_migration = paths->disable_saddr_alt; 283 | return; 284 | } 285 | p->disable_active_migration = paths->disable_daddr_alt; 286 | } 287 | 288 | void quic_path_set_param(struct quic_path_group *paths, struct quic_transport_param *p) 289 | { 290 | if (p->remote) { 291 | paths->disable_saddr_alt = p->disable_active_migration; 292 | return; 293 | } 294 | paths->disable_daddr_alt = p->disable_active_migration; 295 | } 296 | 297 | enum quic_plpmtud_state { 298 | QUIC_PL_DISABLED, 299 | QUIC_PL_BASE, 300 | QUIC_PL_SEARCH, 301 | QUIC_PL_COMPLETE, 302 | QUIC_PL_ERROR, 303 | }; 304 | 305 | #define QUIC_BASE_PLPMTU 1200 306 | #define QUIC_MAX_PLPMTU 9000 307 | #define QUIC_MIN_PLPMTU 512 308 | 309 | #define QUIC_MAX_PROBES 3 310 | 311 | #define QUIC_PL_BIG_STEP 32 312 | #define QUIC_PL_MIN_STEP 4 313 | 314 | u32 quic_path_pl_send(struct quic_path_group *paths, s64 number) 315 | { 316 | u32 pathmtu = 0; 317 | 318 | paths->pl.number = number; 319 | if (paths->pl.probe_count < QUIC_MAX_PROBES) 320 | goto out; 321 | 322 | paths->pl.probe_count = 0; 323 | if (paths->pl.state == QUIC_PL_BASE) { 324 | if (paths->pl.probe_size == QUIC_BASE_PLPMTU) { /* BASE_PLPMTU Confirming Failed */ 325 | paths->pl.state = QUIC_PL_ERROR; /* Base -> Error */ 326 | 327 | paths->pl.pmtu = QUIC_BASE_PLPMTU; 328 | paths->pathmtu = QUIC_BASE_PLPMTU; 329 | pathmtu = paths->pathmtu; 330 | } 331 | } else if (paths->pl.state == QUIC_PL_SEARCH) { 332 | if (paths->pl.pmtu == paths->pl.probe_size) { /* Black Hole Detected */ 333 | paths->pl.state = QUIC_PL_BASE; /* Search -> Base */ 334 | paths->pl.probe_size = QUIC_BASE_PLPMTU; 335 | paths->pl.probe_high = 0; 336 | 337 | paths->pl.pmtu = QUIC_BASE_PLPMTU; 338 | paths->pathmtu = QUIC_BASE_PLPMTU; 339 | pathmtu = paths->pathmtu; 340 | } else { /* Normal probe failure. */ 341 | paths->pl.probe_high = paths->pl.probe_size; 342 | paths->pl.probe_size = paths->pl.pmtu; 343 | } 344 | } else if (paths->pl.state == QUIC_PL_COMPLETE) { 345 | if (paths->pl.pmtu == paths->pl.probe_size) { /* Black Hole Detected */ 346 | paths->pl.state = QUIC_PL_BASE; /* Search Complete -> Base */ 347 | paths->pl.probe_size = QUIC_BASE_PLPMTU; 348 | 349 | paths->pl.pmtu = QUIC_BASE_PLPMTU; 350 | paths->pathmtu = QUIC_BASE_PLPMTU; 351 | pathmtu = paths->pathmtu; 352 | } 353 | } 354 | 355 | out: 356 | pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\n", __func__, paths, 357 | paths->pl.state, paths->pl.pmtu, paths->pl.probe_size, paths->pl.probe_high); 358 | paths->pl.probe_count++; 359 | return pathmtu; 360 | } 361 | 362 | u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer, bool *complete) 363 | { 364 | u32 pathmtu = 0; 365 | 366 | pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\n", __func__, paths, 367 | paths->pl.state, paths->pl.pmtu, paths->pl.probe_size, paths->pl.probe_high); 368 | 369 | *raise_timer = false; 370 | paths->pl.number = 0; 371 | paths->pl.pmtu = paths->pl.probe_size; 372 | paths->pl.probe_count = 0; 373 | if (paths->pl.state == QUIC_PL_BASE) { 374 | paths->pl.state = QUIC_PL_SEARCH; /* Base -> Search */ 375 | paths->pl.probe_size += QUIC_PL_BIG_STEP; 376 | } else if (paths->pl.state == QUIC_PL_ERROR) { 377 | paths->pl.state = QUIC_PL_SEARCH; /* Error -> Search */ 378 | 379 | paths->pl.pmtu = paths->pl.probe_size; 380 | paths->pathmtu = (u32)paths->pl.pmtu; 381 | pathmtu = paths->pathmtu; 382 | paths->pl.probe_size += QUIC_PL_BIG_STEP; 383 | } else if (paths->pl.state == QUIC_PL_SEARCH) { 384 | if (!paths->pl.probe_high) { 385 | if (paths->pl.probe_size < QUIC_MAX_PLPMTU) { 386 | paths->pl.probe_size = 387 | (u16)min(paths->pl.probe_size + QUIC_PL_BIG_STEP, 388 | QUIC_MAX_PLPMTU); 389 | *complete = false; 390 | return pathmtu; 391 | } 392 | paths->pl.probe_high = QUIC_MAX_PLPMTU; 393 | } 394 | paths->pl.probe_size += QUIC_PL_MIN_STEP; 395 | if (paths->pl.probe_size >= paths->pl.probe_high) { 396 | paths->pl.probe_high = 0; 397 | paths->pl.state = QUIC_PL_COMPLETE; /* Search -> Search Complete */ 398 | 399 | paths->pl.probe_size = paths->pl.pmtu; 400 | paths->pathmtu = (u32)paths->pl.pmtu; 401 | pathmtu = paths->pathmtu; 402 | *raise_timer = true; 403 | } 404 | } else if (paths->pl.state == QUIC_PL_COMPLETE) { 405 | /* Raise probe_size again after 30 * interval in Search Complete */ 406 | paths->pl.state = QUIC_PL_SEARCH; /* Search Complete -> Search */ 407 | paths->pl.probe_size = (u16)min(paths->pl.probe_size + QUIC_PL_MIN_STEP, 408 | QUIC_MAX_PLPMTU); 409 | } 410 | 411 | *complete = (paths->pl.state == QUIC_PL_COMPLETE); 412 | return pathmtu; 413 | } 414 | 415 | u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu, bool *reset_timer) 416 | { 417 | u32 pathmtu = 0; 418 | 419 | pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, ptb: %d\n", __func__, paths, 420 | paths->pl.state, paths->pl.pmtu, paths->pl.probe_size, pmtu); 421 | 422 | *reset_timer = false; 423 | if (pmtu < QUIC_MIN_PLPMTU || pmtu >= (u32)paths->pl.probe_size) 424 | return pathmtu; 425 | 426 | if (paths->pl.state == QUIC_PL_BASE) { 427 | if (pmtu >= QUIC_MIN_PLPMTU && pmtu < QUIC_BASE_PLPMTU) { 428 | paths->pl.state = QUIC_PL_ERROR; /* Base -> Error */ 429 | 430 | paths->pl.pmtu = QUIC_BASE_PLPMTU; 431 | paths->pathmtu = QUIC_BASE_PLPMTU; 432 | pathmtu = paths->pathmtu; 433 | } 434 | } else if (paths->pl.state == QUIC_PL_SEARCH) { 435 | if (pmtu >= QUIC_BASE_PLPMTU && pmtu < (u32)paths->pl.pmtu) { 436 | paths->pl.state = QUIC_PL_BASE; /* Search -> Base */ 437 | paths->pl.probe_size = QUIC_BASE_PLPMTU; 438 | paths->pl.probe_count = 0; 439 | 440 | paths->pl.probe_high = 0; 441 | paths->pl.pmtu = QUIC_BASE_PLPMTU; 442 | paths->pathmtu = QUIC_BASE_PLPMTU; 443 | pathmtu = paths->pathmtu; 444 | } else if (pmtu > (u32)paths->pl.pmtu && pmtu < (u32)paths->pl.probe_size) { 445 | paths->pl.probe_size = (u16)pmtu; 446 | paths->pl.probe_count = 0; 447 | } 448 | } else if (paths->pl.state == QUIC_PL_COMPLETE) { 449 | if (pmtu >= QUIC_BASE_PLPMTU && pmtu < (u32)paths->pl.pmtu) { 450 | paths->pl.state = QUIC_PL_BASE; /* Complete -> Base */ 451 | paths->pl.probe_size = QUIC_BASE_PLPMTU; 452 | paths->pl.probe_count = 0; 453 | 454 | paths->pl.probe_high = 0; 455 | paths->pl.pmtu = QUIC_BASE_PLPMTU; 456 | paths->pathmtu = QUIC_BASE_PLPMTU; 457 | pathmtu = paths->pathmtu; 458 | *reset_timer = true; 459 | } 460 | } 461 | return pathmtu; 462 | } 463 | 464 | void quic_path_pl_reset(struct quic_path_group *paths) 465 | { 466 | paths->pl.number = 0; 467 | paths->pl.state = QUIC_PL_BASE; 468 | paths->pl.pmtu = QUIC_BASE_PLPMTU; 469 | paths->pl.probe_size = QUIC_BASE_PLPMTU; 470 | } 471 | 472 | bool quic_path_pl_confirm(struct quic_path_group *paths, s64 largest, s64 smallest) 473 | { 474 | return paths->pl.number && paths->pl.number >= smallest && paths->pl.number <= largest; 475 | } 476 | 477 | int quic_path_init(int (*rcv)(struct sk_buff *skb, u8 err)) 478 | { 479 | quic_wq = create_workqueue("quic_workqueue"); 480 | if (!quic_wq) 481 | return -ENOMEM; 482 | 483 | quic_path_rcv = rcv; 484 | return 0; 485 | } 486 | 487 | void quic_path_destroy(void) 488 | { 489 | destroy_workqueue(quic_wq); 490 | } 491 | -------------------------------------------------------------------------------- /modules/net/quic/path.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_PATH_MIN_PMTU 1200U 12 | #define QUIC_PATH_MAX_PMTU 65536U 13 | 14 | #define QUIC_MIN_UDP_PAYLOAD 1200 15 | #define QUIC_MAX_UDP_PAYLOAD 65527 16 | 17 | #define QUIC_PATH_ENTROPY_LEN 8 18 | 19 | enum { 20 | QUIC_PATH_ALT_NONE, 21 | QUIC_PATH_ALT_PENDING, 22 | QUIC_PATH_ALT_PROBING, 23 | QUIC_PATH_ALT_SWAPPED, 24 | }; 25 | 26 | struct quic_bind_port { 27 | struct hlist_node node; 28 | unsigned short port; 29 | struct net *net; 30 | }; 31 | 32 | struct quic_udp_sock { 33 | struct work_struct work; 34 | struct hlist_node node; 35 | union quic_addr addr; 36 | refcount_t refcnt; 37 | struct sock *sk; 38 | }; 39 | 40 | struct quic_path { 41 | union quic_addr daddr; 42 | union quic_addr saddr; 43 | struct quic_bind_port port; 44 | struct quic_udp_sock *udp_sk; 45 | }; 46 | 47 | struct quic_path_group { 48 | u8 entropy[QUIC_PATH_ENTROPY_LEN]; 49 | struct quic_conn_id retry_dcid; 50 | struct quic_conn_id orig_dcid; 51 | struct quic_path path[2]; 52 | struct flowi fl; 53 | u16 ampl_sndlen; /* amplificationlimit send counting */ 54 | u16 ampl_rcvlen; /* amplificationlimit recv counting */ 55 | 56 | u32 mtu_info; 57 | u32 pathmtu; 58 | struct { 59 | s64 number; 60 | u16 pmtu; 61 | 62 | u16 probe_size; 63 | u16 probe_high; 64 | u8 probe_count; 65 | u8 state; 66 | } pl; /* plpmtud related */ 67 | 68 | u8 disable_saddr_alt:1; 69 | u8 disable_daddr_alt:1; 70 | u8 validated:1; 71 | u8 pref_addr:1; 72 | 73 | u8 ecn_probes; 74 | u8 alt_probes; 75 | u8 alt_state; 76 | u8 blocked:1; 77 | u8 retry:1; 78 | u8 serv:1; 79 | }; 80 | 81 | static inline union quic_addr *quic_path_saddr(struct quic_path_group *paths, u8 path) 82 | { 83 | return &paths->path[path].saddr; 84 | } 85 | 86 | static inline void quic_path_set_saddr(struct quic_path_group *paths, u8 path, 87 | union quic_addr *addr) 88 | { 89 | memcpy(quic_path_saddr(paths, path), addr, sizeof(*addr)); 90 | } 91 | 92 | static inline union quic_addr *quic_path_daddr(struct quic_path_group *paths, u8 path) 93 | { 94 | return &paths->path[path].daddr; 95 | } 96 | 97 | static inline void quic_path_set_daddr(struct quic_path_group *paths, u8 path, 98 | union quic_addr *addr) 99 | { 100 | memcpy(quic_path_daddr(paths, path), addr, sizeof(*addr)); 101 | } 102 | 103 | static inline union quic_addr *quic_path_uaddr(struct quic_path_group *paths, u8 path) 104 | { 105 | return &paths->path[path].udp_sk->addr; 106 | } 107 | 108 | static inline struct quic_bind_port *quic_path_bind_port(struct quic_path_group *paths, u8 path) 109 | { 110 | return &paths->path[path].port; 111 | } 112 | 113 | static inline bool quic_path_alt_state(struct quic_path_group *paths, u8 state) 114 | { 115 | return paths->alt_state == state; 116 | } 117 | 118 | static inline void quic_path_set_alt_state(struct quic_path_group *paths, u8 state) 119 | { 120 | paths->alt_state = state; 121 | } 122 | 123 | static inline struct quic_conn_id *quic_path_dcid(struct quic_path_group *paths) 124 | { 125 | return paths->retry ? &paths->retry_dcid : &paths->orig_dcid; 126 | } 127 | 128 | int quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa, union quic_addr *da, 129 | struct sock *sk); 130 | int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path); 131 | void quic_path_free(struct sock *sk, struct quic_path_group *paths, u8 path); 132 | void quic_path_swap(struct quic_path_group *paths); 133 | 134 | u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer, bool *complete); 135 | u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu, bool *reset_timer); 136 | u32 quic_path_pl_send(struct quic_path_group *paths, s64 number); 137 | 138 | void quic_path_get_param(struct quic_path_group *paths, struct quic_transport_param *p); 139 | void quic_path_set_param(struct quic_path_group *paths, struct quic_transport_param *p); 140 | bool quic_path_pl_confirm(struct quic_path_group *paths, s64 largest, s64 smallest); 141 | void quic_path_pl_reset(struct quic_path_group *paths); 142 | 143 | int quic_path_init(int (*rcv)(struct sk_buff *skb, u8 err)); 144 | void quic_path_destroy(void); 145 | -------------------------------------------------------------------------------- /modules/net/quic/pnspace.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Initialization/cleanup for QUIC protocol support. 8 | * 9 | * Written or modified by: 10 | * Xin Long 11 | */ 12 | 13 | #include 14 | 15 | #include "pnspace.h" 16 | 17 | static int quic_pnspace_grow(struct quic_pnspace *space, u16 size) 18 | { 19 | u16 len, inc, offset; 20 | unsigned long *new; 21 | 22 | if (size > QUIC_PN_MAP_SIZE) 23 | return 0; 24 | 25 | inc = ALIGN((size - space->pn_map_len), BITS_PER_LONG) + QUIC_PN_MAP_INCREMENT; 26 | len = (u16)min(space->pn_map_len + inc, QUIC_PN_MAP_SIZE); 27 | 28 | new = kzalloc(BITS_TO_BYTES(len), GFP_ATOMIC); 29 | if (!new) 30 | return 0; 31 | 32 | offset = (u16)(space->max_pn_seen + 1 - space->base_pn); 33 | bitmap_copy(new, space->pn_map, offset); 34 | kfree(space->pn_map); 35 | space->pn_map = new; 36 | space->pn_map_len = len; 37 | 38 | return 1; 39 | } 40 | 41 | int quic_pnspace_init(struct quic_pnspace *space) 42 | { 43 | if (!space->pn_map) { 44 | space->pn_map = kzalloc(BITS_TO_BYTES(QUIC_PN_MAP_INITIAL), GFP_KERNEL); 45 | if (!space->pn_map) 46 | return -ENOMEM; 47 | space->pn_map_len = QUIC_PN_MAP_INITIAL; 48 | } else { 49 | bitmap_zero(space->pn_map, space->pn_map_len); 50 | } 51 | 52 | space->max_time_limit = QUIC_PNSPACE_TIME_LIMIT; 53 | space->next_pn = QUIC_PNSPACE_NEXT_PN; 54 | space->base_pn = -1; 55 | return 0; 56 | } 57 | EXPORT_SYMBOL_GPL(quic_pnspace_init); 58 | 59 | void quic_pnspace_free(struct quic_pnspace *space) 60 | { 61 | space->pn_map_len = 0; 62 | kfree(space->pn_map); 63 | } 64 | EXPORT_SYMBOL_GPL(quic_pnspace_free); 65 | 66 | int quic_pnspace_check(struct quic_pnspace *space, s64 pn) 67 | { 68 | if (space->base_pn == -1) { 69 | quic_pnspace_set_base_pn(space, pn + 1); 70 | return 0; 71 | } 72 | 73 | if (pn < space->min_pn_seen || pn >= space->base_pn + QUIC_PN_MAP_SIZE) 74 | return -1; 75 | 76 | if (pn < space->base_pn || (pn - space->base_pn < space->pn_map_len && 77 | test_bit(pn - space->base_pn, space->pn_map))) 78 | return 1; 79 | 80 | return 0; 81 | } 82 | EXPORT_SYMBOL_GPL(quic_pnspace_check); 83 | 84 | /* move base_pn next to pn */ 85 | static void quic_pnspace_move(struct quic_pnspace *space, s64 pn) 86 | { 87 | u16 offset; 88 | 89 | offset = (u16)(pn + 1 - space->base_pn); 90 | offset = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len, offset); 91 | space->base_pn += offset; 92 | bitmap_shift_right(space->pn_map, space->pn_map, offset, space->pn_map_len); 93 | } 94 | 95 | int quic_pnspace_mark(struct quic_pnspace *space, s64 pn) 96 | { 97 | s64 mid_pn_seen; 98 | u16 gap; 99 | 100 | if (pn < space->base_pn) 101 | return 0; 102 | 103 | gap = (u16)(pn - space->base_pn); 104 | if (gap >= space->pn_map_len && !quic_pnspace_grow(space, gap + 1)) 105 | return -ENOMEM; 106 | 107 | if (space->max_pn_seen < pn) { 108 | space->max_pn_seen = pn; 109 | space->max_pn_time = space->time; 110 | } 111 | 112 | if (space->base_pn == pn) { 113 | if (quic_pnspace_has_gap(space)) 114 | quic_pnspace_move(space, pn); 115 | else /* fast path */ 116 | space->base_pn++; 117 | } else { 118 | set_bit(gap, space->pn_map); 119 | } 120 | 121 | /* move forward min and mid_pn_seen only when receiving max_pn */ 122 | if (space->max_pn_seen != pn) 123 | return 0; 124 | 125 | mid_pn_seen = min_t(s64, space->mid_pn_seen, space->base_pn); 126 | if (space->max_pn_time < space->mid_pn_time + space->max_time_limit && 127 | space->max_pn_seen <= mid_pn_seen + QUIC_PN_MAP_LIMIT) 128 | return 0; 129 | 130 | if (space->mid_pn_seen + 1 > space->base_pn) 131 | quic_pnspace_move(space, space->mid_pn_seen); 132 | 133 | space->min_pn_seen = space->mid_pn_seen; 134 | space->mid_pn_seen = space->max_pn_seen; 135 | space->mid_pn_time = space->max_pn_time; 136 | return 0; 137 | } 138 | EXPORT_SYMBOL_GPL(quic_pnspace_mark); 139 | 140 | static int quic_pnspace_next_gap_ack(const struct quic_pnspace *space, 141 | s64 *iter, u16 *start, u16 *end) 142 | { 143 | u16 start_ = 0, end_ = 0, offset = (u16)(*iter - space->base_pn); 144 | 145 | start_ = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len, offset); 146 | if (space->max_pn_seen <= space->base_pn + start_) 147 | return 0; 148 | 149 | end_ = (u16)find_next_bit(space->pn_map, space->pn_map_len, start_); 150 | if (space->max_pn_seen <= space->base_pn + end_ - 1) 151 | return 0; 152 | 153 | *start = start_ + 1; 154 | *end = end_; 155 | *iter = space->base_pn + *end; 156 | return 1; 157 | } 158 | 159 | u16 quic_pnspace_num_gabs(struct quic_pnspace *space, struct quic_gap_ack_block *gabs) 160 | { 161 | u16 start, end, ngaps = 0; 162 | s64 iter; 163 | 164 | if (!quic_pnspace_has_gap(space)) 165 | return 0; 166 | 167 | iter = space->base_pn; 168 | if (!iter) /* use min_pn_seen if base_pn hasn't moved */ 169 | iter = space->min_pn_seen + 1; 170 | 171 | while (quic_pnspace_next_gap_ack(space, &iter, &start, &end)) { 172 | gabs[ngaps].start = start; 173 | if (ngaps == QUIC_PN_MAX_GABS - 1) { 174 | gabs[ngaps].end = (u16)(space->max_pn_seen - space->base_pn); 175 | ngaps++; 176 | break; 177 | } 178 | gabs[ngaps].end = end; 179 | ngaps++; 180 | } 181 | return ngaps; 182 | } 183 | EXPORT_SYMBOL_GPL(quic_pnspace_num_gabs); 184 | -------------------------------------------------------------------------------- /modules/net/quic/pnspace.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_PN_MAX_GABS 32 12 | #define QUIC_PN_MAP_MAX_PN (BIT_ULL(62) - 1) 13 | 14 | #define QUIC_PN_MAP_INITIAL 64 15 | #define QUIC_PN_MAP_INCREMENT QUIC_PN_MAP_INITIAL 16 | #define QUIC_PN_MAP_SIZE 4096 17 | #define QUIC_PN_MAP_LIMIT (QUIC_PN_MAP_SIZE * 3 / 4) 18 | 19 | #define QUIC_PNSPACE_MAX (QUIC_CRYPTO_MAX - 1) 20 | #define QUIC_PNSPACE_NEXT_PN 0 21 | #define QUIC_PNSPACE_TIME_LIMIT (333000 * 3) 22 | 23 | enum { 24 | QUIC_ECN_ECT1, 25 | QUIC_ECN_ECT0, 26 | QUIC_ECN_CE, 27 | QUIC_ECN_MAX 28 | }; 29 | 30 | enum { 31 | QUIC_ECN_LOCAL, 32 | QUIC_ECN_PEER, 33 | QUIC_ECN_DIR_MAX 34 | }; 35 | 36 | struct quic_gap_ack_block { 37 | u16 start; 38 | u16 end; 39 | }; 40 | 41 | /* pn_map: 42 | * min_pn_seen --> |----------------------|---------------------|... 43 | * base_pn --^ mid_pn_seen --^ max_pn_seen --^ 44 | * 45 | * move forward: 46 | * min_pn_seen = mid_pn_seen; 47 | * base_pn = first_zero_bit from mid_pn_seen + 1; 48 | * mid_pn_seen = max_pn_seen; 49 | * mid_pn_time = now; 50 | * when: 51 | * 'max_pn_time - mid_pn_time >= max_time_limit' or 52 | * 'max_pn_seen - mid_pn_seen > QUIC_PN_MAP_LIMIT' 53 | * gaps search: 54 | * from base_pn - 1 to max_pn_seen 55 | */ 56 | struct quic_pnspace { 57 | u64 ecn_count[QUIC_ECN_DIR_MAX][QUIC_ECN_MAX]; 58 | unsigned long *pn_map; 59 | u16 pn_map_len; 60 | u8 need_sack:1; 61 | u8 sack_path:1; 62 | 63 | u32 max_time_limit; 64 | s64 min_pn_seen; 65 | s64 mid_pn_seen; 66 | s64 max_pn_seen; 67 | u32 mid_pn_time; 68 | u32 max_pn_time; 69 | s64 base_pn; 70 | u32 time; 71 | 72 | s64 max_pn_acked_seen; 73 | u32 max_pn_acked_time; 74 | u32 last_sent_time; 75 | u32 loss_time; 76 | u32 inflight; 77 | s64 next_pn; /* next packet number to send */ 78 | }; 79 | 80 | static inline void quic_pnspace_set_max_pn_acked_seen(struct quic_pnspace *space, 81 | s64 max_pn_acked_seen) 82 | { 83 | if (space->max_pn_acked_seen >= max_pn_acked_seen) 84 | return; 85 | space->max_pn_acked_seen = max_pn_acked_seen; 86 | space->max_pn_acked_time = jiffies_to_usecs(jiffies); 87 | } 88 | 89 | static inline void quic_pnspace_set_base_pn(struct quic_pnspace *space, s64 pn) 90 | { 91 | space->base_pn = pn; 92 | space->max_pn_seen = space->base_pn - 1; 93 | space->mid_pn_seen = space->max_pn_seen; 94 | space->min_pn_seen = space->max_pn_seen; 95 | 96 | space->max_pn_time = space->time; 97 | space->mid_pn_time = space->max_pn_time; 98 | } 99 | 100 | static inline bool quic_pnspace_has_gap(const struct quic_pnspace *space) 101 | { 102 | return space->base_pn != space->max_pn_seen + 1; 103 | } 104 | 105 | static inline void quic_pnspace_inc_ecn_count(struct quic_pnspace *space, u8 ecn) 106 | { 107 | if (!ecn) 108 | return; 109 | space->ecn_count[QUIC_ECN_LOCAL][ecn - 1]++; 110 | } 111 | 112 | static inline int quic_pnspace_set_ecn_count(struct quic_pnspace *space, u64 *ecn_count) 113 | { 114 | if (space->ecn_count[QUIC_ECN_PEER][QUIC_ECN_ECT0] < ecn_count[QUIC_ECN_ECT0]) 115 | space->ecn_count[QUIC_ECN_PEER][QUIC_ECN_ECT0] = ecn_count[QUIC_ECN_ECT0]; 116 | if (space->ecn_count[QUIC_ECN_PEER][QUIC_ECN_ECT1] < ecn_count[QUIC_ECN_ECT1]) 117 | space->ecn_count[QUIC_ECN_PEER][QUIC_ECN_ECT1] = ecn_count[QUIC_ECN_ECT1]; 118 | if (space->ecn_count[QUIC_ECN_PEER][QUIC_ECN_CE] < ecn_count[QUIC_ECN_CE]) { 119 | space->ecn_count[QUIC_ECN_PEER][QUIC_ECN_CE] = ecn_count[QUIC_ECN_CE]; 120 | return 1; 121 | } 122 | return 0; 123 | } 124 | 125 | static inline bool quic_pnspace_has_ecn_count(struct quic_pnspace *space) 126 | { 127 | return space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT0] || 128 | space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT1] || 129 | space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_CE]; 130 | } 131 | 132 | u16 quic_pnspace_num_gabs(struct quic_pnspace *space, struct quic_gap_ack_block *gabs); 133 | int quic_pnspace_check(struct quic_pnspace *space, s64 pn); 134 | int quic_pnspace_mark(struct quic_pnspace *space, s64 pn); 135 | 136 | void quic_pnspace_free(struct quic_pnspace *space); 137 | int quic_pnspace_init(struct quic_pnspace *space); 138 | -------------------------------------------------------------------------------- /modules/net/quic/protocol.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | extern struct quic_transport_param quic_default_param __read_mostly; 12 | extern struct kmem_cache *quic_frame_cachep __read_mostly; 13 | extern struct percpu_counter quic_sockets_allocated; 14 | 15 | extern long sysctl_quic_mem[3]; 16 | extern int sysctl_quic_rmem[3]; 17 | extern int sysctl_quic_wmem[3]; 18 | 19 | enum { 20 | QUIC_MIB_NUM = 0, 21 | QUIC_MIB_CONN_CURRENTESTABS, 22 | QUIC_MIB_CONN_PASSIVEESTABS, 23 | QUIC_MIB_CONN_ACTIVEESTABS, 24 | QUIC_MIB_PKT_RCVFASTPATHS, 25 | QUIC_MIB_PKT_DECFASTPATHS, 26 | QUIC_MIB_PKT_ENCFASTPATHS, 27 | QUIC_MIB_PKT_RCVBACKLOGS, 28 | QUIC_MIB_PKT_DECBACKLOGS, 29 | QUIC_MIB_PKT_ENCBACKLOGS, 30 | QUIC_MIB_PKT_INVHDRDROP, 31 | QUIC_MIB_PKT_INVNUMDROP, 32 | QUIC_MIB_PKT_INVFRMDROP, 33 | QUIC_MIB_PKT_RCVDROP, 34 | QUIC_MIB_PKT_DECDROP, 35 | QUIC_MIB_PKT_ENCDROP, 36 | QUIC_MIB_FRM_RCVBUFDROP, 37 | QUIC_MIB_FRM_RETRANS, 38 | QUIC_MIB_FRM_CLOSES, 39 | QUIC_MIB_MAX 40 | }; 41 | 42 | struct quic_mib { 43 | unsigned long mibs[QUIC_MIB_MAX]; 44 | }; 45 | 46 | struct quic_net { 47 | DEFINE_SNMP_STAT(struct quic_mib, stat); 48 | #ifdef CONFIG_PROC_FS 49 | struct proc_dir_entry *proc_net; 50 | #endif 51 | }; 52 | 53 | struct quic_net *quic_net(struct net *net); 54 | 55 | #define QUIC_INC_STATS(net, field) SNMP_INC_STATS(quic_net(net)->stat, field) 56 | #define QUIC_DEC_STATS(net, field) SNMP_DEC_STATS(quic_net(net)->stat, field) 57 | -------------------------------------------------------------------------------- /modules/net/quic/socket.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #ifndef __net_quic_h__ 12 | #define __net_quic_h__ 13 | 14 | #include 15 | #include 16 | 17 | #include "pnspace.h" 18 | #include "common.h" 19 | #include "family.h" 20 | #include "stream.h" 21 | #include "connid.h" 22 | #include "crypto.h" 23 | #include "path.h" 24 | #include "cong.h" 25 | 26 | #include "packet.h" 27 | #include "frame.h" 28 | 29 | #include "protocol.h" 30 | #include "output.h" 31 | #include "input.h" 32 | #include "timer.h" 33 | 34 | extern struct proto quic_prot; 35 | extern struct proto quicv6_prot; 36 | 37 | enum quic_state { 38 | QUIC_SS_CLOSED = TCP_CLOSE, 39 | QUIC_SS_LISTENING = TCP_LISTEN, 40 | QUIC_SS_ESTABLISHING = TCP_SYN_RECV, 41 | QUIC_SS_ESTABLISHED = TCP_ESTABLISHED, 42 | }; 43 | 44 | struct quic_request_sock { 45 | struct list_head list; 46 | 47 | struct quic_conn_id dcid; 48 | struct quic_conn_id scid; 49 | union quic_addr daddr; 50 | union quic_addr saddr; 51 | 52 | struct quic_conn_id orig_dcid; 53 | u32 version; 54 | u8 retry; 55 | }; 56 | 57 | enum quic_tsq_enum { 58 | QUIC_MTU_REDUCED_DEFERRED, 59 | QUIC_LOSS_DEFERRED, 60 | QUIC_SACK_DEFERRED, 61 | QUIC_PATH_DEFERRED, 62 | QUIC_PMTU_DEFERRED, 63 | QUIC_TSQ_DEFERRED, 64 | }; 65 | 66 | enum quic_tsq_flags { 67 | QUIC_F_MTU_REDUCED_DEFERRED = BIT(QUIC_MTU_REDUCED_DEFERRED), 68 | QUIC_F_LOSS_DEFERRED = BIT(QUIC_LOSS_DEFERRED), 69 | QUIC_F_SACK_DEFERRED = BIT(QUIC_SACK_DEFERRED), 70 | QUIC_F_PATH_DEFERRED = BIT(QUIC_PATH_DEFERRED), 71 | QUIC_F_PMTU_DEFERRED = BIT(QUIC_PMTU_DEFERRED), 72 | QUIC_F_TSQ_DEFERRED = BIT(QUIC_TSQ_DEFERRED), 73 | }; 74 | 75 | #define QUIC_DEFERRED_ALL (QUIC_F_MTU_REDUCED_DEFERRED | \ 76 | QUIC_F_LOSS_DEFERRED | \ 77 | QUIC_F_SACK_DEFERRED | \ 78 | QUIC_F_PATH_DEFERRED | \ 79 | QUIC_F_PMTU_DEFERRED | \ 80 | QUIC_F_TSQ_DEFERRED) 81 | 82 | struct quic_sock { 83 | struct inet_sock inet; 84 | struct list_head reqs; 85 | 86 | struct quic_config config; 87 | struct quic_data ticket; 88 | struct quic_data token; 89 | struct quic_data alpn; 90 | 91 | struct quic_stream_table streams; 92 | struct quic_conn_id_set source; 93 | struct quic_conn_id_set dest; 94 | struct quic_path_group paths; 95 | struct quic_cong cong; 96 | struct quic_pnspace space[QUIC_PNSPACE_MAX]; 97 | struct quic_crypto crypto[QUIC_CRYPTO_MAX]; 98 | 99 | struct quic_outqueue outq; 100 | struct quic_inqueue inq; 101 | struct quic_packet packet; 102 | struct quic_timer timers[QUIC_TIMER_MAX]; 103 | }; 104 | 105 | struct quic6_sock { 106 | struct quic_sock quic; 107 | struct ipv6_pinfo inet6; 108 | }; 109 | 110 | static inline struct quic_sock *quic_sk(const struct sock *sk) 111 | { 112 | return (struct quic_sock *)sk; 113 | } 114 | 115 | static inline struct list_head *quic_reqs(const struct sock *sk) 116 | { 117 | return &quic_sk(sk)->reqs; 118 | } 119 | 120 | static inline struct quic_config *quic_config(const struct sock *sk) 121 | { 122 | return &quic_sk(sk)->config; 123 | } 124 | 125 | static inline struct quic_data *quic_token(const struct sock *sk) 126 | { 127 | return &quic_sk(sk)->token; 128 | } 129 | 130 | static inline struct quic_data *quic_ticket(const struct sock *sk) 131 | { 132 | return &quic_sk(sk)->ticket; 133 | } 134 | 135 | static inline struct quic_data *quic_alpn(const struct sock *sk) 136 | { 137 | return &quic_sk(sk)->alpn; 138 | } 139 | 140 | static inline struct quic_stream_table *quic_streams(const struct sock *sk) 141 | { 142 | return &quic_sk(sk)->streams; 143 | } 144 | 145 | static inline struct quic_conn_id_set *quic_source(const struct sock *sk) 146 | { 147 | return &quic_sk(sk)->source; 148 | } 149 | 150 | static inline struct quic_conn_id_set *quic_dest(const struct sock *sk) 151 | { 152 | return &quic_sk(sk)->dest; 153 | } 154 | 155 | static inline struct quic_path_group *quic_paths(const struct sock *sk) 156 | { 157 | return &quic_sk(sk)->paths; 158 | } 159 | 160 | static inline bool quic_is_serv(const struct sock *sk) 161 | { 162 | return quic_paths(sk)->serv; 163 | } 164 | 165 | static inline struct quic_cong *quic_cong(const struct sock *sk) 166 | { 167 | return &quic_sk(sk)->cong; 168 | } 169 | 170 | static inline struct quic_pnspace *quic_pnspace(const struct sock *sk, u8 level) 171 | { 172 | return &quic_sk(sk)->space[level % QUIC_CRYPTO_EARLY]; 173 | } 174 | 175 | static inline struct quic_crypto *quic_crypto(const struct sock *sk, u8 level) 176 | { 177 | return &quic_sk(sk)->crypto[level]; 178 | } 179 | 180 | static inline struct quic_outqueue *quic_outq(const struct sock *sk) 181 | { 182 | return &quic_sk(sk)->outq; 183 | } 184 | 185 | static inline struct quic_inqueue *quic_inq(const struct sock *sk) 186 | { 187 | return &quic_sk(sk)->inq; 188 | } 189 | 190 | static inline struct quic_packet *quic_packet(const struct sock *sk) 191 | { 192 | return &quic_sk(sk)->packet; 193 | } 194 | 195 | static inline void *quic_timer(const struct sock *sk, u8 type) 196 | { 197 | return (void *)&quic_sk(sk)->timers[type]; 198 | } 199 | 200 | static inline bool quic_is_establishing(struct sock *sk) 201 | { 202 | return sk->sk_state == QUIC_SS_ESTABLISHING; 203 | } 204 | 205 | static inline bool quic_is_established(struct sock *sk) 206 | { 207 | return sk->sk_state == QUIC_SS_ESTABLISHED; 208 | } 209 | 210 | static inline bool quic_is_listen(struct sock *sk) 211 | { 212 | return sk->sk_state == QUIC_SS_LISTENING; 213 | } 214 | 215 | static inline bool quic_is_closed(struct sock *sk) 216 | { 217 | return sk->sk_state == QUIC_SS_CLOSED; 218 | } 219 | 220 | static inline void quic_set_state(struct sock *sk, int state) 221 | { 222 | struct net *net = sock_net(sk); 223 | int mib; 224 | 225 | if (sk->sk_state == state) 226 | return; 227 | 228 | if (state == QUIC_SS_ESTABLISHED) { 229 | mib = quic_is_serv(sk) ? QUIC_MIB_CONN_PASSIVEESTABS 230 | : QUIC_MIB_CONN_ACTIVEESTABS; 231 | QUIC_INC_STATS(net, mib); 232 | QUIC_INC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS); 233 | } else if (quic_is_established(sk)) { 234 | QUIC_DEC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS); 235 | } 236 | 237 | if (state == QUIC_SS_CLOSED) 238 | sk->sk_prot->unhash(sk); 239 | 240 | inet_sk_set_state(sk, state); 241 | sk->sk_state_change(sk); 242 | } 243 | 244 | static inline bool quic_under_memory_pressure(const struct sock *sk) 245 | { 246 | if (mem_cgroup_sockets_enabled && sk->sk_memcg && 247 | mem_cgroup_under_socket_pressure(sk->sk_memcg)) 248 | return true; 249 | 250 | return !!READ_ONCE(*sk->sk_prot->memory_pressure); 251 | } 252 | 253 | struct sock *quic_sock_lookup(struct sk_buff *skb, union quic_addr *sa, union quic_addr *da, 254 | struct quic_conn_id *dcid); 255 | struct sock *quic_listen_sock_lookup(struct sk_buff *skb, union quic_addr *sa, 256 | union quic_addr *da); 257 | int quic_accept_sock_exists(struct sock *sk, struct sk_buff *skb); 258 | 259 | int quic_request_sock_enqueue(struct sock *sk, struct quic_conn_id *odcid, u8 retry); 260 | bool quic_request_sock_exists(struct sock *sk); 261 | 262 | #endif /* __net_quic_h__ */ 263 | -------------------------------------------------------------------------------- /modules/net/quic/stream.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | #define QUIC_DEF_STREAMS 100 12 | #define QUIC_MAX_STREAMS 4096ULL 13 | 14 | #define QUIC_STREAM_TYPE_BITS 2 15 | #define QUIC_STREAM_ID_STEP BIT(QUIC_STREAM_TYPE_BITS) 16 | 17 | #define QUIC_STREAM_TYPE_CLIENT_BIDI 0x00 18 | #define QUIC_STREAM_TYPE_SERVER_BIDI 0x01 19 | #define QUIC_STREAM_TYPE_CLIENT_UNI 0x02 20 | #define QUIC_STREAM_TYPE_SERVER_UNI 0x03 21 | 22 | struct quic_stream { 23 | struct hlist_node node; 24 | s64 id; 25 | struct { 26 | u64 last_max_bytes; 27 | u64 max_bytes; 28 | u64 window; /* congestion control in stream level? not now */ 29 | u64 offset; 30 | u64 bytes; 31 | 32 | u32 errcode; 33 | u32 frags; 34 | u8 state; 35 | 36 | u8 data_blocked:1; 37 | u8 stop_sent:1; 38 | u8 done:1; 39 | } send; 40 | struct { 41 | u64 max_bytes; 42 | u64 highest; 43 | u64 finalsz; 44 | u64 window; 45 | u64 offset; 46 | u64 bytes; 47 | 48 | u32 frags; 49 | u8 state; 50 | u8 done:1; 51 | } recv; 52 | }; 53 | 54 | struct quic_stream_table { 55 | struct quic_hash_table ht; 56 | 57 | struct { 58 | u64 max_stream_data_bidi_remote; 59 | u64 max_stream_data_bidi_local; 60 | u64 max_stream_data_uni; 61 | u64 max_streams_bidi; 62 | u64 max_streams_uni; 63 | 64 | s64 next_bidi_stream_id; 65 | s64 next_uni_stream_id; 66 | s64 max_bidi_stream_id; 67 | s64 max_uni_stream_id; 68 | s64 active_stream_id; 69 | 70 | u8 bidi_blocked:1; 71 | u8 uni_blocked:1; 72 | u16 streams_bidi; 73 | u16 streams_uni; 74 | } send; 75 | struct { 76 | u64 max_stream_data_bidi_remote; 77 | u64 max_stream_data_bidi_local; 78 | u64 max_stream_data_uni; 79 | u64 max_streams_bidi; 80 | u64 max_streams_uni; 81 | 82 | s64 next_bidi_stream_id; 83 | s64 next_uni_stream_id; 84 | s64 max_bidi_stream_id; 85 | s64 max_uni_stream_id; 86 | 87 | u8 bidi_pending:1; 88 | u8 uni_pending:1; 89 | u16 streams_bidi; 90 | u16 streams_uni; 91 | } recv; 92 | }; 93 | 94 | static inline u64 quic_stream_id_to_streams(s64 stream_id) 95 | { 96 | return (u64)(stream_id >> QUIC_STREAM_TYPE_BITS) + 1; 97 | } 98 | 99 | static inline s64 quic_stream_streams_to_id(u64 streams, u8 type) 100 | { 101 | return (s64)((streams - 1) << QUIC_STREAM_TYPE_BITS) | type; 102 | } 103 | 104 | struct quic_stream *quic_stream_send_get(struct quic_stream_table *streams, s64 stream_id, 105 | u32 flags, bool is_serv); 106 | struct quic_stream *quic_stream_recv_get(struct quic_stream_table *streams, s64 stream_id, 107 | bool is_serv); 108 | void quic_stream_send_put(struct quic_stream_table *streams, struct quic_stream *stream, 109 | bool is_serv); 110 | void quic_stream_recv_put(struct quic_stream_table *streams, struct quic_stream *stream, 111 | bool is_serv); 112 | 113 | bool quic_stream_max_streams_update(struct quic_stream_table *streams, s64 *max_uni, s64 *max_bidi); 114 | struct quic_stream *quic_stream_find(struct quic_stream_table *streams, s64 stream_id); 115 | bool quic_stream_id_send_overflow(struct quic_stream_table *streams, s64 stream_id); 116 | bool quic_stream_id_send_exceeds(struct quic_stream_table *streams, s64 stream_id); 117 | 118 | void quic_stream_get_param(struct quic_stream_table *streams, struct quic_transport_param *p, 119 | bool is_serv); 120 | void quic_stream_set_param(struct quic_stream_table *streams, struct quic_transport_param *p, 121 | bool is_serv); 122 | void quic_stream_free(struct quic_stream_table *streams); 123 | int quic_stream_init(struct quic_stream_table *streams); 124 | -------------------------------------------------------------------------------- /modules/net/quic/timer.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-or-later 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Initialization/cleanup for QUIC protocol support. 8 | * 9 | * Written or modified by: 10 | * Xin Long 11 | */ 12 | 13 | #include 14 | 15 | #include "socket.h" 16 | 17 | void quic_timer_sack_handler(struct sock *sk) 18 | { 19 | struct quic_pnspace *space = quic_pnspace(sk, QUIC_CRYPTO_APP); 20 | struct quic_inqueue *inq = quic_inq(sk); 21 | struct quic_connection_close close = {}; 22 | 23 | if (quic_is_closed(sk)) 24 | return; 25 | 26 | if (inq->sack_flag == QUIC_SACK_FLAG_NONE) { 27 | quic_inq_event_recv(sk, QUIC_EVENT_CONNECTION_CLOSE, &close); 28 | quic_set_state(sk, QUIC_SS_CLOSED); 29 | 30 | pr_debug("%s: idle timeout\n", __func__); 31 | return; 32 | } 33 | 34 | if (inq->sack_flag == QUIC_SACK_FLAG_APP) { 35 | space->need_sack = 1; 36 | space->sack_path = 0; 37 | } 38 | 39 | quic_outq_transmit(sk); 40 | inq->sack_flag = QUIC_SACK_FLAG_NONE; 41 | quic_timer_start(sk, QUIC_TIMER_IDLE, inq->timeout); 42 | } 43 | 44 | static void quic_timer_sack_timeout(struct timer_list *t) 45 | { 46 | struct quic_sock *qs = from_timer(qs, t, timers[QUIC_TIMER_SACK].t); 47 | struct sock *sk = &qs->inet.sk; 48 | 49 | bh_lock_sock(sk); 50 | if (sock_owned_by_user(sk)) { 51 | if (!test_and_set_bit(QUIC_SACK_DEFERRED, &sk->sk_tsq_flags)) 52 | sock_hold(sk); 53 | goto out; 54 | } 55 | 56 | quic_timer_sack_handler(sk); 57 | out: 58 | bh_unlock_sock(sk); 59 | sock_put(sk); 60 | } 61 | 62 | void quic_timer_loss_handler(struct sock *sk) 63 | { 64 | if (quic_is_closed(sk)) 65 | return; 66 | 67 | quic_outq_transmit_pto(sk); 68 | } 69 | 70 | static void quic_timer_loss_timeout(struct timer_list *t) 71 | { 72 | struct quic_sock *qs = from_timer(qs, t, timers[QUIC_TIMER_LOSS].t); 73 | struct sock *sk = &qs->inet.sk; 74 | 75 | bh_lock_sock(sk); 76 | if (sock_owned_by_user(sk)) { 77 | if (!test_and_set_bit(QUIC_LOSS_DEFERRED, &sk->sk_tsq_flags)) 78 | sock_hold(sk); 79 | goto out; 80 | } 81 | 82 | quic_timer_loss_handler(sk); 83 | out: 84 | bh_unlock_sock(sk); 85 | sock_put(sk); 86 | } 87 | 88 | #define QUIC_MAX_ALT_PROBES 3 89 | 90 | void quic_timer_path_handler(struct sock *sk) 91 | { 92 | struct quic_path_group *paths = quic_paths(sk); 93 | u8 path; 94 | 95 | if (quic_is_closed(sk)) 96 | return; 97 | 98 | path = quic_path_alt_state(paths, QUIC_PATH_ALT_PROBING); 99 | if (!path) 100 | goto out; 101 | 102 | if (paths->alt_probes++ < QUIC_MAX_ALT_PROBES) 103 | goto out; 104 | 105 | path = 0; 106 | quic_path_free(sk, paths, 1); 107 | 108 | out: 109 | quic_outq_transmit_frame(sk, QUIC_FRAME_PATH_CHALLENGE, &path, path, false); 110 | quic_timer_reset_path(sk); 111 | } 112 | 113 | static void quic_timer_path_timeout(struct timer_list *t) 114 | { 115 | struct quic_sock *qs = from_timer(qs, t, timers[QUIC_TIMER_PATH].t); 116 | struct sock *sk = &qs->inet.sk; 117 | 118 | bh_lock_sock(sk); 119 | if (sock_owned_by_user(sk)) { 120 | if (!test_and_set_bit(QUIC_PATH_DEFERRED, &sk->sk_tsq_flags)) 121 | sock_hold(sk); 122 | goto out; 123 | } 124 | 125 | quic_timer_path_handler(sk); 126 | out: 127 | bh_unlock_sock(sk); 128 | sock_put(sk); 129 | } 130 | 131 | void quic_timer_reset_path(struct sock *sk) 132 | { 133 | struct quic_cong *cong = quic_cong(sk); 134 | u64 timeout = cong->pto * 2; 135 | 136 | if (timeout < QUIC_MIN_PATH_TIMEOUT) 137 | timeout = QUIC_MIN_PATH_TIMEOUT; 138 | quic_timer_reset(sk, QUIC_TIMER_PATH, timeout); 139 | } 140 | 141 | void quic_timer_pmtu_handler(struct sock *sk) 142 | { 143 | if (quic_is_closed(sk)) 144 | return; 145 | 146 | quic_outq_transmit_probe(sk); 147 | } 148 | 149 | static void quic_timer_pmtu_timeout(struct timer_list *t) 150 | { 151 | struct quic_sock *qs = from_timer(qs, t, timers[QUIC_TIMER_PMTU].t); 152 | struct sock *sk = &qs->inet.sk; 153 | 154 | bh_lock_sock(sk); 155 | if (sock_owned_by_user(sk)) { 156 | if (!test_and_set_bit(QUIC_PMTU_DEFERRED, &sk->sk_tsq_flags)) 157 | sock_hold(sk); 158 | goto out; 159 | } 160 | 161 | quic_timer_pmtu_handler(sk); 162 | out: 163 | bh_unlock_sock(sk); 164 | sock_put(sk); 165 | } 166 | 167 | void quic_timer_pace_handler(struct sock *sk) 168 | { 169 | if (quic_is_closed(sk)) 170 | return; 171 | 172 | quic_outq_transmit(sk); 173 | } 174 | 175 | static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr) 176 | { 177 | struct quic_sock *qs = container_of(hr, struct quic_sock, timers[QUIC_TIMER_PACE].hr); 178 | struct sock *sk = &qs->inet.sk; 179 | 180 | bh_lock_sock(sk); 181 | if (sock_owned_by_user(sk)) { 182 | if (!test_and_set_bit(QUIC_TSQ_DEFERRED, &sk->sk_tsq_flags)) 183 | sock_hold(sk); 184 | goto out; 185 | } 186 | 187 | quic_timer_pace_handler(sk); 188 | out: 189 | bh_unlock_sock(sk); 190 | sock_put(sk); 191 | return HRTIMER_NORESTART; 192 | } 193 | 194 | void quic_timer_reset(struct sock *sk, u8 type, u64 timeout) 195 | { 196 | struct timer_list *t = quic_timer(sk, type); 197 | 198 | if (timeout && !mod_timer(t, jiffies + usecs_to_jiffies(timeout))) 199 | sock_hold(sk); 200 | } 201 | 202 | void quic_timer_start(struct sock *sk, u8 type, u64 timeout) 203 | { 204 | struct timer_list *t; 205 | struct hrtimer *hr; 206 | 207 | if (type == QUIC_TIMER_PACE) { 208 | hr = quic_timer(sk, type); 209 | 210 | if (!hrtimer_is_queued(hr)) { 211 | hrtimer_start(hr, ns_to_ktime(timeout), HRTIMER_MODE_ABS_PINNED_SOFT); 212 | sock_hold(sk); 213 | } 214 | return; 215 | } 216 | 217 | t = quic_timer(sk, type); 218 | if (timeout && !timer_pending(t)) { 219 | if (!mod_timer(t, jiffies + usecs_to_jiffies(timeout))) 220 | sock_hold(sk); 221 | } 222 | } 223 | 224 | void quic_timer_stop(struct sock *sk, u8 type) 225 | { 226 | if (type == QUIC_TIMER_PACE) 227 | return; 228 | if (timer_delete(quic_timer(sk, type))) 229 | sock_put(sk); 230 | } 231 | 232 | void quic_timer_init(struct sock *sk) 233 | { 234 | struct hrtimer *hr; 235 | 236 | timer_setup(quic_timer(sk, QUIC_TIMER_LOSS), quic_timer_loss_timeout, 0); 237 | timer_setup(quic_timer(sk, QUIC_TIMER_SACK), quic_timer_sack_timeout, 0); 238 | timer_setup(quic_timer(sk, QUIC_TIMER_PATH), quic_timer_path_timeout, 0); 239 | timer_setup(quic_timer(sk, QUIC_TIMER_PMTU), quic_timer_pmtu_timeout, 0); 240 | 241 | hr = quic_timer(sk, QUIC_TIMER_PACE); 242 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0) 243 | hrtimer_setup(hr, quic_timer_pace_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_SOFT); 244 | #else 245 | hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_SOFT); 246 | hr->function = quic_timer_pace_timeout; 247 | #endif 248 | } 249 | 250 | void quic_timer_free(struct sock *sk) 251 | { 252 | quic_timer_stop(sk, QUIC_TIMER_LOSS); 253 | quic_timer_stop(sk, QUIC_TIMER_SACK); 254 | quic_timer_stop(sk, QUIC_TIMER_PATH); 255 | quic_timer_stop(sk, QUIC_TIMER_PMTU); 256 | quic_timer_stop(sk, QUIC_TIMER_PACE); 257 | } 258 | -------------------------------------------------------------------------------- /modules/net/quic/timer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 | /* QUIC kernel implementation 3 | * (C) Copyright Red Hat Corp. 2023 4 | * 5 | * This file is part of the QUIC kernel implementation 6 | * 7 | * Written or modified by: 8 | * Xin Long 9 | */ 10 | 11 | enum { 12 | QUIC_TIMER_LOSS, 13 | QUIC_TIMER_SACK, 14 | QUIC_TIMER_PATH, 15 | QUIC_TIMER_PMTU, 16 | QUIC_TIMER_PACE, 17 | QUIC_TIMER_MAX, 18 | QUIC_TIMER_IDLE = QUIC_TIMER_SACK, 19 | }; 20 | 21 | struct quic_timer { 22 | union { 23 | struct timer_list t; 24 | struct hrtimer hr; 25 | }; 26 | }; 27 | 28 | #define QUIC_MIN_PROBE_TIMEOUT 5000000 29 | 30 | #define QUIC_MIN_PATH_TIMEOUT 1500000 31 | 32 | #define QUIC_MIN_IDLE_TIMEOUT 1000000 33 | #define QUIC_DEF_IDLE_TIMEOUT 30000000 34 | 35 | void quic_timer_reset(struct sock *sk, u8 type, u64 timeout); 36 | void quic_timer_start(struct sock *sk, u8 type, u64 timeout); 37 | void quic_timer_stop(struct sock *sk, u8 type); 38 | void quic_timer_init(struct sock *sk); 39 | void quic_timer_free(struct sock *sk); 40 | 41 | void quic_timer_reset_path(struct sock *sk); 42 | 43 | void quic_timer_loss_handler(struct sock *sk); 44 | void quic_timer_pace_handler(struct sock *sk); 45 | void quic_timer_path_handler(struct sock *sk); 46 | void quic_timer_sack_handler(struct sock *sk); 47 | void quic_timer_pmtu_handler(struct sock *sk); 48 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = keys runtest.sh 2 | 3 | noinst_PROGRAMS = func_test perf_test sample_test ticket_test alpn_test 4 | 5 | AM_CPPFLAGS = -I$(top_builddir)/libquic/ -I$(top_builddir)/modules/include/uapi/ 6 | AM_CFLAGS = -Werror -Wall -Wformat-signedness $(LIBGNUTLS_CFLAGS) 7 | LDADD = $(top_builddir)/libquic/libquic.la $(LIBGNUTLS_LIBS) 8 | 9 | func_test_SOURCE = func_test.c 10 | perf_test_SOURCE = perf_test.c 11 | alpn_test_SOURCE = alpn_test.c 12 | ticket_test_SOURCE = ticket_test.c 13 | sample_test_SOURCE = sample_test.c 14 | 15 | http3_test: http3_test.c 16 | $(LIBTOOL) --mode=link $(CC) $^ -o $@ -lnghttp3 \ 17 | $(LDADD) $(AM_CPPFLAGS) $(AM_CFLAGS) 18 | 19 | check: 20 | ./runtest.sh $(tests) 21 | 22 | CLEANFILES = http3_test 23 | DISTCLEANFILES = keys/*.pem keys/*.ext keys/*.txt 24 | MAINTAINERCLEANFILES = Makefile.in 25 | -------------------------------------------------------------------------------- /tests/alpn_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static const char *parse_address( 13 | char const *address, char const *port, struct sockaddr_storage *sas) 14 | { 15 | struct addrinfo hints = {0}; 16 | struct addrinfo *res; 17 | int rc; 18 | 19 | hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; 20 | hints.ai_family = AF_UNSPEC; 21 | hints.ai_socktype = SOCK_DGRAM; 22 | 23 | rc = getaddrinfo(address, port, &hints, &res); 24 | if (rc != 0) 25 | return gai_strerror(rc); 26 | memcpy(sas, res->ai_addr, res->ai_addrlen); 27 | freeaddrinfo(res); 28 | return NULL; 29 | } 30 | 31 | static unsigned short get_port(struct sockaddr_storage *sas) 32 | { 33 | if (sas->ss_family == AF_INET) 34 | return ntohs(((struct sockaddr_in *)sas)->sin_port); 35 | return ntohs(((struct sockaddr_in6 *)sas)->sin6_port); 36 | } 37 | 38 | static void set_port(struct sockaddr_storage *sas, unsigned short port) 39 | { 40 | if (sas->ss_family == AF_INET) 41 | ((struct sockaddr_in *)sas)->sin_port = htons(port); 42 | else 43 | ((struct sockaddr_in6 *)sas)->sin6_port = htons(port); 44 | } 45 | 46 | static void print_address(char *info, struct sockaddr_storage *sas) 47 | { 48 | struct sockaddr_in6 *sain6 = (struct sockaddr_in6 *)sas; 49 | struct sockaddr_in *sain = (struct sockaddr_in *)sas; 50 | char ip_str[INET6_ADDRSTRLEN]; 51 | int port; 52 | 53 | if (sas->ss_family == AF_INET) { 54 | inet_ntop(AF_INET, &(sain->sin_addr), ip_str, INET_ADDRSTRLEN); 55 | port = ntohs(sain->sin_port); 56 | printf("%s: %s:%d\n", info, ip_str, port); 57 | return; 58 | } 59 | inet_ntop(AF_INET6, &(sain6->sin6_addr), ip_str, INET6_ADDRSTRLEN); 60 | port = ntohs(sain6->sin6_port); 61 | printf("%s: %s:%d\n", info, ip_str, port); 62 | } 63 | 64 | static int do_client_alpn(char *ip, int port, char *alpn, char *preferred_addr, int preferred_port) 65 | { 66 | struct sockaddr_storage sa = {}, pa = {}; 67 | char port_string[16]; 68 | unsigned int len; 69 | int ret, sockfd; 70 | const char *rc; 71 | char msg[50]; 72 | 73 | sprintf(port_string, "%d", port); 74 | rc = parse_address(ip, port_string, &sa); 75 | if (rc != NULL) { 76 | printf("parse address failed: %s\n", rc); 77 | return -1; 78 | } 79 | 80 | sprintf(port_string, "%d", preferred_port); 81 | rc = parse_address(preferred_addr, port_string, &pa); 82 | if (rc != NULL) { 83 | printf("parse address failed: %s\n", rc); 84 | return -1; 85 | } 86 | 87 | sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_QUIC); 88 | if (sockfd < 0) { 89 | printf("socket create failed\n"); 90 | return -1; 91 | } 92 | 93 | if (connect(sockfd, (struct sockaddr *)&sa, sizeof(sa))) { 94 | printf("socket connect failed\n"); 95 | return -1; 96 | } 97 | 98 | if (quic_client_handshake(sockfd, NULL, NULL, alpn)) 99 | return -1; 100 | 101 | strcpy(msg, "hello quic server!"); 102 | ret = send(sockfd, msg, strlen(msg), MSG_SYN | MSG_FIN); 103 | if (ret == -1) { 104 | printf("send error %d %d\n", ret, errno); 105 | return -1; 106 | } 107 | printf("send %d\n", ret); 108 | 109 | memset(msg, 0, sizeof(msg)); 110 | ret = recv(sockfd, msg, sizeof(msg), 0); 111 | if (ret == -1) { 112 | printf("recv error %d %d\n", ret, errno); 113 | return 1; 114 | } 115 | printf("recv: \"%s\", len: %d\n", msg, ret); 116 | 117 | sleep(1); 118 | memset(&sa, 0, sizeof(sa)); 119 | len = sizeof(sa); 120 | ret = getpeername(sockfd, (struct sockaddr *)&sa, &len); 121 | if (ret == -1) { 122 | printf("socket getpeername error %d\n", errno); 123 | return -1; 124 | } 125 | print_address("PEER IP:PORT", &sa); 126 | if (memcmp(&sa, &pa, sizeof(sa))) { 127 | print_address("EXPECTED IP:PORT", &pa); 128 | return -1; 129 | } 130 | 131 | sleep(1); 132 | close(sockfd); 133 | return 0; 134 | } 135 | 136 | static int do_client(int argc, char *argv[]) 137 | { 138 | char *ip, *pref; 139 | int port; 140 | 141 | if (argc < 3) { 142 | printf("%s client [PREF ADDR]\n", argv[0]); 143 | return 0; 144 | } 145 | 146 | ip = argv[2]; 147 | pref = argv[4]; 148 | port = atoi(argv[3]); 149 | if (do_client_alpn(ip, port, "smbd", pref, port + 1)) 150 | return -1; 151 | if (do_client_alpn(ip, port, "h3", pref, port + 2)) 152 | return -1; 153 | if (do_client_alpn(ip, port, "ksmbd", pref, port + 3)) 154 | return -1; 155 | 156 | return 0; 157 | } 158 | 159 | static int server_handshake(int sockfd, const char *pkey, const char *cert, 160 | const char *alpns, char *alpn, size_t *alpn_len) 161 | { 162 | gnutls_certificate_credentials_t cred; 163 | gnutls_session_t session; 164 | int ret; 165 | 166 | ret = gnutls_certificate_allocate_credentials(&cred); 167 | if (ret) 168 | goto err; 169 | ret = gnutls_certificate_set_x509_system_trust(cred); 170 | if (ret < 0) 171 | goto err_cred; 172 | ret = gnutls_certificate_set_x509_key_file(cred, cert, pkey, GNUTLS_X509_FMT_PEM); 173 | if (ret) 174 | goto err_cred; 175 | ret = gnutls_init(&session, GNUTLS_SERVER | GNUTLS_NO_AUTO_SEND_TICKET); 176 | if (ret) 177 | goto err_cred; 178 | ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); 179 | if (ret) 180 | goto err_session; 181 | 182 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL); 183 | if (ret) 184 | goto err_session; 185 | 186 | if (alpns) { 187 | ret = quic_session_set_alpn(session, alpns, strlen(alpns)); 188 | if (ret) 189 | goto err_session; 190 | } 191 | 192 | gnutls_transport_set_int(session, sockfd); 193 | 194 | ret = quic_handshake(session); 195 | if (ret) 196 | goto err_session; 197 | 198 | if (alpns) 199 | ret = quic_session_get_alpn(session, alpn, alpn_len); 200 | 201 | err_session: 202 | gnutls_deinit(session); 203 | err_cred: 204 | gnutls_certificate_free_credentials(cred); 205 | err: 206 | return ret; 207 | } 208 | 209 | static int do_server(int argc, char *argv[]) 210 | { 211 | struct sockaddr_storage sa = {}, pa = {}; 212 | char alpns[20] = "smbd, h3, ksmbd"; 213 | int listenfd, sockfd, ret, i = 0; 214 | unsigned int addrlen, len; 215 | int preferred_port; 216 | char msg[50] = {}; 217 | char const *rc; 218 | 219 | if (argc < 5) { 220 | printf("%s server " 221 | " [PREF ADDR]\n", argv[0]); 222 | return 0; 223 | } 224 | 225 | rc = parse_address(argv[2], argv[3], &sa); 226 | if (rc != NULL) { 227 | printf("parse address failed: %s\n", rc); 228 | return -1; 229 | } 230 | 231 | listenfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_QUIC); 232 | if (listenfd < 0) { 233 | printf("socket create failed\n"); 234 | return -1; 235 | } 236 | len = strlen(alpns); 237 | if (setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_ALPN, alpns, len)) { 238 | printf("socket setsockopt alpn failed %u\n", len); 239 | return -1; 240 | } 241 | 242 | if (bind(listenfd, (struct sockaddr *)&sa, sizeof(sa))) { 243 | printf("socket bind failed\n"); 244 | return -1; 245 | } 246 | if (listen(listenfd, 1)) { 247 | printf("socket listen failed\n"); 248 | return -1; 249 | } 250 | while (i++ < 3) { 251 | char alpn[20] = {}; 252 | size_t alpn_len; 253 | 254 | addrlen = sizeof(sa); 255 | sockfd = accept(listenfd, (struct sockaddr *)&sa, &addrlen); 256 | if (sockfd < 0) { 257 | printf("socket accept failed %d %d\n", errno, sockfd); 258 | return -1; 259 | } 260 | 261 | /* call setsockopt(QUIC_SOCKOPT_CONNECTION_MIGRATION) before handshake 262 | * to set up the preferred_address transport param. 263 | */ 264 | ret = getsockname(sockfd, (struct sockaddr *)&sa, &addrlen); 265 | if (ret == -1) { 266 | printf("socket getsockname error %d\n", errno); 267 | return -1; 268 | } 269 | preferred_port = get_port(&sa) + i; 270 | if (argv[6]) { 271 | rc = parse_address(argv[6], "0", &pa); 272 | if (rc != NULL) { 273 | printf("parse address failed: %s\n", rc); 274 | return -1; 275 | } 276 | addrlen = sizeof(pa); 277 | } 278 | set_port(&pa, preferred_port); /* you can also change addr */ 279 | ret = setsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_CONNECTION_MIGRATION, &pa, addrlen); 280 | if (ret == -1) { 281 | printf("socket setsockopt migration error %d\n", errno); 282 | return -1; 283 | } 284 | 285 | alpn_len = sizeof(alpn) - 1; 286 | if (server_handshake(sockfd, argv[4], argv[5], alpns, alpn, &alpn_len)) 287 | return -1; 288 | 289 | printf("ALPN: %s\n", alpn); 290 | 291 | memset(msg, 0, sizeof(msg)); 292 | ret = recv(sockfd, msg, sizeof(msg), 0); 293 | if (ret == -1) { 294 | printf("recv error %d %d\n", ret, errno); 295 | return 1; 296 | } 297 | printf("recv: \"%s\", len: %d\n", msg, ret); 298 | 299 | strcpy(msg, "hello quic client!"); 300 | ret = send(sockfd, msg, strlen(msg), MSG_SYN | MSG_FIN); 301 | if (ret == -1) { 302 | printf("send error %d %d\n", ret, errno); 303 | return -1; 304 | } 305 | printf("send %d\n", ret); 306 | 307 | sleep(1); 308 | memset(&sa, 0, sizeof(sa)); 309 | ret = getsockname(sockfd, (struct sockaddr *)&sa, &addrlen); 310 | if (ret == -1) { 311 | printf("socket getsockname error %d\n", errno); 312 | return -1; 313 | } 314 | print_address("LOCAL IP:PORT: ", &sa); 315 | if (memcmp(&sa, &pa, sizeof(sa))) { 316 | print_address("EXPECTED IP:PORT: ", &pa); 317 | return -1; 318 | } 319 | 320 | recv(sockfd, msg, sizeof(msg), 0); 321 | close(sockfd); 322 | } 323 | close(listenfd); 324 | return 0; 325 | } 326 | 327 | int main(int argc, char *argv[]) 328 | { 329 | if (argc < 2 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) { 330 | printf("%s server|client ...\n", argv[0]); 331 | return 0; 332 | } 333 | 334 | quic_set_log_level(LOG_NOTICE); 335 | 336 | if (!strcmp(argv[1], "client")) 337 | return do_client(argc, argv); 338 | 339 | return do_server(argc, argv); 340 | } 341 | -------------------------------------------------------------------------------- /tests/interop/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM martenseemann/quic-network-simulator-endpoint:latest 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y git g++ make autoconf automake libtool pkg-config gnutls-dev 5 | 6 | RUN git clone --recurse-submodules https://github.com/ngtcp2/nghttp3 7 | RUN cd nghttp3 && autoreconf -i && ./configure --prefix=/usr/ && \ 8 | make -j$(nproc) && make install && cd .. && rm -rf nghttp3 9 | 10 | RUN git clone https://github.com/lxin/quic 11 | RUN cd quic && ./autogen.sh && \ 12 | ./configure --prefix=/usr --without-modules && make -j$(nproc) && make install && cd .. 13 | 14 | COPY interop_test.c interop_test.c 15 | RUN gcc interop_test.c -o interop_test -lnghttp3 -lquic -lgnutls && \ 16 | rm -rf quic interop_test.c 17 | 18 | COPY run_endpoint.sh . 19 | RUN chmod +x run_endpoint.sh 20 | ENTRYPOINT [ "./run_endpoint.sh" ] 21 | -------------------------------------------------------------------------------- /tests/interop/run_endpoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Set up the routing needed for the simulation. 5 | /setup.sh 6 | 7 | if [ "$ROLE" == "client" ]; then 8 | # Wait for the simulator to start up. 9 | /wait-for-it.sh sim:57832 -s -t 10 10 | echo "Starting QUIC client..." 11 | echo "Test case: $TESTCASE" 12 | echo "Requests: $REQUESTS" 13 | echo "Keylogfile: $SSLKEYLOGFILE" 14 | 15 | if [ "$TESTCASE" == "resumption" ] || [ "$TESTCASE" == "zerortt" ]; then 16 | REQ="${REQUESTS%% *}" 17 | echo "./interop_test -c -D /downloads -S ./session.bin -T ./tp.bin -E $TESTCASE $REQ" 18 | ./interop_test -c -D /downloads -S ./session.bin -T ./tp.bin -E $TESTCASE $REQ 19 | 20 | echo "./interop_test -c -D /downloads -S ./session.bin -T ./tp.bin -E $TESTCASE \"${REQUESTS#$REQ}\"" 21 | ./interop_test -c -D /downloads -S ./session.bin -T ./tp.bin -E $TESTCASE "${REQUESTS#$REQ}" 22 | elif [ "$TESTCASE" == "multiconnect" ]; then 23 | for REQ in $REQUESTS; do 24 | echo "./interop_test -c -D /downloads -E $TESTCASE \"$REQ\"" 25 | ./interop_test -c -D /downloads -E $TESTCASE "$REQ" 26 | done 27 | else 28 | echo "./interop_test -c -D /downloads -E $TESTCASE \"$REQUESTS\"" 29 | ./interop_test -c -D /downloads -E $TESTCASE "$REQUESTS" 30 | fi 31 | else 32 | echo "Running QUIC server." 33 | echo "Test case: $TESTCASE" 34 | echo "Keylogfile: $SSLKEYLOGFILE" 35 | 36 | echo "./interop_test -s -D /www -C /certs/cert.pem -P /certs/priv.key -E $TESTCASE :::443" 37 | ./interop_test -s -D /www -C /certs/cert.pem -P /certs/priv.key -E $TESTCASE :::443 38 | fi 39 | -------------------------------------------------------------------------------- /tests/keys/ca_cert_pkey_psk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" = "clean" ]; then 4 | rm -rf *.pem *.ext *.txt 5 | exit 0 6 | fi 7 | 8 | # create PSK files 9 | psk_id1=test1 10 | psk_id2=test2 11 | psk_key1=b8d3a37be2c9a08eaf25cf6abe602ecc94417f8ba6211a58b8d0a3fb0d2e3a90 12 | psk_key2=b8d3a37be2c9a08eaf25cf6abe602ecc94417f8ba6211a58b8d0a3fb0d2e3a91 13 | 14 | if [ "$1" = "psk-keyring" ]; then 15 | # create PSK keyring 16 | KEYRING=`keyctl newring quic @s` 17 | keyctl setperm $KEYRING 0x3f1f1f1f 18 | keyctl link $KEYRING @u 19 | KEY1=$(keyctl add user $psk_id1 `echo $psk_key1 | xxd -r -p` %:quic) 20 | keyctl setperm $KEY1 0x3f1f1f1f 21 | KEY2=$(keyctl add user $psk_id2 `echo $psk_key2 | xxd -r -p` %:quic) 22 | keyctl setperm $KEY2 0x3f1f1f1f 23 | keyctl unlink $KEYRING @s 24 | echo "keyring $KEYRING with user keys $KEY1 and $KEY2 is created" 25 | exit 0 26 | fi 27 | 28 | cat <client-psk.txt 29 | $psk_id2:$psk_key2 30 | $psk_id1:$psk_key1 31 | EOF 32 | 33 | cat <server-psk.txt 34 | $psk_id1:$psk_key1 35 | $psk_id2:$psk_key2 36 | EOF 37 | 38 | # create CA 39 | openssl req -newkey rsa:2048 -nodes -keyout ca-key.pem -x509 -days 365 -out ca-cert.pem -subj "/C=CN/ST=ON/L=Ottawa/O=RH/OU=NET/CN=lucien.xin@gmail.com" 40 | 41 | cat > server.ext << EOF 42 | authorityKeyIdentifier=keyid,issuer 43 | basicConstraints=CA:FALSE 44 | keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment 45 | subjectAltName = @alt_names 46 | [alt_names] 47 | DNS.1 = server.test 48 | EOF 49 | 50 | # create server cert and sign it 51 | openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-req.pem -subj "/C=CA/ST=ON/L=Ottawa/O=RH/OU=NET/CN=lucien.xin@gmail.com" 52 | openssl x509 -req -days 186 -set_serial 01 -in server-req.pem -out server-cert.pem -CA ca-cert.pem -CAkey ca-key.pem -extfile server.ext 53 | 54 | # create client cert and sign it 55 | openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-req.pem -subj "/C=CA/ST=ON/L=Ottawa/O=RH/OU=NET/CN=lucien.xin@gmail.com" 56 | openssl x509 -req -days 186 -set_serial 01 -in client-req.pem -out client-cert.pem -CA ca-cert.pem -CAkey ca-key.pem 57 | -------------------------------------------------------------------------------- /tests/perf_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define SND_MSG_LEN 4096 16 | #define RCV_MSG_LEN 4096 * 16 17 | #define ALPN_LEN 20 18 | #define TOT_LEN 1 * 1024 * 1024 * 1024 19 | 20 | #define SECONDS 1000000 21 | 22 | char snd_msg[SND_MSG_LEN]; 23 | char rcv_msg[RCV_MSG_LEN]; 24 | char alpn[ALPN_LEN] = "sample"; 25 | 26 | struct options { 27 | char *pkey; 28 | char *cert; 29 | char *psk; 30 | char *ca; 31 | char *addr; 32 | char *port; 33 | uint8_t is_serv; 34 | uint8_t no_crypt; 35 | uint64_t tot_len; 36 | uint64_t msg_len; 37 | }; 38 | 39 | static struct option long_options[] = { 40 | {"addr", required_argument, 0, 'a'}, 41 | {"port", required_argument, 0, 'p'}, 42 | {"pkey", required_argument, 0, 'k'}, 43 | {"cert", required_argument, 0, 'c'}, 44 | {"psk", required_argument, 0, 'i'}, 45 | {"ca", required_argument, 0, 's'}, 46 | {"msg_len", required_argument, 0, 'm'}, 47 | {"tot_len", required_argument, 0, 't'}, 48 | {"listen", no_argument, 0, 'l'}, 49 | {"no_crypt", no_argument, 0, 'x'}, 50 | {"help", no_argument, 0, 'h'}, 51 | {0, 0, 0, 0 } 52 | }; 53 | 54 | static void print_usage(char *cmd) 55 | { 56 | printf("%s:\n\n", cmd); 57 | printf(" --listen/-l: work as a server\n"); 58 | printf(" --addr/-a : server IP address\n"); 59 | printf(" --port/-p

: server port\n"); 60 | printf(" --pkey/-k : private key file\n"); 61 | printf(" --cert/-c : certificate file\n"); 62 | printf(" --psk/-i : pre-shared key file\n"); 63 | printf(" --ca/-s : ca file\n"); 64 | printf(" --help/-h : show help\n"); 65 | printf(" --msg_len/-m : msg_len to send\n"); 66 | printf(" --tot_len/-t : tot_len to send\n"); 67 | printf(" --no_crypt/-x : disable 1rtt encryption\n\n"); 68 | } 69 | 70 | static int parse_options(int argc, char *argv[], struct options *opts) 71 | { 72 | int c, option_index = 0; 73 | 74 | while (1) { 75 | c = getopt_long(argc, argv, "la:p:m:t:k:c:s:i:xh", long_options, &option_index); 76 | if (c == -1) 77 | break; 78 | 79 | switch (c) { 80 | case 'l': 81 | opts->is_serv = 1; 82 | break; 83 | case 'a': 84 | opts->addr = optarg; 85 | break; 86 | case 'p': 87 | opts->port = optarg; 88 | break; 89 | case 'c': 90 | opts->cert = optarg; 91 | break; 92 | case 's': 93 | opts->ca = optarg; 94 | break; 95 | case 'i': 96 | case 'k': 97 | opts->pkey = optarg; 98 | break; 99 | case 'm': 100 | opts->msg_len = atoi(optarg); 101 | if (opts->msg_len > SND_MSG_LEN) 102 | return -1; 103 | break; 104 | case 't': 105 | opts->tot_len = atoll(optarg); 106 | if (opts->tot_len > TOT_LEN) 107 | return -1; 108 | break; 109 | case 'x': 110 | opts->no_crypt = 1; 111 | break; 112 | case 'h': 113 | print_usage(argv[0]); 114 | return 1; 115 | default: 116 | return -1; 117 | } 118 | } 119 | 120 | if (opts->is_serv && (!opts->cert && !opts->pkey)) 121 | return -1; 122 | return 0; 123 | } 124 | 125 | static int do_server(struct options *opts) 126 | { 127 | struct quic_transport_param param = {}; 128 | uint32_t flags = 0, addrlen, len = 0; 129 | struct sockaddr_storage ra = {}; 130 | struct sockaddr_in la = {}; 131 | int ret, sockfd, listenfd; 132 | struct addrinfo *rp; 133 | int64_t sid = 0; 134 | 135 | if (getaddrinfo(opts->addr, opts->port, NULL, &rp)) { 136 | printf("getaddrinfo error\n"); 137 | return -1; 138 | } 139 | 140 | if (rp->ai_family == AF_INET6) { 141 | struct sockaddr_in6 la = {}; 142 | 143 | la.sin6_family = AF_INET6; 144 | la.sin6_port = htons(atoi(opts->port)); 145 | inet_pton(AF_INET6, opts->addr, &la.sin6_addr); 146 | listenfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_QUIC); 147 | if (listenfd < 0) { 148 | printf("socket create failed\n"); 149 | return -1; 150 | } 151 | if (bind(listenfd, (struct sockaddr *)&la, sizeof(la))) { 152 | printf("socket bind failed\n"); 153 | return -1; 154 | } 155 | goto listen; 156 | } 157 | 158 | la.sin_family = AF_INET; 159 | la.sin_port = htons(atoi(opts->port)); 160 | inet_pton(AF_INET, opts->addr, &la.sin_addr.s_addr); 161 | listenfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_QUIC); 162 | if (listenfd < 0) { 163 | printf("socket create failed\n"); 164 | return -1; 165 | } 166 | if (bind(listenfd, (struct sockaddr *)&la, sizeof(la))) { 167 | printf("socket bind failed\n"); 168 | return -1; 169 | } 170 | 171 | listen: 172 | param.grease_quic_bit = 1; 173 | param.stateless_reset = 1; 174 | param.max_idle_timeout = 120 * SECONDS; 175 | param.disable_1rtt_encryption = opts->no_crypt; 176 | if (setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, sizeof(param))) { 177 | printf("socket setsockopt transport param failed\n"); 178 | return -1; 179 | } 180 | if (setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_ALPN, alpn, strlen(alpn))) { 181 | printf("socket setsockopt alpn failed\n"); 182 | return -1; 183 | } 184 | 185 | if (listen(listenfd, 1)) { 186 | printf("socket listen failed\n"); 187 | return -1; 188 | } 189 | 190 | loop: 191 | printf("Waiting for New Socket...\n"); 192 | addrlen = sizeof(ra); 193 | sockfd = accept(listenfd, (struct sockaddr *)&ra, &addrlen); 194 | if (sockfd < 0) { 195 | printf("socket accept failed %d %d\n", errno, sockfd); 196 | return -1; 197 | } 198 | 199 | printf("accept %d\n", sockfd); 200 | 201 | if (quic_server_handshake(sockfd, opts->pkey, opts->cert, alpn)) 202 | return -1; 203 | 204 | printf("HANDSHAKE DONE\n"); 205 | 206 | while (1) { 207 | ret = quic_recvmsg(sockfd, &rcv_msg, opts->msg_len * 16, &sid, &flags); 208 | if (ret == -1) { 209 | printf("recv error %d %d\n", ret, errno); 210 | return 1; 211 | } 212 | len += ret; 213 | usleep(20); 214 | if (flags & MSG_STREAM_FIN) 215 | break; 216 | printf(" recv len: %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags); 217 | } 218 | 219 | printf("RECV DONE: tot_len %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags); 220 | 221 | flags = MSG_STREAM_FIN; 222 | strcpy(snd_msg, "recv done"); 223 | ret = quic_sendmsg(sockfd, snd_msg, strlen(snd_msg), sid, flags); 224 | if (ret == -1) { 225 | printf("send %d %d\n", ret, errno); 226 | return -1; 227 | } 228 | 229 | flags = 0; 230 | quic_recvmsg(sockfd, &rcv_msg, sizeof(rcv_msg), &sid, &flags); 231 | 232 | close(sockfd); 233 | printf("CLOSE DONE\n"); 234 | 235 | len = 0; 236 | goto loop; 237 | return 0; 238 | } 239 | 240 | static uint64_t get_now_time() 241 | { 242 | struct timespec t ; 243 | clock_gettime ( CLOCK_REALTIME , & t ) ; 244 | return t.tv_sec * 1000 + ( t.tv_nsec + 500000 ) / 1000000 ; 245 | } 246 | 247 | static int do_client(struct options *opts) 248 | { 249 | struct quic_transport_param param = {}; 250 | struct sockaddr_in ra = {}; 251 | uint32_t len = 0, flags; 252 | struct addrinfo *rp; 253 | uint64_t start, end; 254 | int ret, sockfd; 255 | int64_t sid = 0; 256 | float rate; 257 | 258 | if (getaddrinfo(opts->addr, opts->port, NULL, &rp)) { 259 | printf("getaddrinfo error\n"); 260 | return -1; 261 | } 262 | 263 | if (rp->ai_family == AF_INET6) { 264 | struct sockaddr_in6 ra = {}; 265 | 266 | sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_QUIC); 267 | if (sockfd < 0) { 268 | printf("socket create failed\n"); 269 | return -1; 270 | } 271 | 272 | param.max_idle_timeout = 120 * SECONDS; 273 | param.disable_1rtt_encryption = opts->no_crypt; 274 | if (setsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, sizeof(param))) { 275 | printf("socket setsockopt transport param failed\n"); 276 | return -1; 277 | } 278 | 279 | ra.sin6_family = AF_INET6; 280 | ra.sin6_port = htons(atoi(opts->port)); 281 | inet_pton(AF_INET6, opts->addr, &ra.sin6_addr); 282 | 283 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) { 284 | printf("socket connect failed\n"); 285 | return -1; 286 | } 287 | goto handshake; 288 | } 289 | 290 | sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_QUIC); 291 | if (sockfd < 0) { 292 | printf("socket create failed\n"); 293 | return -1; 294 | } 295 | 296 | param.max_idle_timeout = 120 * SECONDS; 297 | param.disable_1rtt_encryption = opts->no_crypt; 298 | if (setsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, sizeof(param))) { 299 | printf("socket setsockopt transport param failed\n"); 300 | return -1; 301 | } 302 | 303 | ra.sin_family = AF_INET; 304 | ra.sin_port = htons(atoi(opts->port)); 305 | inet_pton(AF_INET, opts->addr, &ra.sin_addr.s_addr); 306 | 307 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) { 308 | printf("socket connect failed\n"); 309 | return -1; 310 | } 311 | 312 | handshake: 313 | if (quic_client_handshake(sockfd, opts->pkey, NULL, alpn)) 314 | return -1; 315 | 316 | printf("HANDSHAKE DONE.\n"); 317 | 318 | start = get_now_time(); 319 | flags = MSG_STREAM_NEW; /* open stream when send first msg */ 320 | ret = quic_sendmsg(sockfd, snd_msg, opts->msg_len, sid, flags); 321 | if (ret == -1) { 322 | printf("send %d %d\n", ret, errno); 323 | return -1; 324 | } 325 | len += ret; 326 | flags = 0; 327 | while (1) { 328 | ret = quic_sendmsg(sockfd, snd_msg, opts->msg_len, sid, flags); 329 | if (ret == -1) { 330 | printf("send %d %d\n", ret, errno); 331 | return -1; 332 | } 333 | len += ret; 334 | if (!(len % (opts->msg_len * 1024))) 335 | printf(" send len: %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags); 336 | if (len > opts->tot_len - opts->msg_len) 337 | break; 338 | } 339 | flags = MSG_STREAM_FIN; /* close stream when send last msg */ 340 | ret = quic_sendmsg(sockfd, snd_msg, opts->msg_len, sid, flags); 341 | if (ret == -1) { 342 | printf("send %d %d\n", ret, errno); 343 | return -1; 344 | } 345 | len += ret; 346 | printf("SEND DONE: tot_len: %u, stream_id: %d, flags: %u.\n", len, (int)sid, flags); 347 | 348 | memset(rcv_msg, 0, sizeof(rcv_msg)); 349 | ret = quic_recvmsg(sockfd, rcv_msg, opts->msg_len * 16, &sid, &flags); 350 | if (ret == -1) { 351 | printf("recv error %d %d\n", ret, errno); 352 | return 1; 353 | } 354 | end = get_now_time(); 355 | start = end - start; 356 | rate = ((float)opts->tot_len * 8 * 1000) / 1024 / start; 357 | if (rate < 1024) 358 | printf("ALL RECVD: %.1f Kbits/Sec\n", rate); 359 | else 360 | printf("ALL RECVD: %.1f Mbits/Sec\n", rate / 1024); 361 | 362 | close(sockfd); 363 | return 0; 364 | } 365 | 366 | int main(int argc, char *argv[]) 367 | { 368 | struct options opts = {}; 369 | int ret; 370 | 371 | opts.msg_len = SND_MSG_LEN; 372 | opts.tot_len = TOT_LEN; 373 | opts.addr = "::"; 374 | opts.port = "1234"; 375 | 376 | ret = parse_options(argc, argv, &opts); 377 | if (ret) { 378 | if (ret < 0) 379 | printf("parse options error\n"); 380 | return -1; 381 | } 382 | 383 | quic_set_log_level(LOG_NOTICE); 384 | 385 | if (!opts.is_serv) 386 | return do_client(&opts); 387 | 388 | return do_server(&opts); 389 | } 390 | -------------------------------------------------------------------------------- /tests/runtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | print_start() 4 | { 5 | echo "" 6 | echo "****** ["$1"] ******" 7 | echo "" 8 | sleep 1 9 | } 10 | 11 | daemon_stop() 12 | { 13 | [ "$1" != "" ] && pkill $1 > /dev/null 2>&1 14 | sleep 3 15 | } 16 | 17 | daemon_run() 18 | { 19 | $@ > /dev/null 2>&1 & 20 | sleep 2 21 | } 22 | 23 | cleanup() 24 | { 25 | exit_code=$? 26 | ip -6 addr del ::2/128 dev lo > /dev/null 2>&1 27 | ip addr del 127.0.0.2/8 dev lo > /dev/null 2>&1 28 | tc qdisc del dev lo root netem loss 30% > /dev/null 2>&1 29 | pkill func_test > /dev/null 2>&1 30 | pkill perf_test > /dev/null 2>&1 31 | pkill alpn_test > /dev/null 2>&1 32 | pkill ticket_test > /dev/null 2>&1 33 | pkill sample_test > /dev/null 2>&1 34 | pkill http3_test > /dev/null 2>&1 35 | rmmod quic_sample_test > /dev/null 2>&1 36 | rmmod quic > /dev/null 2>&1 37 | exit $exit_code 38 | } 39 | 40 | start_tests() 41 | { 42 | make || return 1 43 | setenforce 0 > /dev/null 2>&1 44 | modprobe -a udp_tunnel ip6_udp_tunnel || return 1 45 | if [ -f ../modules/net/quic/quic.ko ]; then 46 | [ -d /sys/module/quic ] || insmod ../modules/net/quic/quic.ko || return 1 47 | else 48 | modprobe quic || return 1 49 | fi 50 | 51 | print_start "Install Keys & Certificates" 52 | pushd keys/ 53 | sh ca_cert_pkey_psk.sh || return 1 54 | if systemctl is-active --quiet tlshd; then 55 | sh ca_cert_pkey_psk.sh psk-keyring || return 1 56 | systemctl restart tlshd || return 1 57 | fi 58 | popd 59 | if [ -d /etc/pki/ca-trust/source/anchors/ ]; then 60 | install keys/ca-cert.pem /etc/pki/ca-trust/source/anchors/ca-cert.pem 61 | update-ca-trust 62 | elif [ -d /usr/local/share/ca-certificates/ ]; then 63 | install keys/ca-cert.pem /usr/local/share/ca-certificates/ca-cert.crt 64 | update-ca-certificates 65 | fi 66 | } 67 | 68 | done_tests() 69 | { 70 | echo "" 71 | echo "ALL TESTS DONE!" 72 | } 73 | 74 | http3_connect() 75 | { 76 | local url=$1 77 | 78 | echo "- $url" 79 | 80 | for i in `seq 3`; do 81 | ./http3_test -c $url > /dev/null && break 82 | echo "WARNING: retrying $i ..." 83 | done 84 | } 85 | 86 | func_tests() 87 | { 88 | print_start "Function Tests (PSK)" 89 | daemon_run ./func_test server 0.0.0.0 1234 ./keys/server-psk.txt 90 | ./func_test client 127.0.0.1 1234 ./keys/client-psk.txt || return 1 91 | daemon_stop "func_test" 92 | 93 | print_start "Function Tests (Certificate)" 94 | daemon_run ./func_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem 95 | ./func_test client 127.0.0.1 1234 || return 1 96 | daemon_stop "func_test" 97 | } 98 | 99 | perf_tests() 100 | { 101 | print_start "Performance Tests (IPv4)" 102 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem 103 | ./perf_test --addr 127.0.0.1 || return 1 104 | daemon_stop "perf_test" 105 | 106 | print_start "Performance Tests (IPv6, Disable 1RTT Encryption)" 107 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem \ 108 | --cert ./keys/server-cert.pem --no_crypt 109 | ./perf_test --addr ::1 --no_crypt || return 1 110 | daemon_stop "perf_test" 111 | 112 | print_start "Performance Tests (IPv6)" 113 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem 114 | ./perf_test --addr ::1 || return 1 115 | daemon_stop "perf_test" 116 | } 117 | 118 | netem_tests() 119 | { 120 | modprobe -q sch_netem || return 0 121 | tc qdisc add dev lo root netem loss 30% 122 | print_start "Performance Tests (IPv4, 30% packet loss on both sides)" 123 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem 124 | ./perf_test --addr 127.0.0.1 --tot_len 1048576 --msg_len 1024 || return 1 125 | daemon_stop "perf_test" 126 | 127 | print_start "Performance Tests (IPv6, 30% packet loss on both sides)" 128 | daemon_run ./perf_test -l --pkey ./keys/server-key.pem --cert ./keys/server-cert.pem 129 | ./perf_test --addr ::1 --tot_len 1048576 --msg_len 1024 || return 1 130 | daemon_stop "perf_test" 131 | tc qdisc del dev lo root netem loss 30% 132 | } 133 | 134 | http3_tests() { 135 | [ -f /usr/local/include/nghttp3/nghttp3.h -o -f /usr/include/nghttp3/nghttp3.h ] || return 0 136 | 137 | print_start "Http/3 Tests (http3_test -> Public Websites)" 138 | make http3_test > /dev/null || return 1 139 | 140 | http3_connect https://d.moritzbuhl.de/pub || return 1 # linuxquic 141 | http3_connect https://cloudflare-quic.com/ || return 1 # Cloudflare Quiche 142 | http3_connect https://quic.aiortc.org/ || return 1 # aioquic 143 | http3_connect https://facebook.com/ || return 1 # mvfst 144 | http3_connect https://nghttp2.org:4433/ || return 1 # ngtcp2 145 | http3_connect https://outlook.office.com/ || return 1 # msquic 146 | http3_connect https://www.litespeedtech.com/ || return 1 # lsquic 147 | http3_connect https://www.google.com/ || return 1 # Google quiche 148 | http3_connect https://quic.tech:8443/ || return 1 # Cloudflare Quiche 149 | http3_connect https://test.privateoctopus.com:4433 || return 1 # picoquic 150 | http3_connect https://www.haproxy.org/ || return 1 # haproxy 151 | http3_connect https://quic.nginx.org:443 || return 1 # nginx 152 | http3_connect https://interop.seemann.io || return 1 # quic-go 153 | http3_connect https://mew.org:443 || return 1 # Haskell 154 | 155 | print_start "Http/3 Tests (http3_test client -> http3_test server)" 156 | daemon_run ./http3_test -s 127.0.0.1:443 ./keys/server-key.pem ./keys/server-cert.pem 157 | ./http3_test -c https://localhost/ || return 1 158 | daemon_stop "http3_test" 159 | } 160 | 161 | tlshd_tests() 162 | { 163 | systemctl is-active --quiet tlshd || return 0 164 | 165 | print_start "Kernel Tests (kernel -> lkquic, Certificate, Sample)" 166 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then 167 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-key.pem \ 168 | ./keys/server-cert.pem sample 169 | insmod ../modules/net/quic/quic_sample_test.ko || return 1 170 | else 171 | modprobe -n quic_sample_test || return 0 172 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-key.pem \ 173 | ./keys/server-cert.pem sample 174 | modprobe quic_sample_test || return 1 175 | fi 176 | rmmod quic_sample_test 177 | dmesg | tail -n 5 178 | daemon_stop "sample_test" 179 | 180 | print_start "Kernel Tests (lkquic -> kernel, Certificate, Sample)" 181 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then 182 | daemon_run ./sample_test client 127.0.0.1 1234 none none sample 183 | insmod ../modules/net/quic/quic_sample_test.ko role=server || return 1 184 | else 185 | modprobe -n quic_sample_test || return 0 186 | daemon_run ./sample_test client 127.0.0.1 1234 none none sample 187 | modprobe quic_sample_test role=server || return 1 188 | fi 189 | rmmod quic_sample_test 190 | dmesg | tail -n 5 191 | daemon_stop "sample_test" 192 | 193 | print_start "Kernel Tests (kernel -> lkquic, PSK, Sample)" 194 | PSK=`keyctl show @u |grep test1 |awk '{print $1}'` 195 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then 196 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-psk.txt none sample 197 | insmod ../modules/net/quic/quic_sample_test.ko psk=$PSK || return 1 198 | else 199 | modprobe -n quic_sample_test || return 0 200 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-psk.txt none sample 201 | modprobe quic_sample_test psk=$PSK || return 1 202 | fi 203 | rmmod quic_sample_test 204 | dmesg | tail -n 5 205 | daemon_stop "sample_test" 206 | 207 | print_start "Kernel Tests (lkquic -> kernel, PSK, Sample)" 208 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then 209 | daemon_run ./sample_test client 127.0.0.1 1234 ./keys/client-psk.txt none sample 210 | insmod ../modules/net/quic/quic_sample_test.ko role=server psk=1 || return 1 211 | else 212 | modprobe -n quic_sample_test || return 0 213 | daemon_run ./sample_test client 127.0.0.1 1234 ./keys/client-psk.txt none sample 214 | modprobe quic_sample_test role=server psk=1 || return 1 215 | fi 216 | rmmod quic_sample_test 217 | dmesg | tail -n 5 218 | daemon_stop "sample_test" 219 | 220 | print_start "Kernel Tests (kernel -> lkquic, Certificate, Session Resumption)" 221 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then 222 | daemon_run ./ticket_test server 0.0.0.0 1234 ./keys/server-key.pem \ 223 | ./keys/server-cert.pem ticket 224 | insmod ../modules/net/quic/quic_sample_test.ko alpn=ticket || return 1 225 | else 226 | modprobe -n quic_sample_test || return 0 227 | daemon_run ./ticket_test server 0.0.0.0 1234 ./keys/server-key.pem \ 228 | ./keys/server-cert.pem ticket 229 | modprobe quic_sample_test alpn=ticket || return 1 230 | fi 231 | rmmod quic_sample_test 232 | dmesg | tail -n 5 233 | daemon_stop "ticket_test" 234 | 235 | print_start "Kernel Tests (lkquic -> kernel, Certificate, Session Resumption)" 236 | if [ -f ../modules/net/quic/quic_sample_test.ko ]; then 237 | daemon_run ./ticket_test client 127.0.0.1 1234 ticket 238 | insmod ../modules/net/quic/quic_sample_test.ko role=server alpn=ticket || return 1 239 | else 240 | modprobe -n quic_sample_test || return 0 241 | daemon_run ./ticket_test client 127.0.0.1 1234 ticket 242 | modprobe quic_sample_test role=server alpn=ticket || return 1 243 | fi 244 | rmmod quic_sample_test 245 | dmesg | tail -n 5 246 | daemon_stop "ticket_test" 247 | } 248 | 249 | alpn_tests() 250 | { 251 | print_start "ALPN and Preferred Address Tests (IPv4 -> IPv6)" 252 | daemon_run ./alpn_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem ::1 253 | ./alpn_test client 127.0.0.1 1234 ::1 || return 1 254 | daemon_stop "alpn_test" 255 | 256 | print_start "ALPN and Preferred Address Tests (IPv6 -> IPv4)" 257 | daemon_run ./alpn_test server :: 1234 ./keys/server-key.pem ./keys/server-cert.pem 127.0.0.1 258 | ./alpn_test client ::1 1234 127.0.0.1 || return 1 259 | daemon_stop "alpn_test" 260 | 261 | print_start "ALPN and Preferred Address Tests (IPv4 -> IPv4)" 262 | ip addr add 127.0.0.2/8 dev lo 263 | daemon_run ./alpn_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem \ 264 | 127.0.0.2 265 | ./alpn_test client 127.0.0.1 1234 127.0.0.2 || return 1 266 | ip addr del 127.0.0.2/8 dev lo 267 | daemon_stop "alpn_test" 268 | 269 | print_start "ALPN and Preferred Address Tests (IPv6 -> IPv6)" 270 | ip -6 addr add ::2/128 dev lo 271 | daemon_run ./alpn_test server :: 1234 ./keys/server-key.pem ./keys/server-cert.pem ::2 272 | ./alpn_test client ::1 1234 ::2 || return 1 273 | ip -6 addr del ::2/128 dev lo 274 | daemon_stop "alpn_test" 275 | } 276 | 277 | ticket_tests() 278 | { 279 | print_start "Session Resumption Tests" 280 | daemon_run ./ticket_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem 281 | ./ticket_test client 127.0.0.1 1234 || return 1 282 | daemon_stop "ticket_test" 283 | } 284 | 285 | sample_tests() 286 | { 287 | print_start "Sample Tests" 288 | daemon_run ./sample_test server 0.0.0.0 1234 ./keys/server-key.pem ./keys/server-cert.pem 289 | ./sample_test client 127.0.0.1 1234 none none || return 1 290 | daemon_stop "sample_test" 291 | 292 | } 293 | 294 | TESTS="func perf netem http3 tlshd alpn ticket sample" 295 | trap cleanup EXIT 296 | 297 | [ "$1" = "" ] || TESTS=$1 298 | 299 | start_tests || exit $? 300 | 301 | for name in $TESTS; do 302 | eval ${name}_tests || exit $? 303 | done 304 | 305 | done_tests 306 | -------------------------------------------------------------------------------- /tests/sample_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static const char *parse_address( 13 | char const *address, char const *port, struct sockaddr_storage *sas) 14 | { 15 | struct addrinfo hints = {0}; 16 | struct addrinfo *res; 17 | int rc; 18 | 19 | hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; 20 | hints.ai_family = AF_UNSPEC; 21 | hints.ai_socktype = SOCK_DGRAM; 22 | 23 | rc = getaddrinfo(address, port, &hints, &res); 24 | if (rc != 0) 25 | return gai_strerror(rc); 26 | memcpy(sas, res->ai_addr, res->ai_addrlen); 27 | freeaddrinfo(res); 28 | return NULL; 29 | } 30 | 31 | static int do_client(int argc, char *argv[]) 32 | { 33 | struct sockaddr_storage ra = {}; 34 | char msg[50], *psk, *host; 35 | unsigned int flags; 36 | int ret, sockfd; 37 | const char *rc; 38 | int64_t sid; 39 | 40 | if (argc < 6) { 41 | printf("%s client " 42 | " [ALPN]\n", argv[0]); 43 | return 0; 44 | } 45 | 46 | rc = parse_address(argv[2], argv[3], &ra); 47 | if (rc != NULL) { 48 | printf("parse address failed: %s\n", rc); 49 | return -1; 50 | } 51 | sockfd = socket(ra.ss_family, SOCK_DGRAM, IPPROTO_QUIC); 52 | if (sockfd < 0) { 53 | printf("socket create failed\n"); 54 | return -1; 55 | } 56 | 57 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) { 58 | printf("socket connect failed\n"); 59 | return -1; 60 | } 61 | 62 | psk = strcmp(argv[4], "none") ? argv[4] : NULL; 63 | host = strcmp(argv[5], "none") ? argv[5] : NULL; 64 | if (quic_client_handshake(sockfd, psk, host, argv[6])) 65 | return -1; 66 | 67 | /* set MSG_STREAM_NEW flag to open a stream while sending first data 68 | * or call getsockopt(QUIC_SOCKOPT_STREAM_OPEN) to open a stream. 69 | * set MSG_STREAM_FIN to mark the last data on this stream. 70 | */ 71 | strcpy(msg, "hello quic server!"); 72 | sid = QUIC_STREAM_TYPE_UNI_MASK; 73 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN; 74 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags); 75 | if (ret == -1) { 76 | printf("send error %d %d\n", ret, errno); 77 | return -1; 78 | } 79 | printf("send '%s' on stream %d\n", msg, (int)sid); 80 | 81 | flags = 0; 82 | memset(msg, 0, sizeof(msg)); 83 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 84 | if (ret == -1) { 85 | printf("recv error %d %d\n", ret, errno); 86 | return 1; 87 | } 88 | printf("recv '%s' on stream %d\n", msg, (int)sid); 89 | 90 | close(sockfd); 91 | return 0; 92 | } 93 | 94 | static int do_server(int argc, char *argv[]) 95 | { 96 | unsigned int addrlen, flags; 97 | struct sockaddr_storage sa = {}; 98 | char msg[50], *alpn, *cert; 99 | int listenfd, sockfd, ret; 100 | const char *rc; 101 | int64_t sid; 102 | 103 | if (argc < 6) { 104 | printf("%s server " 105 | " [ALPN]\n", argv[0]); 106 | return 0; 107 | } 108 | 109 | rc = parse_address(argv[2], argv[3], &sa); 110 | if (rc != NULL) { 111 | printf("parse address failed: %s\n", rc); 112 | return -1; 113 | } 114 | listenfd = socket(sa.ss_family, SOCK_DGRAM, IPPROTO_QUIC); 115 | if (listenfd < 0) { 116 | printf("socket create failed\n"); 117 | return -1; 118 | } 119 | if (bind(listenfd, (struct sockaddr *)&sa, sizeof(sa))) { 120 | printf("socket bind failed\n"); 121 | return -1; 122 | } 123 | alpn = argv[6]; /* For kernel ALPN match */ 124 | if (alpn && setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_ALPN, alpn, strlen(alpn))) { 125 | printf("socket setsockopt alpn failed\n"); 126 | return -1; 127 | } 128 | if (listen(listenfd, 1)) { 129 | printf("socket listen failed\n"); 130 | return -1; 131 | } 132 | addrlen = sizeof(sa); 133 | sockfd = accept(listenfd, (struct sockaddr *)&sa, &addrlen); 134 | if (sockfd < 0) { 135 | printf("socket accept failed %d %d\n", errno, sockfd); 136 | return -1; 137 | } 138 | 139 | cert = strcmp(argv[5], "none") ? argv[5] : NULL; 140 | if (quic_server_handshake(sockfd, argv[4], cert, argv[6])) 141 | return -1; 142 | 143 | flags = 0; 144 | memset(msg, 0, sizeof(msg)); 145 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 146 | if (ret == -1) { 147 | printf("recv error %d %d\n", ret, errno); 148 | return 1; 149 | } 150 | printf("recv '%s' on stream %d\n", msg, (int)sid); 151 | 152 | strcpy(msg, "hello quic client!"); 153 | sid = QUIC_STREAM_TYPE_SERVER_MASK; 154 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN; 155 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags); 156 | if (ret == -1) { 157 | printf("send error %d %d\n", ret, errno); 158 | return -1; 159 | } 160 | printf("send '%s' on stream %d\n", msg, (int)sid); 161 | 162 | flags = 0; 163 | quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 164 | 165 | close(sockfd); 166 | close(listenfd); 167 | return 0; 168 | } 169 | 170 | int main(int argc, char *argv[]) 171 | { 172 | if (argc < 2 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) { 173 | printf("%s server|client ...\n", argv[0]); 174 | return 0; 175 | } 176 | 177 | quic_set_log_level(LOG_NOTICE); 178 | 179 | if (!strcmp(argv[1], "client")) 180 | return do_client(argc, argv); 181 | 182 | return do_server(argc, argv); 183 | } 184 | -------------------------------------------------------------------------------- /tests/syzkaller/net_quic_syscall.list: -------------------------------------------------------------------------------- 1 | "socket", "socketpair", "setsockopt", "getsockopt", "getsockname", "getpeername", 2 | "bind", "listen", "connect", "accept", "accept4", "close", "shutdown", 3 | "sendmsg", "sendmmsg", "sendto", "recvmsg", "recvmmsg", "recvfrom", 4 | "socket$inet_quic", "socket$inet6_quic", 5 | "epoll_create", "epoll_create1", "epoll_ctl", "epoll_wait", "epoll_pwait", "epoll_pwait2", 6 | "mmap", "poll", "ppoll", "pread64", "preadv", "select", "pselect6", "sendfile" 7 | -------------------------------------------------------------------------------- /tests/syzkaller/socket_inet_quic.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017 syzkaller project authors. All rights reserved. 2 | # Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 | 4 | # AF_INET and AF_INET6: QUIC support 5 | 6 | include 7 | include 8 | include 9 | 10 | resource sock_quic[sock_in] 11 | 12 | socket$inet_quic(domain const[AF_INET], type flags[quic_socket_type], proto const[IPPROTO_QUIC]) sock_quic 13 | 14 | quic_socket_type = SOCK_STREAM, SOCK_DGRAM 15 | 16 | resource sock_quic6[sock_in6] 17 | 18 | socket$inet6_quic(domain const[AF_INET6], type flags[quic_socket_type], proto const[IPPROTO_QUIC]) sock_quic6 19 | 20 | # TODO: separate for ip & ipv6 21 | sendmsg$inet_quic(fd sock_quic, msg ptr[in, msghdr_quic], f flags[send_flags]) 22 | sendmmsg$inet_quic(fd sock_quic, mmsg ptr[in, array[msghdr_quic]], vlen len[mmsg], f flags[send_flags]) 23 | 24 | msghdr_quic { 25 | addr ptr[in, sockaddr_quic] 26 | addrlen len[addr, int32] 27 | vec ptr[in, array[iovec_in]] 28 | vlen len[vec, intptr] 29 | ctrl ptr[in, array[cmsghdr_quic], opt] 30 | ctrllen bytesize[ctrl, intptr] 31 | f flags[send_flags, int32] 32 | } 33 | 34 | cmsghdr_quic [ 35 | handshake cmsghdr_quic_handshake_info 36 | stream cmsghdr_quic_stream_info 37 | ] [varlen] 38 | 39 | quic_handshake_info { 40 | crypto_level int8 41 | } 42 | 43 | cmsghdr_quic_handshake_info { 44 | len len[parent, intptr] 45 | level const[SOL_QUIC, int32] 46 | type const[QUIC_HANDSHAKE_INFO, int32] 47 | msg quic_handshake_info 48 | } [align[PTR_SIZE]] 49 | 50 | quic_stream_info { 51 | stream_id int64 52 | stream_flags int32 53 | } 54 | 55 | cmsghdr_quic_stream_info { 56 | len len[parent, intptr] 57 | level const[SOL_QUIC, int32] 58 | type const[QUIC_STREAM_INFO, int32] 59 | msg quic_stream_info 60 | } [align[PTR_SIZE]] 61 | 62 | # Generic QUIC socket options 63 | quic_option_types_buf = QUIC_SOCKOPT_TOKEN, QUIC_SOCKOPT_ALPN, QUIC_SOCKOPT_SESSION_TICKET, QUIC_SOCKOPT_TRANSPORT_PARAM_EXT 64 | 65 | getsockopt$inet_quic_buf(fd sock_quic, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[out], optlen ptr[inout, len[optval, int32]]) 66 | setsockopt$inet_quic_buf(fd sock_quic, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[in], optlen len[optval]) 67 | getsockopt$inet6_quic_buf(fd sock_quic6, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[out], optlen ptr[inout, len[optval, int32]]) 68 | setsockopt$inet6_quic_buf(fd sock_quic6, level const[SOL_QUIC], optname flags[quic_option_types_buf], optval buffer[in], optlen len[optval]) 69 | 70 | # Specific QUIC socket options 71 | 72 | setsockopt$inet_quic_QUIC_SOCKOPT_EVENT(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[in, quic_event_option], len len[val]) 73 | setsockopt$inet_quic6_QUIC_SOCKOPT_EVENT(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[in, quic_event_option], len len[val]) 74 | getsockopt$inet_quic_QUIC_SOCKOPT_EVENT(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[inout, quic_event_option], len ptr[inout, len[val, int32]]) 75 | getsockopt$inet_quic6_QUIC_SOCKOPT_EVENT(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_EVENT], val ptr[inout, quic_event_option], len ptr[inout, len[val, int32]]) 76 | 77 | getsockopt$inet_quic_QUIC_SOCKOPT_STREAM_OPEN(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_OPEN], val ptr[inout, quic_stream_info], len ptr[inout, len[val, int32]]) 78 | getsockopt$inet_quic6_QUIC_SOCKOPT_STREAM_OPEN(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_OPEN], val ptr[inout, quic_stream_info], len ptr[inout, len[val, int32]]) 79 | 80 | setsockopt$inet_quic_QUIC_SOCKOPT_STREAM_RESET(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_RESET], val ptr[in, quic_errinfo], len len[val]) 81 | setsockopt$inet_quic6_QUIC_SOCKOPT_STREAM_RESET(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_RESET], val ptr[in, quic_errinfo], len len[val]) 82 | 83 | setsockopt$inet_quic_QUIC_SOCKOPT_STREAM_STOP_SENDING(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_STOP_SENDING], val ptr[in, quic_errinfo], len len[val]) 84 | setsockopt$inet_quic6_QUIC_SOCKOPT_STREAM_STOP_SENDING(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_STREAM_STOP_SENDING], val ptr[in, quic_errinfo], len len[val]) 85 | 86 | setsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[in, quic_connection_id_info], len len[val]) 87 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[in, quic_connection_id_info], len len[val]) 88 | getsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[inout, quic_connection_id_info], len ptr[inout, len[val, int32]]) 89 | getsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_ID(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_ID], val ptr[inout, quic_connection_id_info], len ptr[inout, len[val, int32]]) 90 | 91 | setsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[in, quic_connection_close], len len[val]) 92 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[in, quic_connection_close], len len[val]) 93 | getsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[inout, quic_connection_close], len ptr[inout, len[val, int32]]) 94 | getsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_CLOSE(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_CLOSE], val ptr[inout, quic_connection_close], len ptr[inout, len[val, int32]]) 95 | 96 | setsockopt$inet_quic_QUIC_SOCKOPT_CONNECTION_MIGRATION(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_MIGRATION], val ptr[in, sockaddr_quic], len len[val]) 97 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONNECTION_MIGRATION(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONNECTION_MIGRATION], val ptr[in, sockaddr_quic], len len[val]) 98 | 99 | setsockopt$inet_quic_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[in, quic_transport_param], len len[val]) 100 | setsockopt$inet_quic6_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[in, quic_transport_param], len len[val]) 101 | getsockopt$inet_quic_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[inout, quic_transport_param], len ptr[inout, len[val, int32]]) 102 | getsockopt$inet_quic6_QUIC_SOCKOPT_TRANSPORT_PARAM(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_TRANSPORT_PARAM], val ptr[inout, quic_transport_param], len ptr[inout, len[val, int32]]) 103 | 104 | setsockopt$inet_quic_QUIC_SOCKOPT_CONFIG(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[in, quic_config], len len[val]) 105 | setsockopt$inet_quic6_QUIC_SOCKOPT_CONFIG(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[in, quic_config], len len[val]) 106 | getsockopt$inet_quic_QUIC_SOCKOPT_CONFIG(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[inout, quic_config], len ptr[inout, len[val, int32]]) 107 | getsockopt$inet_quic6_QUIC_SOCKOPT_CONFIG(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CONFIG], val ptr[inout, quic_config], len ptr[inout, len[val, int32]]) 108 | 109 | setsockopt$inet_quic_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[in, quic_crypto_secret], len len[val]) 110 | setsockopt$inet_quic6_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[in, quic_crypto_secret], len len[val]) 111 | getsockopt$inet_quic_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[inout, quic_crypto_secret], len ptr[inout, len[val, int32]]) 112 | getsockopt$inet_quic6_QUIC_SOCKOPT_CRYPTO_SECRET(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_CRYPTO_SECRET], val ptr[inout, quic_crypto_secret], len ptr[inout, len[val, int32]]) 113 | 114 | setsockopt$inet_quic_QUIC_SOCKOPT_KEY_UPDATE(fd sock_quic, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_KEY_UPDATE], optval buffer[in], len len[optval]) 115 | setsockopt$inet_quic6_QUIC_SOCKOPT_KEY_UPDATE(fd sock_quic6, level const[SOL_QUIC], opt const[QUIC_SOCKOPT_KEY_UPDATE], optval buffer[in], len len[optval]) 116 | 117 | sockaddr_quic [ 118 | in sockaddr_in 119 | in6 sockaddr_in6 120 | ] [varlen] 121 | 122 | quic_event_option { 123 | type int8 124 | on int8 125 | } 126 | 127 | quic_errinfo { 128 | stream_id int64 129 | errcode int32 130 | } 131 | 132 | quic_connection_id_info { 133 | dest int8 134 | active int32 135 | prior_to int32 136 | } 137 | 138 | quic_connection_close { 139 | errcode int32 140 | frame int8 141 | phrase array[int8] 142 | } 143 | 144 | quic_transport_param { 145 | remote int8 146 | disable_active_migration int8 147 | grease_quic_bit int8 148 | stateless_reset int8 149 | disable_1rtt_encryption int8 150 | disable_compatible_version int8 151 | active_connection_id_limit int8 152 | ack_delay_exponent int8 153 | max_datagram_frame_size int16 154 | max_udp_payload_size int16 155 | max_idle_timeout int32 156 | max_ack_delay int32 157 | max_streams_bidi int16 158 | max_streams_uni int16 159 | max_data int64 160 | max_stream_data_bidi_local int64 161 | max_stream_data_bidi_remote int64 162 | max_stream_data_uni int64 163 | reserved int64 164 | } 165 | 166 | quic_config { 167 | version int32 168 | plpmtud_probe_interval int32 169 | initial_smoothed_rtt int32 170 | payload_cipher_type int32 171 | congestion_control_algo int8 172 | validate_peer_address int8 173 | stream_data_nodelay int8 174 | receive_session_ticket int8 175 | certificate_request int8 176 | reserved array[int8, 3] 177 | } 178 | 179 | quic_crypto_secret { 180 | send int8 181 | level int8 182 | type int32 183 | secret array[int8, 48] 184 | } 185 | -------------------------------------------------------------------------------- /tests/syzkaller/socket_inet_quic.txt.const: -------------------------------------------------------------------------------- 1 | # Code generated by syz-sysgen. DO NOT EDIT. 2 | arches = 386, amd64, arm, arm64, mips64le, ppc64le, riscv64, s390x 3 | AF_INET = 2 4 | AF_INET6 = 10 5 | IPPROTO_QUIC = 261 6 | SOL_QUIC = 288 7 | QUIC_STREAM_INFO = 0 8 | QUIC_HANDSHAKE_INFO = 1 9 | QUIC_SOCKOPT_EVENT = 0 10 | QUIC_SOCKOPT_STREAM_OPEN = 1 11 | QUIC_SOCKOPT_STREAM_RESET = 2 12 | QUIC_SOCKOPT_STREAM_STOP_SENDING = 3 13 | QUIC_SOCKOPT_CONNECTION_ID = 4 14 | QUIC_SOCKOPT_CONNECTION_CLOSE = 5 15 | QUIC_SOCKOPT_CONNECTION_MIGRATION = 6 16 | QUIC_SOCKOPT_KEY_UPDATE = 7 17 | QUIC_SOCKOPT_TRANSPORT_PARAM = 8 18 | QUIC_SOCKOPT_CONFIG = 9 19 | QUIC_SOCKOPT_TOKEN = 10 20 | QUIC_SOCKOPT_ALPN = 11 21 | QUIC_SOCKOPT_SESSION_TICKET = 12 22 | QUIC_SOCKOPT_CRYPTO_SECRET = 13 23 | QUIC_SOCKOPT_TRANSPORT_PARAM_EXT = 14 24 | SOCK_STREAM = 1, mips64le:2 25 | SOCK_DGRAM = 2, mips64le:1 26 | __NR_getsockopt = 209, 386:s390x:365, amd64:55, arm:295, mips64le:5054, ppc64le:340 27 | __NR_sendmmsg = 269, 386:345, amd64:307, arm:374, mips64le:5302, ppc64le:349, s390x:358 28 | __NR_sendmsg = 211, 386:s390x:370, amd64:46, arm:296, mips64le:5045, ppc64le:341 29 | __NR_setsockopt = 208, 386:s390x:366, amd64:54, arm:294, mips64le:5053, ppc64le:339 30 | __NR_socket = 198, 386:s390x:359, amd64:41, arm:281, mips64le:5040, ppc64le:326 31 | -------------------------------------------------------------------------------- /tests/ticket_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static uint8_t ticket[4096]; 13 | 14 | static const char *parse_address( 15 | char const *address, char const *port, struct sockaddr_storage *sas) 16 | { 17 | struct addrinfo hints = {0}; 18 | struct addrinfo *res; 19 | int rc; 20 | 21 | hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; 22 | hints.ai_family = AF_UNSPEC; 23 | hints.ai_socktype = SOCK_DGRAM; 24 | 25 | rc = getaddrinfo(address, port, &hints, &res); 26 | if (rc != 0) 27 | return gai_strerror(rc); 28 | memcpy(sas, res->ai_addr, res->ai_addrlen); 29 | freeaddrinfo(res); 30 | return NULL; 31 | } 32 | 33 | static int client_handshake(int sockfd, const char *alpns, const char *host, 34 | const uint8_t *ticket_in, size_t ticket_in_len, 35 | uint8_t *ticket_out, size_t *ticket_out_len) 36 | { 37 | gnutls_certificate_credentials_t cred; 38 | gnutls_session_t session; 39 | size_t alpn_len; 40 | char alpn[64]; 41 | int ret; 42 | 43 | ret = gnutls_certificate_allocate_credentials(&cred); 44 | if (ret) 45 | goto err; 46 | ret = gnutls_certificate_set_x509_system_trust(cred); 47 | if (ret < 0) 48 | goto err_cred; 49 | 50 | ret = gnutls_init(&session, GNUTLS_CLIENT | 51 | GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA); 52 | if (ret) 53 | goto err_cred; 54 | ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); 55 | if (ret) 56 | goto err_session; 57 | 58 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL); 59 | if (ret) 60 | goto err_session; 61 | 62 | if (alpns) { 63 | ret = quic_session_set_alpn(session, alpns, strlen(alpns)); 64 | if (ret) 65 | goto err_session; 66 | } 67 | 68 | if (host) { 69 | ret = gnutls_server_name_set(session, GNUTLS_NAME_DNS, host, strlen(host)); 70 | if (ret) 71 | goto err_session; 72 | } 73 | 74 | gnutls_transport_set_int(session, sockfd); 75 | 76 | if (ticket_in) { 77 | ret = quic_session_set_data(session, ticket_in, ticket_in_len); 78 | if (ret) 79 | goto err_session; 80 | } 81 | 82 | ret = quic_handshake(session); 83 | if (ret) 84 | goto err_session; 85 | 86 | if (alpns) { 87 | alpn_len = sizeof(alpn); 88 | ret = quic_session_get_alpn(session, alpn, &alpn_len); 89 | if (ret) 90 | goto err_session; 91 | } 92 | 93 | if (ticket_out) { 94 | sleep(1); 95 | ret = quic_session_get_data(session, ticket_out, ticket_out_len); 96 | if (ret) 97 | goto err_session; 98 | } 99 | 100 | err_session: 101 | gnutls_deinit(session); 102 | err_cred: 103 | gnutls_certificate_free_credentials(cred); 104 | err: 105 | return ret; 106 | } 107 | 108 | static int do_client(int argc, char *argv[]) 109 | { 110 | struct quic_transport_param param = {}; 111 | unsigned int param_len, flags; 112 | struct sockaddr_storage ra = {}; 113 | char msg[50], *alpn; 114 | size_t ticket_len; 115 | int ret, sockfd; 116 | const char *rc; 117 | int64_t sid; 118 | 119 | if (argc < 3) { 120 | printf("%s client [ALPN]\n", argv[0]); 121 | return 0; 122 | } 123 | 124 | rc = parse_address(argv[2], argv[3], &ra); 125 | if (rc != NULL) { 126 | printf("parse address failed: %s\n", rc); 127 | return -1; 128 | } 129 | 130 | sockfd = socket(ra.ss_family, SOCK_DGRAM, IPPROTO_QUIC); 131 | if (sockfd < 0) { 132 | printf("socket create failed\n"); 133 | return -1; 134 | } 135 | 136 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) { 137 | printf("socket connect failed\n"); 138 | return -1; 139 | } 140 | 141 | /* get session ticket, remote tranaport param for session resumption */ 142 | alpn = argv[4]; 143 | ticket_len = sizeof(ticket); 144 | if (client_handshake(sockfd, alpn, NULL, NULL, 0, ticket, &ticket_len)) 145 | return -1; 146 | 147 | param_len = sizeof(param); 148 | param.remote = 1; 149 | ret = getsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, ¶m_len); 150 | if (ret == -1) { 151 | printf("socket getsockopt remote transport param\n"); 152 | return -1; 153 | } 154 | 155 | printf("get the session ticket %u and transport param %u, save it\n", 156 | (unsigned int)ticket_len, param_len); 157 | 158 | strcpy(msg, "hello quic server!"); 159 | sid = QUIC_STREAM_TYPE_UNI_MASK; 160 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN; 161 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags); 162 | if (ret == -1) { 163 | printf("send error %d %d\n", ret, errno); 164 | return -1; 165 | } 166 | printf("send '%s' on stream %d\n", msg, (int)sid); 167 | 168 | flags = 0; 169 | memset(msg, 0, sizeof(msg)); 170 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 171 | if (ret == -1) { 172 | printf("recv error %d %d\n", ret, errno); 173 | return 1; 174 | } 175 | printf("recv '%s' on stream %d\n", msg, (int)sid); 176 | 177 | close(sockfd); 178 | 179 | printf("start new connection with the session ticket used...\n"); 180 | sleep(2); 181 | 182 | rc = parse_address(argv[2], argv[3], &ra); 183 | if (rc != NULL) { 184 | printf("parse address failed: %s\n", rc); 185 | return -1; 186 | } 187 | 188 | sockfd = socket(ra.ss_family, SOCK_DGRAM, IPPROTO_QUIC); 189 | if (sockfd < 0) { 190 | printf("socket create failed\n"); 191 | return -1; 192 | } 193 | 194 | if (connect(sockfd, (struct sockaddr *)&ra, sizeof(ra))) { 195 | printf("socket connect failed\n"); 196 | return -1; 197 | } 198 | 199 | ret = setsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_TRANSPORT_PARAM, ¶m, param_len); 200 | if (ret == -1) { 201 | printf("socket setsockopt remote transport param\n"); 202 | return -1; 203 | } 204 | 205 | /* send early data before handshake */ 206 | strcpy(msg, "hello quic server, I'm back!"); 207 | sid = QUIC_STREAM_TYPE_UNI_MASK; 208 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN; 209 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags); 210 | if (ret == -1) { 211 | printf("send error %d %d\n", ret, errno); 212 | return -1; 213 | } 214 | printf("send '%s' on stream %d\n", msg, (int)sid); 215 | 216 | if (client_handshake(sockfd, alpn, NULL, ticket, ticket_len, NULL, NULL)) 217 | return -1; 218 | 219 | flags = 0; 220 | memset(msg, 0, sizeof(msg)); 221 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 222 | if (ret == -1) { 223 | printf("recv error %d %d\n", ret, errno); 224 | return 1; 225 | } 226 | printf("recv '%s' on stream %d\n", msg, (int)sid); 227 | 228 | close(sockfd); 229 | return 0; 230 | } 231 | 232 | static int server_handshake(int sockfd, const char *pkey, const char *cert, const char *alpns, 233 | uint8_t *key, unsigned int keylen) 234 | { 235 | gnutls_certificate_credentials_t cred; 236 | gnutls_datum_t skey = {key, keylen}; 237 | gnutls_session_t session; 238 | size_t alpn_len; 239 | char alpn[64]; 240 | int ret; 241 | 242 | ret = gnutls_certificate_allocate_credentials(&cred); 243 | if (ret) 244 | goto err; 245 | ret = gnutls_certificate_set_x509_system_trust(cred); 246 | if (ret < 0) 247 | goto err_cred; 248 | ret = gnutls_certificate_set_x509_key_file(cred, cert, pkey, GNUTLS_X509_FMT_PEM); 249 | if (ret) 250 | goto err_cred; 251 | ret = gnutls_init(&session, GNUTLS_SERVER | GNUTLS_NO_AUTO_SEND_TICKET | 252 | GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA); 253 | if (ret) 254 | goto err_cred; 255 | ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred); 256 | if (ret) 257 | goto err_session; 258 | 259 | ret = gnutls_session_ticket_enable_server(session, &skey); 260 | if (ret) 261 | goto err_session; 262 | 263 | ret = gnutls_record_set_max_early_data_size(session, 0xffffffffu); 264 | if (ret) 265 | goto err_session; 266 | 267 | ret = gnutls_priority_set_direct(session, QUIC_PRIORITY, NULL); 268 | if (ret) 269 | goto err_session; 270 | 271 | if (alpns) { 272 | ret = quic_session_set_alpn(session, alpns, strlen(alpns)); 273 | if (ret) 274 | goto err_session; 275 | } 276 | 277 | gnutls_transport_set_int(session, sockfd); 278 | 279 | ret = quic_handshake(session); 280 | if (ret) 281 | goto err_session; 282 | 283 | if (alpns) { 284 | alpn_len = sizeof(alpn); 285 | ret = quic_session_get_alpn(session, alpn, &alpn_len); 286 | } 287 | 288 | err_session: 289 | gnutls_deinit(session); 290 | err_cred: 291 | gnutls_certificate_free_credentials(cred); 292 | err: 293 | return ret; 294 | } 295 | 296 | static int do_server(int argc, char *argv[]) 297 | { 298 | unsigned int addrlen, keylen, flags; 299 | struct sockaddr_storage sa = {}; 300 | int listenfd, sockfd, ret; 301 | char msg[50], *alpn; 302 | uint8_t key[64]; 303 | const char *rc; 304 | int64_t sid; 305 | 306 | if (argc < 5) { 307 | printf("%s server " 308 | "\n", argv[0]); 309 | return 0; 310 | } 311 | 312 | rc = parse_address(argv[2], argv[3], &sa); 313 | if (rc != NULL) { 314 | printf("parse address failed: %s\n", rc); 315 | return -1; 316 | } 317 | 318 | listenfd = socket(sa.ss_family, SOCK_DGRAM, IPPROTO_QUIC); 319 | if (listenfd < 0) { 320 | printf("socket create failed\n"); 321 | return -1; 322 | } 323 | if (bind(listenfd, (struct sockaddr *)&sa, sizeof(sa))) { 324 | printf("socket bind failed\n"); 325 | return -1; 326 | } 327 | alpn = argv[6]; 328 | if (alpn && setsockopt(listenfd, SOL_QUIC, QUIC_SOCKOPT_ALPN, alpn, strlen(alpn))) { 329 | printf("socket setsockopt alpn failed\n"); 330 | return -1; 331 | } 332 | 333 | if (listen(listenfd, 1)) { 334 | printf("socket listen failed\n"); 335 | return -1; 336 | } 337 | 338 | addrlen = sizeof(sa); 339 | sockfd = accept(listenfd, (struct sockaddr *)&sa, &addrlen); 340 | if (sockfd < 0) { 341 | printf("socket accept failed %d %d\n", errno, sockfd); 342 | return -1; 343 | } 344 | 345 | keylen = sizeof(key); 346 | if (getsockopt(sockfd, SOL_QUIC, QUIC_SOCKOPT_SESSION_TICKET, key, &keylen)) { 347 | printf("socket getsockopt session ticket error %d", errno); 348 | return -1; 349 | } 350 | 351 | if (server_handshake(sockfd, argv[4], argv[5], alpn, key, keylen)) 352 | return -1; 353 | 354 | flags = 0; 355 | memset(msg, 0, sizeof(msg)); 356 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 357 | if (ret == -1) { 358 | printf("recv error %d %d\n", ret, errno); 359 | return 1; 360 | } 361 | printf("recv '%s' on stream %d\n", msg, (int)sid); 362 | 363 | strcpy(msg, "hello quic client!"); 364 | sid = QUIC_STREAM_TYPE_SERVER_MASK; 365 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN; 366 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags); 367 | if (ret == -1) { 368 | printf("send error %d %d\n", ret, errno); 369 | return -1; 370 | } 371 | printf("send '%s' on stream %d\n", msg, (int)sid); 372 | 373 | close(sockfd); 374 | 375 | printf("wait for the client next connection...\n"); 376 | 377 | addrlen = sizeof(sa); 378 | sockfd = accept(listenfd, (struct sockaddr *)&sa, &addrlen); 379 | if (sockfd < 0) { 380 | printf("socket accept failed %d %d\n", errno, sockfd); 381 | return -1; 382 | } 383 | 384 | if (server_handshake(sockfd, argv[4], argv[5], alpn, key, keylen)) 385 | return -1; 386 | 387 | flags = 0; 388 | memset(msg, 0, sizeof(msg)); 389 | ret = quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 390 | if (ret == -1) { 391 | printf("recv error %d %d\n", ret, errno); 392 | return 1; 393 | } 394 | printf("recv '%s' on stream %d\n", msg, (int)sid); 395 | 396 | strcpy(msg, "hello quic client! welcome back!"); 397 | sid = QUIC_STREAM_TYPE_SERVER_MASK; 398 | flags = MSG_STREAM_NEW | MSG_STREAM_FIN; 399 | ret = quic_sendmsg(sockfd, msg, strlen(msg), sid, flags); 400 | if (ret == -1) { 401 | printf("send error %d %d\n", ret, errno); 402 | return -1; 403 | } 404 | printf("send '%s' on stream %d\n", msg, (int)sid); 405 | 406 | flags = 0; 407 | quic_recvmsg(sockfd, msg, sizeof(msg) - 1, &sid, &flags); 408 | 409 | close(sockfd); 410 | close(listenfd); 411 | return 0; 412 | } 413 | 414 | int main(int argc, char *argv[]) 415 | { 416 | if (argc < 2 || (strcmp(argv[1], "server") && strcmp(argv[1], "client"))) { 417 | printf("%s server|client ...\n", argv[0]); 418 | return 0; 419 | } 420 | 421 | quic_set_log_level(LOG_NOTICE); 422 | 423 | if (!strcmp(argv[1], "client")) 424 | return do_client(argc, argv); 425 | 426 | return do_server(argc, argv); 427 | } 428 | --------------------------------------------------------------------------------