├── .github ├── dependabot.yml └── workflows │ └── build-docker-images.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── docker-compose.yml ├── endpoint ├── Dockerfile ├── run_endpoint.sh └── setup.sh ├── interop └── README.md └── sim ├── CMakeLists.patch ├── Dockerfile ├── run.sh ├── scenarios ├── blackhole │ ├── README.md │ ├── blackhole-error-model.cc │ ├── blackhole-error-model.h │ └── blackhole.cc ├── corrupt-rate │ ├── README.md │ ├── corrupt-rate-error-model.cc │ ├── corrupt-rate-error-model.h │ └── corrupt-rate.cc ├── drop-rate │ ├── README.md │ ├── drop-rate-error-model.cc │ ├── drop-rate-error-model.h │ └── drop-rate.cc ├── droplist │ ├── README.md │ ├── droplist-error-model.cc │ ├── droplist-error-model.h │ └── droplist.cc ├── helper │ ├── quic-network-simulator-helper.cc │ ├── quic-network-simulator-helper.h │ ├── quic-packet.cc │ ├── quic-packet.h │ ├── quic-point-to-point-helper.cc │ └── quic-point-to-point-helper.h ├── rebind │ ├── README.md │ ├── rebind-error-model.cc │ ├── rebind-error-model.h │ └── rebind.cc ├── simple-p2p │ ├── README.md │ └── simple-p2p.cc ├── tcp-cross-traffic │ ├── README.md │ └── tcp-cross-traffic.cc └── udp-cross-traffic │ └── udp-cross-traffic.cc └── wait-for-it-quic ├── go.mod └── wait-for-it.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "docker" 7 | directory: "/endpoint" 8 | schedule: 9 | interval: "weekly" 10 | - package-ecosystem: "docker" 11 | directory: "/sim" 12 | schedule: 13 | interval: "weekly" 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | -------------------------------------------------------------------------------- /.github/workflows/build-docker-images.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker images 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | 8 | jobs: 9 | endpoint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Set up QEMU 15 | id: qemu 16 | uses: docker/setup-qemu-action@v3 17 | with: 18 | image: tonistiigi/binfmt:latest 19 | platforms: all 20 | 21 | - name: Set up Docker Buildx 22 | uses: docker/setup-buildx-action@v3 23 | with: 24 | platforms: linux/amd64,linux/arm64 25 | 26 | - name: Login to DockerHub 27 | uses: docker/login-action@v3 28 | if: ${{ github.event_name == 'push' }} 29 | with: 30 | username: ${{ secrets.DOCKER_USERNAME }} 31 | password: ${{ secrets.DOCKER_PASSWORD }} 32 | 33 | - name: Build endpoint 34 | uses: docker/build-push-action@v6 35 | with: 36 | context: "{{defaultContext}}:endpoint/" 37 | platforms: linux/amd64,linux/arm64 38 | push: ${{ github.event_name == 'push' }} 39 | tags: martenseemann/quic-network-simulator-endpoint:latest 40 | simulator: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - name: Set up QEMU 46 | id: qemu 47 | uses: docker/setup-qemu-action@v3 48 | with: 49 | image: tonistiigi/binfmt:latest 50 | platforms: all 51 | 52 | - name: Set up Docker Buildx 53 | uses: docker/setup-buildx-action@v3 54 | 55 | - name: Login to DockerHub 56 | uses: docker/login-action@v3 57 | if: ${{ github.event_name == 'push' }} 58 | with: 59 | username: ${{ secrets.DOCKER_USERNAME }} 60 | password: ${{ secrets.DOCKER_PASSWORD }} 61 | 62 | - name: Build simulator 63 | uses: docker/build-push-action@v6 64 | with: 65 | context: "{{defaultContext}}:sim/" 66 | platforms: linux/amd64,linux/arm64 67 | push: ${{ github.event_name == 'push' }} 68 | tags: martenseemann/quic-network-simulator:latest 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs/ 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019 Jana Iyengar, Marten Seemann 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Network Simulator for QUIC benchmarking 2 | 3 | This project builds a test framework that can be used for benchmarking and 4 | measuring the performance of QUIC implementations under various network 5 | conditions. It uses the [ns-3](https://www.nsnam.org/) network simulator for 6 | simulating network conditions and cross-traffic, and for bridging the real world 7 | with the simulated world. It uses docker for isolating and coercing traffic 8 | between the client and server to flow through the simulated network. 9 | 10 | ## Framework 11 | 12 | The framework uses docker-compose to compose three docker images: the network 13 | simulator (as found in the [sim](sim) directory), and a client and a server (as 14 | found in the individual QUIC implementation directories, or for a simple shell, 15 | the [endpoint](endpoint) directory). 16 | 17 | The framework uses two networks on the host machine: `leftnet` (IPv4 18 | 193.167.0.0/24, IPv6 fd00:cafe:cafe:0::/64) and `rightnet` (IPv4 19 | 193.167.100.0/24, IPv6 fd00:cafe:cafe:100::/64). `leftnet` is connected to the 20 | client docker image, and `rightnet` is connected to the server. The ns-3 21 | simulation sits in the middle and forwards packets between `leftnet` and 22 | `rightnet`. 23 | 24 | ``` 25 | +-----------------------+ 26 | | client eth0 | 27 | | | 28 | | 193.167.0.100 | 29 | | fd00:cafe:cafe:0::100 | 30 | +----------+------------+ 31 | | 32 | | 33 | +----------+------------+ 34 | | docker-bridge | 35 | | | 36 | | 193.167.0.1 | 37 | | fd00:cafe:cafe:0::1 | 38 | +-----------------------------------+ 39 | | | eth0 | | 40 | | | | | 41 | | | 193.167.0.2 | | 42 | | | fd00:cafe:cafe:0::2 | | 43 | | +----------+------------+ | 44 | | | | 45 | | | | 46 | | +----------+------------+ | 47 | | | ns3 | | 48 | | +----------+------------+ | 49 | | | | 50 | | | | 51 | | +----------+------------+ | 52 | | | eth1 | | 53 | | | | | 54 | | | 193.167.100.2 | | 55 | | | fd00:cafe:cafe:100::2 | sim| 56 | +-----------------------------------+ 57 | | docker-bridge | 58 | | | 59 | | 193.167.100.1 | 60 | | fd00:cafe:cafe:100::1 | 61 | +----------+------------+ 62 | | 63 | | 64 | +----------+------------+ 65 | | server eth0 | 66 | | | 67 | | 193.167.100.100 | 68 | |fd00:cafe:cafe:100::100| 69 | +-----------------------+ 70 | ``` 71 | 72 | 73 | ## Building your own QUIC docker image 74 | 75 | The [endpoint](endpoint) directory contains the base Docker image for an 76 | endpoint Docker container. The pre-built image of this container is available 77 | on 78 | [dockerhub](https://hub.docker.com/r/martenseemann/quic-network-simulator-endpoint). 79 | 80 | Follow these steps to set up your own QUIC implementation: 81 | 82 | 1. Create a new directory for your implementation (say, my_quic_impl). You will 83 | create two files in this directory: `Dockerfile` and `run_endpoint.sh`, as 84 | described below. 85 | 86 | 1. Copy the Dockerfile below and add the commands to build your QUIC 87 | implementation. 88 | 89 | ```dockerfile 90 | FROM martenseemann/quic-network-simulator-endpoint:latest 91 | 92 | # download and build your QUIC implementation 93 | # [ DO WORK HERE ] 94 | 95 | # copy run script and run it 96 | COPY run_endpoint.sh . 97 | RUN chmod +x run_endpoint.sh 98 | ENTRYPOINT [ "./run_endpoint.sh" ] 99 | ``` 100 | 101 | 1. Now, copy the script below into `run_endpoint.sh`, and add commands as 102 | instructed. Logs should be recorded in `/logs` for them to be available 103 | after simulation completion (more on this later). 104 | 105 | ```bash 106 | #!/bin/bash 107 | 108 | # Set up the routing needed for the simulation 109 | /setup.sh 110 | 111 | # The following variables are available for use: 112 | # - ROLE contains the role of this execution context, client or server 113 | # - SERVER_PARAMS contains user-supplied command line parameters 114 | # - CLIENT_PARAMS contains user-supplied command line parameters 115 | 116 | if [ "$ROLE" == "client" ]; then 117 | # Wait for the simulator to start up. 118 | /wait-for-it.sh sim:57832 -s -t 30 119 | [ INSERT COMMAND TO RUN YOUR QUIC CLIENT ] 120 | elif [ "$ROLE" == "server" ]; then 121 | [ INSERT COMMAND TO RUN YOUR QUIC SERVER ] 122 | fi 123 | ``` 124 | 125 | For an example, have a look at the [quic-go 126 | setup](https://github.com/marten-seemann/quic-go-docker) or the [quicly 127 | setup](https://github.com/h2o/h2o-qns). 128 | 129 | 130 | ## Running a Simulation 131 | 132 | 1. From the quic-network-simulator directory, first build the necessary images: 133 | 134 | ``` 135 | CLIENT=[client directory name] \ 136 | SERVER=[server directory name] \ 137 | docker-compose build 138 | ``` 139 | 140 | Note that you will need to run this build command any time you change the 141 | client or server implementation, `Dockerfile`, or `run_endpoint.sh` file. 142 | 143 | For instance: 144 | 145 | ``` 146 | CLIENT="my_quic_impl" \ 147 | SERVER="another_quic_impl" \ 148 | docker-compose build 149 | ``` 150 | 151 | 1. You will want to run the setup with a scenario. The scenarios that are 152 | currently provided are listed below: 153 | 154 | * [Simple point-to-point link, with configurable link properties](sim/scenarios/simple-p2p) 155 | 156 | * [Single TCP connection running over a configurable point-to-point link](sim/scenarios/tcp-cross-traffic) 157 | 158 | You can now run the experiment as follows: 159 | ``` 160 | CLIENT=[client directory name] \ 161 | CLIENT_PARAMS=[params to client] \ 162 | SERVER=[server directory name] \ 163 | SERVER_PARAMS=[params to server] \ 164 | SCENARIO=[scenario] \ 165 | docker-compose up 166 | ``` 167 | 168 | SERVER_PARAMS and CLIENT_PARAMS may be omitted if the corresponding QUIC 169 | implementations do not require them. 170 | 171 | For instance, the following command runs a simple point-to-point scenario and 172 | specifies a command line parameter for only the client implementation: 173 | 174 | ``` 175 | CLIENT="my_quic_impl" \ 176 | CLIENT_PARAMS="-p /10000.txt" \ 177 | SERVER="another_quic_impl" \ 178 | SCENARIO="simple-p2p --delay=15ms --bandwidth=10Mbps --queue=25" \ 179 | docker-compose up 180 | ``` 181 | 182 | A mounted directory is provided for recording logs from the endpoints. 183 | docker-compose creates a `logs/server` and `logs/client` directory from 184 | the directory from which it is run. Inside the docker container, the 185 | directory is available as `/logs`. 186 | 187 | 188 | ## Debugging and FAQs 189 | 190 | 1. With the server (similarly for the client) up and running, you can get a root 191 | shell in the server docker container using the following: 192 | 193 | ```bash 194 | docker exec -it server /bin/bash 195 | ``` 196 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | 3 | services: 4 | sim: 5 | image: martenseemann/quic-network-simulator 6 | container_name: sim 7 | hostname: sim 8 | stdin_open: true 9 | tty: true 10 | volumes: 11 | - ./logs/sim:/logs 12 | environment: 13 | - SCENARIO=$SCENARIO 14 | cap_add: 15 | - NET_ADMIN 16 | expose: 17 | - "57832" 18 | networks: 19 | leftnet: 20 | ipv4_address: 193.167.0.2 21 | ipv6_address: fd00:cafe:cafe:0::2 22 | rightnet: 23 | ipv4_address: 193.167.100.2 24 | ipv6_address: fd00:cafe:cafe:100::2 25 | 26 | server: 27 | build: ./$SERVER 28 | image: $SERVER 29 | container_name: server 30 | hostname: server 31 | stdin_open: true 32 | tty: true 33 | volumes: 34 | - ./logs/server:/logs 35 | environment: 36 | - ROLE=server 37 | - SERVER_PARAMS=$SERVER_PARAMS 38 | depends_on: 39 | - sim 40 | cap_add: 41 | - NET_ADMIN 42 | networks: 43 | rightnet: 44 | ipv4_address: 193.167.100.100 45 | ipv6_address: fd00:cafe:cafe:100::100 46 | extra_hosts: 47 | - "client4:193.167.0.100" 48 | - "client6:fd00:cafe:cafe:0::100" 49 | - "client46:193.167.0.100" 50 | - "client46:fd00:cafe:cafe:0::100" 51 | 52 | client: 53 | build: ./$CLIENT 54 | image: $CLIENT 55 | container_name: client 56 | hostname: client 57 | stdin_open: true 58 | tty: true 59 | volumes: 60 | - ./logs/client:/logs 61 | environment: 62 | - ROLE=client 63 | - CLIENT_PARAMS=$CLIENT_PARAMS 64 | depends_on: 65 | - sim 66 | cap_add: 67 | - NET_ADMIN 68 | networks: 69 | leftnet: 70 | ipv4_address: 193.167.0.100 71 | ipv6_address: fd00:cafe:cafe:0::100 72 | extra_hosts: 73 | - "server4:193.167.100.100" 74 | - "server6:fd00:cafe:cafe:100::100" 75 | - "server46:193.167.100.100" 76 | - "server46:fd00:cafe:cafe:100::100" 77 | 78 | networks: 79 | leftnet: 80 | driver: bridge 81 | driver_opts: 82 | com.docker.network.bridge.enable_ip_masquerade: 'false' 83 | enable_ipv6: true 84 | ipam: 85 | config: 86 | - subnet: 193.167.0.0/24 87 | - subnet: fd00:cafe:cafe:0::/64 88 | rightnet: 89 | driver: bridge 90 | driver_opts: 91 | com.docker.network.bridge.enable_ip_masquerade: 'false' 92 | enable_ipv6: true 93 | ipam: 94 | config: 95 | - subnet: 193.167.100.0/24 96 | - subnet: fd00:cafe:cafe:100::/64 97 | -------------------------------------------------------------------------------- /endpoint/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y wget net-tools iputils-ping tcpdump ethtool iperf iproute2 5 | 6 | COPY setup.sh . 7 | RUN chmod +x setup.sh 8 | 9 | COPY run_endpoint.sh . 10 | RUN chmod +x run_endpoint.sh 11 | 12 | RUN wget https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh && chmod +x wait-for-it.sh 13 | 14 | ENTRYPOINT [ "/run_endpoint.sh" ] 15 | -------------------------------------------------------------------------------- /endpoint/run_endpoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set up the routing needed for the simulation. 4 | /setup.sh 5 | 6 | if [ "$ROLE" == "client" ]; then 7 | # Wait for the simulator to start up. 8 | /wait-for-it.sh sim:57832 -s -t 30 9 | fi 10 | 11 | /bin/bash 12 | -------------------------------------------------------------------------------- /endpoint/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Setting up routes..." 4 | # By default, docker containers don't compute UDP / TCP checksums. 5 | # When packets run through ns3 however, the receiving endpoint requires valid checksums. 6 | # This command makes sure that the endpoints set the checksum on outgoing packets. 7 | ethtool -K eth0 tx off 8 | 9 | # this relies on the IPv4 address being first in the "hostname -I" output 10 | IP=$(hostname -I | cut -f1 -d" ") 11 | GATEWAY="${IP%.*}.2" 12 | UNNEEDED_ROUTE="${IP%.*}.0" 13 | echo "Endpoint's IPv4 address is $IP" 14 | 15 | route add -net 193.167.0.0 netmask 255.255.0.0 gw $GATEWAY 16 | # delete unused route 17 | route del -net $UNNEEDED_ROUTE netmask 255.255.255.0 18 | 19 | # this relies on the IPv6 address being second in the "hostname -I" output 20 | IP=$(hostname -I | cut -f2 -d" ") 21 | GATEWAY="${IP%:*}:2" 22 | UNNEEDED_ROUTE="${IP%:*}:" 23 | echo "Endpoint's IPv6 address is $IP" 24 | 25 | ip -d route add fd00:cafe:cafe::/48 via $GATEWAY 26 | # delete unused route 27 | ip -d route del $UNNEEDED_ROUTE/64 28 | 29 | # create the /logs and the /logs/qlog directory 30 | mkdir -p /logs/qlog 31 | -------------------------------------------------------------------------------- /interop/README.md: -------------------------------------------------------------------------------- 1 | # Interop Test Runner 2 | 3 | Moved to https://github.com/marten-seemann/quic-interop-runner. 4 | -------------------------------------------------------------------------------- /sim/CMakeLists.patch: -------------------------------------------------------------------------------- 1 | diff --git a/scratch/CMakeLists.txt b/scratch/CMakeLists.txt 2 | index adeeb2cf8..6e23f81b6 100644 3 | --- a/scratch/CMakeLists.txt 4 | +++ b/scratch/CMakeLists.txt 5 | @@ -58,6 +58,11 @@ function(create_scratch source_files) 6 | set(target_prefix scratch${scratch_dirname}_) 7 | endif() 8 | 9 | + # Link our scratches against the helper files 10 | + get_filename_component(scratch_dirname ${scratch_src} DIRECTORY) 11 | + file(GLOB helper_files CONFIGURE_DEPENDS ${scratch_dirname}/../helper/*) 12 | + list(APPEND source_files ${helper_files}) 13 | + 14 | # Get source absolute path and transform into relative path 15 | get_filename_component(scratch_src ${scratch_src} ABSOLUTE) 16 | get_filename_component(scratch_absolute_directory ${scratch_src} DIRECTORY) 17 | @@ -88,7 +93,8 @@ file( 18 | ) 19 | # Filter out files 20 | foreach(entry ${scratch_subdirectories}) 21 | - if(NOT (IS_DIRECTORY ${entry})) 22 | + # Don't treat our helper directory as a scratch 23 | + if(NOT (IS_DIRECTORY ${entry}) OR ${entry} MATCHES ".*/helper") 24 | list(REMOVE_ITEM scratch_subdirectories ${entry}) 25 | endif() 26 | endforeach() 27 | -------------------------------------------------------------------------------- /sim/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:24.04 AS builder 2 | 3 | ARG TARGETARCH 4 | RUN echo "TARGETARCH : $TARGETARCH" 5 | 6 | RUN apt-get update && \ 7 | DEBIAN_FRONTEND=noninteractive apt-get install -y \ 8 | python3 build-essential cmake ninja-build libboost-dev libgsl-dev libxml2-dev \ 9 | libsqlite3-dev golang-go 10 | 11 | ENV NS_VERS=3.44 12 | ADD --checksum=sha256:fd2c8ed5814cc298753fb47c73535b6c1127b38d05c9104ced3f6f0b5e693787 \ 13 | https://www.nsnam.org/release/ns-allinone-$NS_VERS.tar.bz2 ns3.tar.bz2 14 | RUN tar xjf ns3.tar.bz2 && rm ns3.tar.bz2 15 | RUN mv /ns-allinone-$NS_VERS/ns-$NS_VERS /ns3 16 | 17 | WORKDIR /ns3 18 | 19 | RUN mkdir out/ 20 | RUN ./ns3 configure --build-profile=release --out=out/ 21 | 22 | # make including of the QuicNetworkSimulatorHelper class possible 23 | COPY CMakeLists.patch . 24 | RUN patch -d scratch < CMakeLists.patch 25 | COPY scenarios scratch/ 26 | 27 | # compile all the scenarios 28 | RUN ./ns3 build 29 | 30 | # strip ns3 version prefix from scratches 31 | RUN find out/scratch -name "ns${NS_VERS}-*" | \ 32 | sed -e 'p' -E -e "s|ns${NS_VERS}-*||g" | \ 33 | xargs -n2 mv 34 | 35 | COPY wait-for-it-quic /wait-for-it-quic 36 | RUN cd /wait-for-it-quic && go build . 37 | 38 | FROM ubuntu:24.04 39 | 40 | RUN apt-get update && \ 41 | apt-get install -y --no-install-recommends \ 42 | net-tools iptables libgsl-dev libxml2 libsqlite3-0 tcpdump && \ 43 | apt-get autoremove -y && apt-get clean -y && \ 44 | rm -rf /var/lib/apt/lists/* 45 | 46 | WORKDIR /ns3 47 | COPY --from=builder /ns3/out/src/fd-net-device/* /ns3/out/src/fd-net-device/ 48 | COPY --from=builder /ns3/out/scratch/*/* /ns3/scratch/ 49 | COPY --from=builder /ns3/out/lib/ /ns3/out/lib 50 | COPY --from=builder /wait-for-it-quic/wait-for-it-quic /usr/bin 51 | 52 | COPY run.sh . 53 | RUN chmod +x run.sh 54 | RUN mkdir /logs 55 | 56 | ENTRYPOINT [ "./run.sh" ] 57 | -------------------------------------------------------------------------------- /sim/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # We are using eth0 and eth1 as EmuFdNetDevices in ns3. 6 | # Use promiscuous mode to allow ns3 to capture all packets. 7 | ifconfig eth0 promisc 8 | ifconfig eth1 promisc 9 | 10 | # A packet arriving at eth0 destined to 10.100.0.0/16 could be routed directly to eth1, 11 | # and a packet arriving at eth1 destined to 10.0.0.0/16 directly to eth0. 12 | # This would allow packets to skip the ns3 simulator altogether. 13 | # Drop those to make sure they actually take the path through ns3. 14 | iptables -A FORWARD -i eth0 -o eth1 -j DROP 15 | iptables -A FORWARD -i eth1 -o eth0 -j DROP 16 | ip6tables -A FORWARD -i eth0 -o eth1 -j DROP 17 | ip6tables -A FORWARD -i eth1 -o eth0 -j DROP 18 | 19 | if [[ -n "$WAITFORSERVER" ]]; then 20 | wait-for-it-quic -t 10s $WAITFORSERVER 21 | fi 22 | 23 | echo "Using scenario:" $SCENARIO 24 | 25 | tcpdump -i eth0 -U -w "/logs/trace_node_left.pcap" & 26 | tcpdump -i eth1 -U -w "/logs/trace_node_right.pcap" & 27 | eval ./scratch/"$SCENARIO &" 28 | 29 | PID=`jobs -p` 30 | trap "kill -SIGINT $PID" INT 31 | trap "kill -SIGTERM $PID" TERM 32 | trap "kill -SIGKILL $PID" KILL 33 | wait 34 | -------------------------------------------------------------------------------- /sim/scenarios/blackhole/README.md: -------------------------------------------------------------------------------- 1 | # Blackhole 2 | 3 | This scenario uses a bottleneck link like the [../simple-p2p](simple-p2p) 4 | scenario. The link can be configured to start dropping (blackholing) all 5 | packets for a certain duration, and unblock some time later. Optionally, 6 | this pattern can be repeated. 7 | 8 | This scenario can be used to test various features of QUIC: 9 | * Short durations can be used to test PTOs. 10 | * Slightly longer durations can be used to test 11 | the detection of persistent congestion. 12 | * Long durations can be used to test the idle timeout. 13 | 14 | This scenario has the following configurable properties: 15 | 16 | * `--delay`: One-way delay of network. Specify with units. This is a required 17 | parameter. For example `--delay=15ms`. 18 | 19 | * `--bandwidth`: Bandwidth of the link. Specify with units. This is a required 20 | parameter. For example `--bandwidth=10Mbps`. Specifying a value larger than 21 | 10Mbps may cause the simulator to saturate the CPU. 22 | 23 | * `--queue`: Queue size of the queue attached to the link. Specified in 24 | packets. This is a required parameter. For example `--queue=25`. 25 | 26 | * `--on`: Time period that traffic is allowed to flow. Specify with units. This is a 27 | required parameter. For example `--on=10s`. 28 | 29 | * `--off`: Time period that traffic is blocked. Specify with units. This is a required 30 | parameter. For example `--off=3s`. 31 | 32 | * `--repeat`: Repeatedly block and unblock traffic. This is an optional 33 | parameter, which defaults to 1. For example `--repeat=2` will allow traffic 34 | `on` seconds, block it for `off` seconds, allow it for `on` seconds, block it 35 | for `off` seconds, and then allow traffic indefinitely. 36 | 37 | * `--direction`: Specifiy the direction in which to block traffic. This is an 38 | optional parameter. Valid values are `both`, `toclient` and `toserver`, to block 39 | traffic in both directions, traffic flowing towards, or traffic flowing towards the 40 | server, respectively. Defaults to `both`. 41 | 42 | 43 | 44 | For example, 45 | ```bash 46 | ./run.sh "blackhole --delay=15ms --bandwidth=10Mbps --queue=25 --on=10s --off=2s" 47 | ``` 48 | -------------------------------------------------------------------------------- /sim/scenarios/blackhole/blackhole-error-model.cc: -------------------------------------------------------------------------------- 1 | #include "blackhole-error-model.h" 2 | 3 | NS_OBJECT_ENSURE_REGISTERED(BlackholeErrorModel); 4 | 5 | TypeId BlackholeErrorModel::GetTypeId(void) { 6 | static TypeId tid = TypeId("BlackholeErrorModel") 7 | .SetParent() 8 | .AddConstructor() 9 | ; 10 | return tid; 11 | } 12 | 13 | BlackholeErrorModel::BlackholeErrorModel() : enabled_(true) { } 14 | 15 | bool BlackholeErrorModel::DoCorrupt(Ptr p) { 16 | return enabled_; 17 | } 18 | 19 | void BlackholeErrorModel::Enable() { 20 | enabled_ = true; 21 | } 22 | 23 | void BlackholeErrorModel::Disable() { 24 | enabled_ = false; 25 | } 26 | 27 | void BlackholeErrorModel::DoReset(void) { } 28 | -------------------------------------------------------------------------------- /sim/scenarios/blackhole/blackhole-error-model.h: -------------------------------------------------------------------------------- 1 | #ifndef BLACKHOLE_ERROR_MODEL_H 2 | #define BLACKHOLE_ERROR_MODEL_H 3 | 4 | #include "ns3/error-model.h" 5 | 6 | using namespace ns3; 7 | 8 | // The BlackholeErrorModel drops all packets. 9 | class BlackholeErrorModel : public ErrorModel { 10 | public: 11 | static TypeId GetTypeId(void); 12 | BlackholeErrorModel(); 13 | 14 | void Enable(); 15 | void Disable(); 16 | 17 | private: 18 | bool enabled_; 19 | bool DoCorrupt (Ptr p); 20 | void DoReset(void); 21 | }; 22 | 23 | #endif /* BLACKHOLE_ERROR_MODEL_H */ 24 | -------------------------------------------------------------------------------- /sim/scenarios/blackhole/blackhole.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/error-model.h" 3 | #include "ns3/internet-module.h" 4 | #include "ns3/point-to-point-module.h" 5 | #include "../helper/quic-network-simulator-helper.h" 6 | #include "../helper/quic-point-to-point-helper.h" 7 | #include "blackhole-error-model.h" 8 | 9 | using namespace ns3; 10 | 11 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 12 | 13 | void enable(Ptr em, const Time next, const int repeat) { 14 | static int counter = 0; 15 | counter++; 16 | std::cout << Simulator::Now().GetSeconds() << "s: Enabling blackhole" << std::endl; 17 | em->Enable(); 18 | if(counter < repeat) { 19 | Simulator::Schedule(next, &enable, em, next, repeat); 20 | } 21 | } 22 | 23 | void disable(Ptr em, const Time next, const int repeat) { 24 | static int counter = 0; 25 | std::cout << Simulator::Now().GetSeconds() << "s: Disabling blackhole" << std::endl; 26 | counter++; 27 | em->Disable(); 28 | if(counter < repeat) { 29 | Simulator::Schedule(next, &disable, em, next, repeat); 30 | } 31 | } 32 | 33 | int main(int argc, char *argv[]) { 34 | std::string delay, bandwidth, queue, on, off, repeat_s, drop_direction_s; 35 | CommandLine cmd; 36 | cmd.AddValue("delay", "delay of the p2p link", delay); 37 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 38 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 39 | cmd.AddValue("on", "time that the connection is active (e.g. 15s)", on); 40 | cmd.AddValue("off", "time that the connection is dropping all packets (e.g. 2s)", off); 41 | cmd.AddValue("repeat", "(optional) turn the connection on and off this many times. Default: 1", repeat_s); 42 | cmd.AddValue("direction", "(optional) [ both, toclient, toserver ] direction in which to drop packet. Default: both", drop_direction_s); 43 | cmd.Parse (argc, argv); 44 | 45 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 46 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 47 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 48 | NS_ABORT_MSG_IF(on.length() == 0, "Missing parameter: on"); 49 | NS_ABORT_MSG_IF(off.length() == 0, "Missing parameter: off"); 50 | 51 | int repeat = 1; 52 | if(repeat_s.length() > 0) { 53 | repeat = std::stoi(repeat_s); 54 | } 55 | NS_ABORT_MSG_IF(repeat <= 0, "Invalid value: repeat value must be greater than zero."); 56 | 57 | enum drop_direction { both, to_client, to_server }; 58 | 59 | drop_direction drop_dir = both; 60 | if(drop_direction_s.length() > 0) { 61 | if(drop_direction_s == "toclient") drop_dir = to_client; 62 | else if(drop_direction_s == "toserver") drop_dir = to_server; 63 | else if(drop_direction_s == "both") drop_dir = both; 64 | else NS_ABORT_MSG("Invalid directon value."); 65 | } 66 | 67 | QuicNetworkSimulatorHelper sim; 68 | 69 | // Stick in the point-to-point line between the sides. 70 | QuicPointToPointHelper p2p; 71 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 72 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 73 | p2p.SetQueueSize(StringValue(queue + "p")); 74 | 75 | NetDeviceContainer devices = p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 76 | 77 | Ptr em = CreateObject(); 78 | em->Disable(); 79 | if(drop_dir == to_client || drop_dir == both) { 80 | devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(em)); 81 | } 82 | if(drop_dir == to_server || drop_dir == both) { 83 | devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em)); 84 | } 85 | 86 | Time intv = Time(on) + Time(off); 87 | Simulator::Schedule(Time(on), &enable, em, intv, repeat); 88 | Simulator::Schedule(intv, &disable, em, intv, repeat); 89 | 90 | sim.Run(Seconds(36000)); 91 | } 92 | -------------------------------------------------------------------------------- /sim/scenarios/corrupt-rate/README.md: -------------------------------------------------------------------------------- 1 | # Corrupt Rate 2 | 3 | This scenario uses a bottleneck link similar to the [simple-p2p](../simple-p2p) 4 | scenario and optionally allows configuring the link to corrupt packets in either 5 | direction. Packets to be corrupted are chosen randomly in both directions, based 6 | on rates specified by the user, but no more than a given number of packets are 7 | corrupted in a row. 8 | 9 | This scenario has the following configurable properties: 10 | 11 | * `--delay`: One-way delay of network. Specify with units. This is a required 12 | parameter. For example `--delay=15ms`. 13 | 14 | * `--bandwidth`: Bandwidth of the link. Specify with units. This is a required 15 | parameter. For example `--bandwidth=10Mbps`. Specifying a value larger than 16 | 10Mbps may cause the simulator to saturate the CPU. 17 | 18 | * `--queue`: Queue size of the queue attached to the link. Specified in 19 | packets. This is a required parameter. For example `--queue=25`. 20 | 21 | * `--rate_to_client`: A value between 0 and 100 specifying the packet corruption 22 | rate (in percentage) in the server to client direction. This is a required 23 | parameter. For example, `--rate_to_client=10`. 24 | 25 | * `--burst_to_client`: The maximum number of packets that will be corrupted in a 26 | row in the server to client direction. This is an optional parameter. For 27 | example, `--burst_to_client=3`. 28 | 29 | * `--rate_to_server`: Same as `rate_to_client` but in the other direction. 30 | 31 | * `--burst_to_server`: Same as `burst_to_client` but in the other direction. 32 | 33 | For example, 34 | ```bash 35 | ./run.sh "corrupt-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_client=10 --rate_to_server=20 --burst_to_client=3 --burst_to_server=3" 36 | ``` 37 | -------------------------------------------------------------------------------- /sim/scenarios/corrupt-rate/corrupt-rate-error-model.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "corrupt-rate-error-model.h" 7 | #include "../helper/quic-packet.h" 8 | 9 | using namespace std; 10 | 11 | NS_OBJECT_ENSURE_REGISTERED(CorruptRateErrorModel); 12 | 13 | TypeId CorruptRateErrorModel::GetTypeId(void) { 14 | static TypeId tid = TypeId("CorruptRateErrorModel") 15 | .SetParent() 16 | .AddConstructor() 17 | ; 18 | return tid; 19 | } 20 | 21 | CorruptRateErrorModel::CorruptRateErrorModel() 22 | : rate(0), distr(0, 99), burst(INT_MAX), corrupted_in_a_row(0), corrupted(0), forwarded(0) { 23 | std::random_device rd; 24 | rng = new std::mt19937(rd()); 25 | } 26 | 27 | void CorruptRateErrorModel::DoReset(void) { 28 | corrupted_in_a_row = 0; 29 | corrupted = 0; 30 | forwarded = 0; 31 | } 32 | 33 | bool CorruptRateErrorModel::DoCorrupt(Ptr p) { 34 | if(!IsUDPPacket(p)) return false; 35 | 36 | QuicPacket qp = QuicPacket(p); 37 | 38 | bool shouldCorrupt = false; 39 | if (qp.IsVersionNegotiationPacket()) { 40 | // Don't corrupt Version Negotiation packets. 41 | // Version Negotiation packets are expected to be sent when the version 42 | // field of the Initial was corrupted. Client are supposed to ignore 43 | // Version Negotiation packets that contain the version that they 44 | // offered. 45 | corrupted_in_a_row = 0; 46 | shouldCorrupt = false; 47 | } else if (corrupted_in_a_row >= burst) { 48 | corrupted_in_a_row = 0; 49 | shouldCorrupt = false; 50 | } else if (distr(*rng) < rate) { 51 | corrupted_in_a_row++; 52 | shouldCorrupt = true; 53 | } else { 54 | corrupted_in_a_row = 0; 55 | shouldCorrupt = false; 56 | } 57 | 58 | int pos = 0; 59 | uint8_t old_n = 0; 60 | uint8_t new_n = 0; 61 | if (shouldCorrupt) { 62 | cout << "Corrupting "; 63 | corrupted++; 64 | 65 | // Corrupt a byte in the 50 bytes of the UDP payload. 66 | // This way, we will frequently hit the QUIC header. 67 | std::uniform_int_distribution<> d(0, min(uint32_t(50), p->GetSize() - 1)); 68 | pos = d(*rng); 69 | vector & payload = qp.GetUdpPayload(); 70 | // Replace the byte at position pos with a random value. 71 | while (true) { 72 | uint8_t n = std::uniform_int_distribution<>(0, 255)(*rng); 73 | if (payload[pos] == n) 74 | continue; 75 | old_n = payload[pos]; 76 | payload[pos] = n; 77 | new_n = payload[pos]; 78 | break; 79 | } 80 | } else { 81 | cout << "Forwarding "; 82 | forwarded++; 83 | } 84 | qp.ReassemblePacket(); 85 | 86 | cout << qp.GetUdpPayload().size() << " bytes " 87 | << qp.GetIpv4Header().GetSource() << ":" 88 | << qp.GetUdpHeader().GetSourcePort() << " -> " 89 | << qp.GetIpv4Header().GetDestination() << ":" 90 | << qp.GetUdpHeader().GetDestinationPort(); 91 | if (pos != 0) 92 | cout << " offset " << pos << " 0x" << std::hex 93 | << (unsigned int)old_n << " -> 0x" << (unsigned int)new_n 94 | << std::dec; 95 | cout << ", corrupted " 96 | << corrupted << "/" << corrupted + forwarded << " (" << fixed 97 | << setprecision(1) << (double)corrupted / (corrupted + forwarded) * 100 98 | << "%)" << endl; 99 | 100 | return shouldCorrupt; 101 | } 102 | 103 | void CorruptRateErrorModel::SetCorruptRate(int rate_in) { 104 | rate = rate_in; 105 | } 106 | 107 | void CorruptRateErrorModel::SetMaxCorruptBurst(int burst_in) { 108 | burst = burst_in; 109 | } 110 | -------------------------------------------------------------------------------- /sim/scenarios/corrupt-rate/corrupt-rate-error-model.h: -------------------------------------------------------------------------------- 1 | #ifndef CORRUPTRATE_ERROR_MODEL_H 2 | #define CORRUPTRATE_ERROR_MODEL_H 3 | 4 | #include 5 | #include 6 | #include "ns3/error-model.h" 7 | 8 | using namespace ns3; 9 | 10 | // The CorruptRateErrorModel corrupts random packets, at a user-specified rate. 11 | // But no more than a user-specified number of packets in a row. 12 | class CorruptRateErrorModel : public RateErrorModel { 13 | public: 14 | static TypeId GetTypeId(void); 15 | CorruptRateErrorModel(); 16 | void SetCorruptRate(int perc); 17 | void SetMaxCorruptBurst(int burst); 18 | 19 | private: 20 | int rate; 21 | std::mt19937 *rng; 22 | std::uniform_int_distribution<> distr; 23 | int burst; 24 | int corrupted_in_a_row; 25 | int corrupted; 26 | int forwarded; 27 | 28 | bool DoCorrupt (Ptr p); 29 | void DoReset(void); 30 | }; 31 | 32 | #endif /* CORRUPTRATE_ERROR_MODEL_H */ 33 | -------------------------------------------------------------------------------- /sim/scenarios/corrupt-rate/corrupt-rate.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/error-model.h" 3 | #include "ns3/internet-module.h" 4 | #include "ns3/point-to-point-module.h" 5 | #include "../helper/quic-network-simulator-helper.h" 6 | #include "../helper/quic-point-to-point-helper.h" 7 | #include "corrupt-rate-error-model.h" 8 | 9 | using namespace ns3; 10 | using namespace std; 11 | 12 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 13 | 14 | int main(int argc, char *argv[]) { 15 | std::string delay, bandwidth, queue, client_rate, server_rate, 16 | client_burst, server_burst; 17 | std::random_device rand_dev; 18 | std::mt19937 generator(rand_dev()); // Seed random number generator first 19 | Ptr client_corrupts = CreateObject(); 20 | Ptr server_corrupts = CreateObject(); 21 | CommandLine cmd; 22 | 23 | cmd.AddValue("delay", "delay of the p2p link", delay); 24 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 25 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 26 | cmd.AddValue("rate_to_client", "packet corruption rate (towards client)", client_rate); 27 | cmd.AddValue("rate_to_server", "packet corruption rate (towards server)", server_rate); 28 | cmd.AddValue("burst_to_client", 29 | "max. packet corruption burst length (towards client)", 30 | client_burst); 31 | cmd.AddValue("burst_to_server", 32 | "max. packet corruption burst length (towards server)", 33 | server_burst); 34 | cmd.Parse (argc, argv); 35 | 36 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 37 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 38 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 39 | NS_ABORT_MSG_IF(client_rate.length() == 0, "Missing parameter: rate_to_client"); 40 | NS_ABORT_MSG_IF(server_rate.length() == 0, "Missing parameter: rate_to_server"); 41 | 42 | // Set client and server corruption rates. 43 | client_corrupts->SetCorruptRate(stoi(client_rate)); 44 | if (client_burst.length() > 0) 45 | client_corrupts->SetMaxCorruptBurst(stoi(client_burst)); 46 | server_corrupts->SetCorruptRate(stoi(server_rate)); 47 | if (server_burst.length() > 0) 48 | server_corrupts->SetMaxCorruptBurst(stoi(server_burst)); 49 | 50 | QuicNetworkSimulatorHelper sim; 51 | 52 | // Stick in the point-to-point line between the sides. 53 | QuicPointToPointHelper p2p; 54 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 55 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 56 | p2p.SetQueueSize(StringValue(queue + "p")); 57 | 58 | NetDeviceContainer devices = p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 59 | 60 | devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(client_corrupts)); 61 | devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(server_corrupts)); 62 | 63 | sim.Run(Seconds(36000)); 64 | } 65 | -------------------------------------------------------------------------------- /sim/scenarios/drop-rate/README.md: -------------------------------------------------------------------------------- 1 | # Drop Rate 2 | 3 | This scenario uses a bottleneck link similar to the [simple-p2p](../simple-p2p) 4 | scenario and optionally allows configuring the link to drop packets in either 5 | direction. Packets to be dropped are chosen randomly in both directions, based 6 | on rates specified by the user, but no more than a given number of packets are 7 | dropped in a row. 8 | 9 | This scenario has the following configurable properties: 10 | 11 | * `--delay`: One-way delay of network. Specify with units. This is a required 12 | parameter. For example `--delay=15ms`. 13 | 14 | * `--bandwidth`: Bandwidth of the link. Specify with units. This is a required 15 | parameter. For example `--bandwidth=10Mbps`. Specifying a value larger than 16 | 10Mbps may cause the simulator to saturate the CPU. 17 | 18 | * `--queue`: Queue size of the queue attached to the link. Specified in 19 | packets. This is a required parameter. For example `--queue=25`. 20 | 21 | * `--rate_to_client`: A value between 0 and 100 specifying the packet drop rate 22 | (in percentage) in the server to client direction. This is a required 23 | parameter. For example, `--rate_to_client=10`. 24 | 25 | * `--burst_to_client`: The maximum number of packets that will be dropped in a 26 | row in the server to client direction. This is an optional parameter. For 27 | example, `--burst_to_client=3`. 28 | 29 | * `--rate_to_server`: Same as `rate_to_client` but in the other direction. 30 | 31 | * `--burst_to_server`: Same as `burst_to_client` but in the other direction. 32 | 33 | For example, 34 | ```bash 35 | ./run.sh "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_client=10 --rate_to_server=20 --burst_to_client=3 --burst_to_server=3" 36 | ``` 37 | -------------------------------------------------------------------------------- /sim/scenarios/drop-rate/drop-rate-error-model.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../helper/quic-packet.h" 4 | #include "drop-rate-error-model.h" 5 | 6 | using namespace std; 7 | 8 | NS_OBJECT_ENSURE_REGISTERED(DropRateErrorModel); 9 | 10 | TypeId DropRateErrorModel::GetTypeId(void) { 11 | static TypeId tid = TypeId("DropRateErrorModel") 12 | .SetParent() 13 | .AddConstructor() 14 | ; 15 | return tid; 16 | } 17 | 18 | DropRateErrorModel::DropRateErrorModel() 19 | : rate(0), distr(0, 99), burst(INT_MAX), dropped_in_a_row(0), dropped(0), forwarded(0) 20 | { 21 | std::random_device rd; 22 | rng = new std::mt19937(rd()); 23 | } 24 | 25 | void DropRateErrorModel::DoReset(void) { 26 | dropped_in_a_row = 0; 27 | dropped = 0; 28 | forwarded = 0; 29 | } 30 | 31 | bool DropRateErrorModel::DoCorrupt(Ptr p) { 32 | if(!IsUDPPacket(p)) return false; 33 | 34 | bool shouldDrop = false; 35 | if (dropped_in_a_row >= burst) { 36 | dropped_in_a_row = 0; 37 | shouldDrop = false; 38 | } else if (distr(*rng) < rate) { 39 | dropped_in_a_row++; 40 | shouldDrop = true; 41 | } else { 42 | dropped_in_a_row = 0; 43 | shouldDrop = false; 44 | } 45 | 46 | QuicPacket qp = QuicPacket(p); 47 | 48 | if (shouldDrop) { 49 | cout << "Dropping "; 50 | dropped++; 51 | } else { 52 | cout << "Forwarding "; 53 | forwarded++; 54 | qp.ReassemblePacket(); 55 | } 56 | cout << qp.GetUdpPayload().size() 57 | << " bytes " << qp.GetIpv4Header().GetSource() << ":" 58 | << qp.GetUdpHeader().GetSourcePort() << " -> " 59 | << qp.GetIpv4Header().GetDestination() << ":" 60 | << qp.GetUdpHeader().GetDestinationPort() 61 | << ", dropped " << dropped << "/" << dropped + forwarded << " (" 62 | << fixed << setprecision(1) 63 | << (double)dropped / (dropped + forwarded) * 100 64 | << "%)" << endl; 65 | 66 | return shouldDrop; 67 | } 68 | 69 | void DropRateErrorModel::SetDropRate(int rate_in) { 70 | rate = rate_in; 71 | } 72 | 73 | void DropRateErrorModel::SetMaxDropBurst(int burst_in) { 74 | burst = burst_in; 75 | } 76 | -------------------------------------------------------------------------------- /sim/scenarios/drop-rate/drop-rate-error-model.h: -------------------------------------------------------------------------------- 1 | #ifndef DROPRATE_ERROR_MODEL_H 2 | #define DROPRATE_ERROR_MODEL_H 3 | 4 | #include 5 | #include 6 | #include "ns3/error-model.h" 7 | 8 | using namespace ns3; 9 | 10 | // The DropRateErrorModel drops random packets, at a user-specified drop rate. 11 | // But no more than a user-specified number of packets in a row. 12 | class DropRateErrorModel : public ErrorModel { 13 | public: 14 | static TypeId GetTypeId(void); 15 | DropRateErrorModel(); 16 | void SetDropRate(int perc); 17 | void SetMaxDropBurst(int burst); 18 | 19 | private: 20 | int rate; 21 | std::mt19937 *rng; 22 | std::uniform_int_distribution<> distr; 23 | int next_rate; 24 | int burst; 25 | int dropped_in_a_row; 26 | int dropped; 27 | int forwarded; 28 | 29 | bool DoCorrupt (Ptr p); 30 | void DoReset(void); 31 | }; 32 | 33 | #endif /* DROPRATE_ERROR_MODEL_H */ 34 | -------------------------------------------------------------------------------- /sim/scenarios/drop-rate/drop-rate.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/error-model.h" 3 | #include "ns3/internet-module.h" 4 | #include "ns3/point-to-point-module.h" 5 | #include "../helper/quic-network-simulator-helper.h" 6 | #include "../helper/quic-point-to-point-helper.h" 7 | #include "drop-rate-error-model.h" 8 | 9 | using namespace ns3; 10 | using namespace std; 11 | 12 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 13 | 14 | int main(int argc, char *argv[]) { 15 | std::string delay, bandwidth, queue, client_rate, server_rate, 16 | client_burst, server_burst; 17 | std::random_device rand_dev; 18 | std::mt19937 generator(rand_dev()); // Seed random number generator first 19 | Ptr client_drops = CreateObject(); 20 | Ptr server_drops = CreateObject(); 21 | CommandLine cmd; 22 | 23 | cmd.AddValue("delay", "delay of the p2p link", delay); 24 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 25 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 26 | cmd.AddValue("rate_to_client", "packet drop rate (towards client)", client_rate); 27 | cmd.AddValue("rate_to_server", "packet drop rate (towards server)", server_rate); 28 | cmd.AddValue("burst_to_client", 29 | "max. packet drop burst length (towards client)", 30 | client_burst); 31 | cmd.AddValue("burst_to_server", 32 | "max. packet drop burst length (towards server)", 33 | server_burst); 34 | cmd.Parse (argc, argv); 35 | 36 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 37 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 38 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 39 | NS_ABORT_MSG_IF(client_rate.length() == 0, "Missing parameter: rate_to_client"); 40 | NS_ABORT_MSG_IF(server_rate.length() == 0, "Missing parameter: rate_to_server"); 41 | 42 | // Set client and server drop rates and drop bursts. 43 | client_drops->SetDropRate(stoi(client_rate)); 44 | if (client_burst.length() > 0) 45 | client_drops->SetMaxDropBurst(stoi(client_burst)); 46 | server_drops->SetDropRate(stoi(server_rate)); 47 | if (server_burst.length() > 0) 48 | server_drops->SetMaxDropBurst(stoi(server_burst)); 49 | 50 | QuicNetworkSimulatorHelper sim; 51 | 52 | // Stick in the point-to-point line between the sides. 53 | QuicPointToPointHelper p2p; 54 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 55 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 56 | p2p.SetQueueSize(StringValue(queue + "p")); 57 | 58 | NetDeviceContainer devices = p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 59 | 60 | devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(client_drops)); 61 | devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(server_drops)); 62 | 63 | sim.Run(Seconds(36000)); 64 | } 65 | -------------------------------------------------------------------------------- /sim/scenarios/droplist/README.md: -------------------------------------------------------------------------------- 1 | # Droplist 2 | 3 | This scenario uses a bottleneck link similar to the [simple-p2p](../simple-p2p) 4 | scenario and optionally allows configuring the link to drop specific packets in 5 | either direction. 6 | 7 | This scenario can be used to test the performance of QUIC implementations under 8 | specific loss patterns. For instance, tests can: 9 | * drop specific handshake packets, 10 | * drop tail packets, 11 | * drop important control packets, such as path validation packets. 12 | 13 | This scenario has the following configurable properties: 14 | 15 | * `--delay`: One-way delay of network. Specify with units. This is a required 16 | parameter. For example `--delay=15ms`. 17 | 18 | * `--bandwidth`: Bandwidth of the link. Specify with units. This is a required 19 | parameter. For example `--bandwidth=10Mbps`. Specifying a value larger than 20 | 10Mbps may cause the simulator to saturate the CPU. 21 | 22 | * `--queue`: Queue size of the queue attached to the link. Specified in 23 | packets. This is a required parameter. For example `--queue=25`. 24 | 25 | * `--drops_to_client`: A comma-separated list of packets to drop, starting at 1, 26 | in the server to client direction. These packet numbers are simply the index of 27 | the packets arriving at the bottleneck link. This index applies to all IP 28 | packets arriving at the link, and may not be the same as the QUIC packet 29 | number, especially if an endpoint coalesces packets or if an endpoint resets 30 | packet numbers across epochs. This is an optional parameter. For example, 31 | `--drops_to_client=1,3,15`. (Note that there are no spaces in the list.)` 32 | 33 | * `--drops_to_server`: Same as `drops_to_client` but in the other direction. 34 | 35 | For example, 36 | ```bash 37 | ./run.sh "droplist --delay=15ms --bandwidth=10Mbps --queue=25 --drops_to_client=1,3,4 --drops_to_server=5" 38 | ``` 39 | -------------------------------------------------------------------------------- /sim/scenarios/droplist/droplist-error-model.cc: -------------------------------------------------------------------------------- 1 | #include "../helper/quic-packet.h" 2 | #include "droplist-error-model.h" 3 | 4 | using namespace std; 5 | 6 | NS_OBJECT_ENSURE_REGISTERED(DroplistErrorModel); 7 | 8 | TypeId DroplistErrorModel::GetTypeId(void) { 9 | static TypeId tid = TypeId("DroplistErrorModel") 10 | .SetParent() 11 | .AddConstructor() 12 | ; 13 | return tid; 14 | } 15 | 16 | DroplistErrorModel::DroplistErrorModel() 17 | : packet_num(0) { } 18 | 19 | void DroplistErrorModel::DoReset(void) { } 20 | 21 | bool DroplistErrorModel::DoCorrupt(Ptr p) { 22 | if(!IsUDPPacket(p)) return false; 23 | if(drops.find(++packet_num) == drops.end()) return false; 24 | 25 | QuicPacket qp = QuicPacket(p); 26 | cout << "Dropping packet " << packet_num << " (" << qp.GetUdpPayload().size() << " bytes) from " << qp.GetIpv4Header().GetSource() << endl; 27 | qp.ReassemblePacket(); 28 | return true; 29 | } 30 | 31 | void DroplistErrorModel::SetDrop(int packet_num) { 32 | drops.insert(packet_num); 33 | } 34 | -------------------------------------------------------------------------------- /sim/scenarios/droplist/droplist-error-model.h: -------------------------------------------------------------------------------- 1 | #ifndef DROPLIST_ERROR_MODEL_H 2 | #define DROPLIST_ERROR_MODEL_H 3 | 4 | #include 5 | #include "ns3/error-model.h" 6 | 7 | using namespace ns3; 8 | 9 | // The DroplistErrorModel drops packets enumerated by the user. This model does 10 | // a simple packet count and drops the specified packets. Packet numbering 11 | // starts at 1. 12 | 13 | class DroplistErrorModel : public ErrorModel { 14 | public: 15 | static TypeId GetTypeId(void); 16 | DroplistErrorModel(); 17 | void SetDrop(int packet_num); 18 | 19 | private: 20 | std::set drops; 21 | int packet_num; 22 | bool DoCorrupt (Ptr p); 23 | void DoReset(void); 24 | }; 25 | 26 | #endif /* DROPLIST_ERROR_MODEL_H */ 27 | -------------------------------------------------------------------------------- /sim/scenarios/droplist/droplist.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/error-model.h" 3 | #include "ns3/internet-module.h" 4 | #include "ns3/point-to-point-module.h" 5 | #include "../helper/quic-network-simulator-helper.h" 6 | #include "../helper/quic-point-to-point-helper.h" 7 | #include "droplist-error-model.h" 8 | 9 | using namespace ns3; 10 | using namespace std; 11 | 12 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 13 | 14 | void SetDrops(Ptr drop_model, string drops_in) { 15 | char *cstr = new char[drops_in.length()+1]; 16 | strcpy(cstr, drops_in.c_str()); 17 | char *p = strtok(cstr,","); 18 | while (p) { 19 | drop_model->SetDrop(stoi(p)); 20 | p = strtok(NULL,","); 21 | } 22 | delete[] cstr; 23 | } 24 | 25 | int main(int argc, char *argv[]) { 26 | std::string delay, bandwidth, queue, client_drops_in, server_drops_in; 27 | Ptr client_drops = CreateObject(); 28 | Ptr server_drops = CreateObject(); 29 | CommandLine cmd; 30 | 31 | cmd.AddValue("delay", "delay of the p2p link", delay); 32 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 33 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 34 | cmd.AddValue("drops_to_client", "list of packets (towards client) to drop", client_drops_in); 35 | cmd.AddValue("drops_to_server", "list of packets (towards server) to drop", server_drops_in); 36 | cmd.Parse (argc, argv); 37 | 38 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 39 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 40 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 41 | 42 | // Set client and server droplists. 43 | SetDrops(client_drops, client_drops_in); 44 | SetDrops(server_drops, server_drops_in); 45 | 46 | QuicNetworkSimulatorHelper sim; 47 | 48 | // Stick in the point-to-point line between the sides. 49 | QuicPointToPointHelper p2p; 50 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 51 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 52 | p2p.SetQueueSize(StringValue(queue + "p")); 53 | 54 | NetDeviceContainer devices = p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 55 | 56 | devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(client_drops)); 57 | devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(server_drops)); 58 | 59 | sim.Run(Seconds(36000)); 60 | } 61 | -------------------------------------------------------------------------------- /sim/scenarios/helper/quic-network-simulator-helper.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "ns3/core-module.h" 14 | #include "ns3/fd-net-device-module.h" 15 | #include "ns3/internet-module.h" 16 | #include "quic-network-simulator-helper.h" 17 | 18 | using namespace ns3; 19 | 20 | void onSignal(int signum) { 21 | std::cout << "Received signal: " << signum << std::endl; 22 | // see https://gitlab.com/nsnam/ns-3-dev/issues/102 23 | Simulator::Stop(); 24 | NS_FATAL_ERROR(signum); 25 | } 26 | 27 | void installNetDevice(Ptr node, std::string deviceName, Mac48AddressValue macAddress, Ipv4InterfaceAddress ipv4Address, Ipv6InterfaceAddress ipv6Address) { 28 | EmuFdNetDeviceHelper emu; 29 | emu.SetDeviceName(deviceName); 30 | NetDeviceContainer devices = emu.Install(node); 31 | Ptr device = devices.Get(0); 32 | device->SetAttribute("Address", macAddress); 33 | 34 | Ptr ipv4 = node->GetObject(); 35 | uint32_t interface = ipv4->AddInterface(device); 36 | ipv4->AddAddress(interface, ipv4Address); 37 | ipv4->SetMetric(interface, 1); 38 | ipv4->SetUp(interface); 39 | 40 | Ptr ipv6 = node->GetObject(); 41 | ipv6->SetAttribute("IpForward", BooleanValue(true)); 42 | interface = ipv6->AddInterface(device); 43 | ipv6->AddAddress(interface, ipv6Address); 44 | ipv6->SetMetric(interface, 1); 45 | ipv6->SetUp(interface); 46 | } 47 | 48 | Mac48Address getMacAddress(std::string iface) { 49 | unsigned char buf[6]; 50 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 51 | struct ifreq ifr; 52 | ifr.ifr_addr.sa_family = AF_INET; 53 | strncpy(ifr.ifr_name, iface.c_str(), IFNAMSIZ-1); 54 | ioctl(fd, SIOCGIFHWADDR, &ifr); 55 | for(unsigned int i=0; i<6; i++) { 56 | buf[i] = ifr.ifr_hwaddr.sa_data[i]; 57 | } 58 | ioctl(fd, SIOCGIFMTU, &ifr); 59 | close(fd); 60 | Mac48Address mac; 61 | mac.CopyFrom(buf); 62 | return mac; 63 | } 64 | 65 | QuicNetworkSimulatorHelper::QuicNetworkSimulatorHelper() { 66 | GlobalValue::Bind("SimulatorImplementationType", StringValue("ns3::RealtimeSimulatorImpl")); 67 | GlobalValue::Bind("ChecksumEnabled", BooleanValue(true)); 68 | 69 | NodeContainer nodes; 70 | nodes.Create(2); 71 | InternetStackHelper internet; 72 | internet.Install(nodes); 73 | 74 | left_node_ = nodes.Get(0); 75 | right_node_ = nodes.Get(1); 76 | 77 | installNetDevice(left_node_, "eth0", getMacAddress("eth0"), Ipv4InterfaceAddress("193.167.0.2", "255.255.255.0"), Ipv6InterfaceAddress("fd00:cafe:cafe:0::2", 64)); 78 | installNetDevice(right_node_, "eth1", getMacAddress("eth1"), Ipv4InterfaceAddress("193.167.100.2", "255.255.255.0"), Ipv6InterfaceAddress("fd00:cafe:cafe:100::2", 64)); 79 | } 80 | 81 | void massageIpv6Routing(Ptr local, Ptr peer) { 82 | Ptr routing = Ipv6RoutingHelper::GetRouting(local->GetObject()->GetRoutingProtocol()); 83 | Ptr peer_ipv6 = peer->GetObject(); 84 | Ipv6Address dst; 85 | for (uint32_t i = 0; i < peer_ipv6->GetNInterfaces(); i++) 86 | for (uint32_t j = 0; j < peer_ipv6->GetNAddresses(i); j++) 87 | if (peer_ipv6->GetAddress(i, j).GetAddress().CombinePrefix(64) == "fd00:cafe:cafe:50::") { 88 | dst = peer_ipv6->GetAddress(i, j).GetAddress(); 89 | goto done; 90 | } 91 | 92 | done: 93 | assert(dst.IsInitialized()); 94 | routing->SetDefaultRoute(dst, 2); 95 | } 96 | 97 | void QuicNetworkSimulatorHelper::Run(Time duration) { 98 | signal(SIGTERM, onSignal); 99 | signal(SIGINT, onSignal); 100 | signal(SIGKILL, onSignal); 101 | 102 | Ipv4GlobalRoutingHelper::PopulateRoutingTables(); 103 | 104 | // Ipv6GlobalRoutingHelper does not exist - fake it 105 | massageIpv6Routing(left_node_, right_node_); 106 | massageIpv6Routing(right_node_, left_node_); 107 | 108 | // write the routing table to file 109 | Ptr routingStream = Create("dynamic-global-routing.routes", std::ios::out); 110 | Ipv4RoutingHelper::PrintRoutingTableAllAt(Seconds(0.), routingStream); 111 | Ipv6RoutingHelper::PrintRoutingTableAllAt(Seconds(0.), routingStream); 112 | 113 | Simulator::Stop(duration); 114 | RunSynchronizer(); 115 | Simulator::Run(); 116 | Simulator::Destroy(); 117 | } 118 | 119 | void QuicNetworkSimulatorHelper::RunSynchronizer() const { 120 | int sockfd = socket(AF_INET, SOCK_STREAM, 0); 121 | NS_ABORT_MSG_IF(sockfd < 0, "ERROR opening socket"); 122 | 123 | struct sockaddr_in addr; 124 | bzero((char *) &addr, sizeof(addr)); 125 | 126 | addr.sin_family = AF_INET; 127 | addr.sin_addr.s_addr = INADDR_ANY; 128 | addr.sin_port = htons(57832); 129 | 130 | int res = bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)); 131 | NS_ABORT_MSG_IF(res < 0, "ERROR on binding"); 132 | // We never intend to accept any of the connections. 133 | // Use a large backlog queue instead. 134 | listen(sockfd, 100); 135 | } 136 | 137 | Ptr QuicNetworkSimulatorHelper::GetLeftNode() const { 138 | return left_node_; 139 | } 140 | 141 | Ptr QuicNetworkSimulatorHelper::GetRightNode() const { 142 | return right_node_; 143 | } 144 | -------------------------------------------------------------------------------- /sim/scenarios/helper/quic-network-simulator-helper.h: -------------------------------------------------------------------------------- 1 | #ifndef QUIC_NETWORK_SIMULATOR_HELPER_H 2 | #define QUIC_NETWORK_SIMULATOR_HELPER_H 3 | 4 | #include "ns3/node.h" 5 | 6 | using namespace ns3; 7 | 8 | class QuicNetworkSimulatorHelper { 9 | public: 10 | QuicNetworkSimulatorHelper(); 11 | void Run(Time); 12 | Ptr GetLeftNode() const; 13 | Ptr GetRightNode() const; 14 | 15 | private: 16 | void RunSynchronizer() const; 17 | Ptr left_node_, right_node_; 18 | }; 19 | 20 | #endif /* QUIC_NETWORK_SIMULATOR_HELPER_H */ 21 | -------------------------------------------------------------------------------- /sim/scenarios/helper/quic-packet.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "quic-packet.h" 5 | 6 | #include "ns3/packet.h" 7 | #include "ns3/ppp-header.h" 8 | #include "ns3/ipv4-header.h" 9 | #include "ns3/ipv6-header.h" 10 | #include "ns3/udp-header.h" 11 | 12 | using namespace ns3; 13 | using namespace std; 14 | 15 | 16 | bool IsUDPPacket(Ptr p) { 17 | PppHeader ppp_hdr = PppHeader(); 18 | p->RemoveHeader(ppp_hdr); 19 | bool is_udp = false; 20 | switch(ppp_hdr.GetProtocol()) { 21 | case 0x21: // IPv4 22 | { 23 | Ipv4Header hdr = Ipv4Header(); 24 | p->PeekHeader(hdr); 25 | is_udp = hdr.GetProtocol() == 17; 26 | } 27 | break; 28 | case 0x57: // IPv6 29 | { 30 | Ipv6Header hdr = Ipv6Header(); 31 | p->PeekHeader(hdr); 32 | is_udp = hdr.GetNextHeader() == 17; 33 | } 34 | break; 35 | default: 36 | cout << "Unknown PPP protocol: " << ppp_hdr.GetProtocol() << endl; 37 | break; 38 | } 39 | p->AddHeader(ppp_hdr); 40 | return is_udp; 41 | } 42 | 43 | 44 | QuicPacket::QuicPacket(Ptr p) : p_(p) { 45 | const uint32_t p_len = p->GetSize(); 46 | uint8_t *buffer= new uint8_t[p_len]; 47 | p->CopyData(buffer, p_len); 48 | ppp_hdr_ = PppHeader(); 49 | uint32_t ppp_hdr_len = p->RemoveHeader(ppp_hdr_); 50 | // TODO: This currently only works for IPv4. 51 | ipv4_hdr_ = Ipv4Header(); 52 | uint32_t ip_hdr_len = p->RemoveHeader(ipv4_hdr_); 53 | udp_hdr_ = UdpHeader(); 54 | udp_hdr_len_ = p->RemoveHeader(udp_hdr_); 55 | total_hdr_len_ = ppp_hdr_len + ip_hdr_len + udp_hdr_len_; 56 | udp_payload_ = vector(&buffer[total_hdr_len_], &buffer[total_hdr_len_] + p_len - total_hdr_len_); 57 | } 58 | 59 | Ipv4Header& QuicPacket::GetIpv4Header() { return ipv4_hdr_; } 60 | 61 | UdpHeader& QuicPacket::GetUdpHeader() { return udp_hdr_; } 62 | 63 | vector& QuicPacket::GetUdpPayload() { return udp_payload_; } 64 | 65 | bool QuicPacket::IsVersionNegotiationPacket() { 66 | if(udp_payload_.size() <= 5) return false; 67 | return udp_payload_[1] == 0 && udp_payload_[2] == 0 && udp_payload_[3] == 0 && udp_payload_[4] == 0; 68 | } 69 | 70 | void QuicPacket::ReassemblePacket() { 71 | // Start with the UDP payload. 72 | Packet new_p = Packet(udp_payload_.data(), udp_payload_.size()); 73 | // Add the UDP header and make sure to recalculate the checksum. 74 | udp_hdr_.ForcePayloadSize(udp_payload_.size() + udp_hdr_len_); 75 | udp_hdr_.ForceChecksum(0); 76 | udp_hdr_.InitializeChecksum(ipv4_hdr_.GetSource(), ipv4_hdr_.GetDestination(), ipv4_hdr_.GetProtocol()); 77 | new_p.AddHeader(udp_hdr_); 78 | // Add the IP header, again make sure to recalculate the checksum. 79 | ipv4_hdr_.EnableChecksum(); 80 | new_p.AddHeader(ipv4_hdr_); 81 | // Add the PPP header. 82 | new_p.AddHeader(ppp_hdr_); 83 | p_->RemoveAtEnd(p_->GetSize()); 84 | p_->AddAtEnd(Ptr(&new_p)); 85 | } 86 | -------------------------------------------------------------------------------- /sim/scenarios/helper/quic-packet.h: -------------------------------------------------------------------------------- 1 | #ifndef QUIC_PACKET_H 2 | #define QUIC_PACKET_H 3 | 4 | #include 5 | 6 | #include "ns3/header.h" 7 | #include "ns3/packet.h" 8 | #include "ns3/ppp-header.h" 9 | #include "ns3/ipv4-header.h" 10 | #include "ns3/udp-header.h" 11 | 12 | using namespace ns3; 13 | using namespace std; 14 | 15 | bool IsUDPPacket(Ptr p); 16 | 17 | class QuicPacket { 18 | public: 19 | QuicPacket(Ptr p); 20 | // Assemble a new packet. 21 | // It uses the PPP, IP and UDP header that it parsed from the original packet, 22 | // and recalculates IP and UDP checksums. 23 | void ReassemblePacket(); 24 | Ipv4Header& GetIpv4Header(); 25 | UdpHeader& GetUdpHeader(); 26 | vector& GetUdpPayload(); 27 | bool IsVersionNegotiationPacket(); 28 | 29 | private: 30 | Ptr p_; 31 | PppHeader ppp_hdr_; 32 | Ipv4Header ipv4_hdr_; 33 | UdpHeader udp_hdr_; 34 | uint32_t udp_hdr_len_; 35 | uint32_t total_hdr_len_; 36 | vector udp_payload_; 37 | }; 38 | 39 | #endif /* QUIC_PACKET_H */ 40 | -------------------------------------------------------------------------------- /sim/scenarios/helper/quic-point-to-point-helper.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/traffic-control-helper.h" 3 | #include "ns3/ipv4-address-helper.h" 4 | #include "ns3/ipv6-address-helper.h" 5 | #include "quic-point-to-point-helper.h" 6 | 7 | using namespace ns3; 8 | 9 | QuicPointToPointHelper::QuicPointToPointHelper() : queue_size_(StringValue("100p")) { 10 | SetQueue("ns3::DropTailQueue", "MaxSize", StringValue("1p")); 11 | } 12 | 13 | void QuicPointToPointHelper::SetQueueSize(StringValue size) { 14 | queue_size_ = size; 15 | } 16 | 17 | NetDeviceContainer QuicPointToPointHelper::Install(Ptr a, Ptr b) { 18 | NetDeviceContainer devices = PointToPointHelper::Install(a, b); 19 | TrafficControlHelper tch; 20 | tch.SetRootQueueDisc("ns3::PfifoFastQueueDisc", "MaxSize", queue_size_); 21 | tch.Install(devices); 22 | 23 | Ipv4AddressHelper ipv4; 24 | ipv4.SetBase("193.167.50.0", "255.255.255.0"); 25 | ipv4.Assign(devices); 26 | 27 | Ipv6AddressHelper ipv6; 28 | ipv6.SetBase("fd00:cafe:cafe:50::", 64); 29 | ipv6.Assign(devices); 30 | 31 | return devices; 32 | } 33 | -------------------------------------------------------------------------------- /sim/scenarios/helper/quic-point-to-point-helper.h: -------------------------------------------------------------------------------- 1 | #ifndef QUIC_POINT_TO_POINT_HELPER_H 2 | #define QUIC_POINT_TO_POINT_HELPER_H 3 | 4 | #include "ns3/point-to-point-module.h" 5 | 6 | using namespace ns3; 7 | 8 | // The QuicPointToPointHelper acts like the ns3::PointToPointHelper, 9 | // but sets a ns3::DropTailQueue to one packet in order to minimize queueing latency. 10 | // Queues are simulated using a PfifoFastQueueDisc, with a default size of 100 packets. 11 | // The queue size can be set to a custom value using SetQueueSize(). 12 | class QuicPointToPointHelper : public PointToPointHelper { 13 | public: 14 | QuicPointToPointHelper(); 15 | 16 | // SetQueueSize sets the queue size for the PfifoFastQueueDisc 17 | void SetQueueSize(StringValue); 18 | NetDeviceContainer Install(Ptr a, Ptr b); 19 | private: 20 | StringValue queue_size_; // for the PfifoFastQueueDisc 21 | }; 22 | 23 | #endif /* QUIC_POINT_TO_POINT_HELPER_H */ 24 | -------------------------------------------------------------------------------- /sim/scenarios/rebind/README.md: -------------------------------------------------------------------------------- 1 | # Port and IP Address Rebinding 2 | 3 | This scenario uses a bottleneck link similar to the [simple-p2p](../simple-p2p) 4 | scenario and simulates a path that is frequently changing the source port (and 5 | optionally source IP address) of all client connections (similar to a NAT). This 6 | scenario can be used to test whether QUIC implementations correctly handle such 7 | rebindings. All flows are rebound every time the rebind timer expires. 8 | 9 | This scenario has the following configurable properties: 10 | 11 | * `--first-rebind`: The time of the first rebind event. This is a required 12 | parameter. For example, `--first-rebind=1s`. 13 | 14 | * `--rebind-freq`: The frequency for subsequent rebinds after the first event. 15 | This is an optional parameter. By default, only a single rebind is performed. 16 | For example, `--rebind-freq=3s`. 17 | 18 | * `--rebind-addr`: Whether IP addresses are also rebound. This is an optional 19 | parameter. By default, only client ports are rebound; `--rebind-addr` will 20 | rebind source IP addresses and source ports. 21 | 22 | For example, 23 | ```bash 24 | ./run.sh "rebind --delay=15ms --bandwidth=10Mbps --queue=25 --first-rebind=1s" 25 | ``` 26 | -------------------------------------------------------------------------------- /sim/scenarios/rebind/rebind-error-model.cc: -------------------------------------------------------------------------------- 1 | #include "rebind-error-model.h" 2 | #include "ns3/core-module.h" 3 | #include 4 | 5 | using namespace std; 6 | 7 | NS_OBJECT_ENSURE_REGISTERED(RebindErrorModel); 8 | 9 | TypeId RebindErrorModel::GetTypeId(void) { 10 | static TypeId tid = TypeId("RebindErrorModel") 11 | .SetParent() 12 | .AddConstructor(); 13 | return tid; 14 | } 15 | 16 | RebindErrorModel::RebindErrorModel() 17 | : client("193.167.0.100"), server("193.167.100.100"), nat(client), 18 | rebind_addr(false) { 19 | rng = CreateObject(); 20 | } 21 | 22 | void RebindErrorModel::DoReset() {} 23 | 24 | void RebindErrorModel::SetRebindAddr(bool ra) { rebind_addr = ra; } 25 | 26 | void RebindErrorModel::DoRebind() { 27 | const Ipv4Address old_nat = nat; 28 | if (rebind_addr) 29 | do { 30 | nat.Set((old_nat.Get() & 0xffffff00) | rng->GetInteger(1, 0xfe)); 31 | } while (nat == old_nat || nat == client); 32 | 33 | for (auto &b : fwd) { 34 | // FIXME: this will abort if we run out of ports (= after 64K rebinds) 35 | assert(rev.size() < UINT16_MAX - 1); 36 | const uint16_t old_port = b.second; 37 | do 38 | b.second = rng->GetInteger(1, UINT16_MAX); 39 | while (rev.find(b.second) != rev.end()); 40 | rev[b.second] = b.first; 41 | rev[old_port] = 0; 42 | cout << Simulator::Now().GetSeconds() << "s: " 43 | << " rebinding: " << old_nat << ":" << old_port << " -> " << nat << ":" 44 | << b.second << endl; 45 | } 46 | } 47 | 48 | bool RebindErrorModel::DoCorrupt(Ptr p) { 49 | if(!IsUDPPacket(p)) return false; 50 | 51 | QuicPacket qp = QuicPacket(p); 52 | 53 | const Ipv4Address &src_ip_in = qp.GetIpv4Header().GetSource(); 54 | const Ipv4Address &dst_ip_in = qp.GetIpv4Header().GetDestination(); 55 | const uint16_t src_port_in = qp.GetUdpHeader().GetSourcePort(); 56 | const uint16_t dst_port_in = qp.GetUdpHeader().GetDestinationPort(); 57 | 58 | if (src_ip_in == client) { 59 | if (fwd.find(src_port_in) == fwd.end()) 60 | rev[src_port_in] = fwd[src_port_in] = src_port_in; 61 | qp.GetUdpHeader().SetSourcePort(fwd[src_port_in]); 62 | qp.GetIpv4Header().SetSource(nat); 63 | 64 | } else if (src_ip_in == server) { 65 | if (rev[dst_port_in] == 0) { 66 | cout << Simulator::Now().GetSeconds() << "s: " 67 | << "unknown binding for destination " << dst_ip_in << ":" 68 | << dst_port_in << ", dropping packet" << endl; 69 | return true; 70 | } 71 | qp.GetIpv4Header().SetDestination(client); 72 | qp.GetUdpHeader().SetDestinationPort(rev[dst_port_in]); 73 | 74 | } else { 75 | cout << Simulator::Now().GetSeconds() << "s: " 76 | << "unknown source " << src_ip_in << ", dropping packet" << endl; 77 | return true; 78 | } 79 | 80 | qp.ReassemblePacket(); 81 | return false; 82 | } 83 | -------------------------------------------------------------------------------- /sim/scenarios/rebind/rebind-error-model.h: -------------------------------------------------------------------------------- 1 | #ifndef REBIND_ERROR_MODEL_H 2 | #define REBIND_ERROR_MODEL_H 3 | 4 | #include "../helper/quic-packet.h" 5 | #include "ns3/error-model.h" 6 | #include "ns3/random-variable-stream.h" 7 | #include 8 | 9 | using namespace ns3; 10 | 11 | class RebindErrorModel : public ErrorModel { 12 | public: 13 | static TypeId GetTypeId(void); 14 | RebindErrorModel(); 15 | void SetDrop(int packet_num); 16 | Ptr rng; 17 | void DoRebind(); 18 | void SetRebindAddr(bool ra); 19 | 20 | private: 21 | bool DoCorrupt(Ptr p); 22 | void DoReset(void); 23 | Ipv4Address client, server, nat; 24 | bool rebind_addr; 25 | 26 | unordered_map fwd, rev; 27 | }; 28 | 29 | #endif /* REBIND_ERROR_MODEL_H */ 30 | -------------------------------------------------------------------------------- /sim/scenarios/rebind/rebind.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/string.h" 2 | 3 | #include "../helper/quic-network-simulator-helper.h" 4 | #include "../helper/quic-point-to-point-helper.h" 5 | #include "ns3/core-module.h" 6 | #include "ns3/error-model.h" 7 | #include "ns3/internet-module.h" 8 | #include "ns3/point-to-point-module.h" 9 | #include "rebind-error-model.h" 10 | 11 | using namespace ns3; 12 | using namespace std; 13 | 14 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 15 | 16 | void rebind(Ptr em, const Time next) { 17 | em->DoRebind(); 18 | if (!next.IsZero()) 19 | Simulator::Schedule(next, &rebind, em, next); 20 | } 21 | 22 | int main(int argc, char *argv[]) { 23 | string delay, bandwidth, queue, first_rebind = "0s", rebind_freq = "0s"; 24 | bool rebind_addr = false; 25 | CommandLine cmd; 26 | cmd.AddValue("delay", "delay of the p2p link", delay); 27 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 28 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 29 | cmd.AddValue("first-rebind", "time of first rebind (e.g., 3s)", first_rebind); 30 | cmd.AddValue("rebind-freq", "rebind frequency after first rebind", 31 | rebind_freq); 32 | cmd.AddValue("rebind-addr", "change client IP address when rebinding", 33 | rebind_addr); 34 | cmd.Parse(argc, argv); 35 | 36 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 37 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 38 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 39 | NS_ABORT_MSG_IF(first_rebind.length() == 0, 40 | "Missing parameter: first_rebind"); 41 | NS_ABORT_MSG_IF(rebind_freq.length() > 0 && Time(first_rebind).IsZero(), 42 | "Illegal parameter value: rebind_freq is zero"); 43 | 44 | QuicNetworkSimulatorHelper sim; 45 | 46 | // Stick in the point-to-point line between the sides. 47 | QuicPointToPointHelper p2p; 48 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 49 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 50 | p2p.SetQueueSize(StringValue(queue + "p")); 51 | 52 | NetDeviceContainer devices = 53 | p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 54 | 55 | Ptr em = CreateObject(); 56 | em->SetRebindAddr(rebind_addr); 57 | em->Enable(); 58 | 59 | devices.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue(em)); 60 | devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em)); 61 | 62 | cout << Simulator::Now().GetSeconds() << "s: first rebind in " 63 | << first_rebind; 64 | if (rebind_freq.length() > 0) 65 | cout << ", frequency " << rebind_freq; 66 | cout << endl; 67 | 68 | Simulator::Schedule(Time(first_rebind), &rebind, em, Time(rebind_freq)); 69 | sim.Run(Seconds(36000)); 70 | } 71 | -------------------------------------------------------------------------------- /sim/scenarios/simple-p2p/README.md: -------------------------------------------------------------------------------- 1 | # Simple Point-to-Point Link 2 | 3 | This scenario builds a network with a simple configurable link, which is the 4 | bottleneck link in the network. You can use this scenario to test your 5 | implementation's performance under various bandwidth and delay 6 | conditions. Importantly, you can set the queue size at the bottleneck link. This 7 | enables a congestion controller to determine network bandwidth by witnessing 8 | either queueing delay increases or loss when the queue is full. 9 | 10 | This scenario can be used to compare your QUIC implementation's throughput when 11 | competing with external traffic sources. For example, you can run iperf 12 | alongside your client and server (within the same docker containers), to test 13 | your QUIC implementation's performance when competing with one or multiple flows 14 | of the host's TCP. NOTE: iperf sends traffic from the iperf client to the iperf 15 | server. 16 | 17 | This scenario has the following configurable properties: 18 | 19 | * `--delay`: One-way delay of network. Specify with units. This is a required 20 | parameter. For example `--delay=15ms`. 21 | 22 | * `--bandwidth`: Bandwidth of the link. Specify with units. This is a required 23 | parameter. For example `--bandwidth=10Mbps`. Specifying a value larger than 24 | 10Mbps may cause the simulator to saturate the CPU. 25 | 26 | * `--queue`: Queue size of the queue attached to the link. Specified in 27 | packets. This is a required parameter. For example `--queue=25`. 28 | 29 | For example, 30 | ```bash 31 | ./run.sh "simple-p2p --delay=15ms --bandwidth=10Mbps --queue=25" 32 | ``` 33 | -------------------------------------------------------------------------------- /sim/scenarios/simple-p2p/simple-p2p.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/internet-module.h" 3 | #include "ns3/point-to-point-module.h" 4 | #include "../helper/quic-network-simulator-helper.h" 5 | #include "../helper/quic-point-to-point-helper.h" 6 | 7 | using namespace ns3; 8 | 9 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 10 | 11 | int main(int argc, char *argv[]) { 12 | std::string delay, bandwidth, queue; 13 | CommandLine cmd; 14 | cmd.AddValue("delay", "delay of the p2p link", delay); 15 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 16 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 17 | cmd.Parse (argc, argv); 18 | 19 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 20 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 21 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 22 | 23 | QuicNetworkSimulatorHelper sim; 24 | 25 | // Stick in the point-to-point line between the sides. 26 | QuicPointToPointHelper p2p; 27 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 28 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 29 | p2p.SetQueueSize(StringValue(queue + "p")); 30 | 31 | NetDeviceContainer devices = p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 32 | 33 | sim.Run(Seconds(36000)); 34 | } 35 | -------------------------------------------------------------------------------- /sim/scenarios/tcp-cross-traffic/README.md: -------------------------------------------------------------------------------- 1 | # TCP Cross-Traffic 2 | 3 | This scenario builds a network with a simple configurable bottleneck link and 4 | generates TCP traffic running over this link from the server to the client. If 5 | there is no other traffic, this flow will saturate the link. Running your QUIC 6 | traffic through this scenario allows you to test how your congestion controller 7 | performs when competing against a TCP flow at the bottleneck. This scenario is 8 | most useful with large transfers, for comparing long-term bandwidth sharing and 9 | for looking into flow dynamics when competing. 10 | 11 | Note that this TCP is a native implementation within ns-3 and uses Reno 12 | congestion control. 13 | 14 | The simulator prints the bandwidth seen by the receiving TCP application over 15 | 5-second intervals. 16 | 17 | This scenario has the following configurable properties: 18 | 19 | * `--delay`: One-way delay of network. Specify with units. This is a required 20 | parameter. For example `--delay=15ms`. 21 | 22 | * `--bandwidth`: Bandwidth of the link. Specify with units. This is a required 23 | parameter. For example `--bandwidth=10Mbps`. Specifying a value larger than 24 | 10Mbps may cause the simulator to saturate the CPU. 25 | 26 | * `--queue`: Queue size of the queue attached to the link. Specified in 27 | packets. This is a required parameter. For example `--queue=25`. 28 | 29 | For example, 30 | ```bash 31 | ./run.sh "tcp-cross-traffic --delay=15ms --bandwidth=10Mbps --queue=25" 32 | ``` 33 | -------------------------------------------------------------------------------- /sim/scenarios/tcp-cross-traffic/tcp-cross-traffic.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/internet-module.h" 3 | #include "ns3/point-to-point-module.h" 4 | #include "ns3/applications-module.h" 5 | #include "../helper/quic-network-simulator-helper.h" 6 | #include "../helper/quic-point-to-point-helper.h" 7 | 8 | using namespace ns3; 9 | 10 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 11 | 12 | const Time print_interval = Seconds(5); 13 | 14 | void printBW(Ptr sink) { 15 | static uint64_t last_rec = 0; 16 | static Time last_time; 17 | 18 | uint64_t current = sink->GetTotalRx(); 19 | Time now = Simulator::Now(); 20 | 21 | std::cout << "Bandwidth: " << 8 * (current - last_rec) / ((now - last_time).GetSeconds() * 1000) << " Kbps" << std::endl; 22 | last_time = now; 23 | last_rec = current; 24 | Simulator::Schedule(print_interval, &printBW, sink); 25 | } 26 | 27 | int main(int argc, char *argv[]) { 28 | std::string delay, bandwidth, queue; 29 | CommandLine cmd; 30 | cmd.AddValue("delay", "delay of the p2p link", delay); 31 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 32 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 33 | cmd.Parse(argc, argv); 34 | 35 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 36 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 37 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 38 | 39 | QuicNetworkSimulatorHelper sim; 40 | 41 | QuicPointToPointHelper p2p; 42 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 43 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 44 | p2p.SetQueueSize(StringValue(queue + "p")); 45 | 46 | NetDeviceContainer devices = p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 47 | 48 | NodeContainer nodes; 49 | nodes.Create(2); 50 | InternetStackHelper internet; 51 | internet.Install(nodes); 52 | Ptr source_node = nodes.Get(1); 53 | Ptr sink_node = nodes.Get(0); 54 | 55 | QuicPointToPointHelper p2p_source; 56 | p2p_source.SetDeviceAttribute("DataRate", StringValue("100Mbps")); 57 | p2p_source.SetChannelAttribute("Delay", StringValue("1ms")); 58 | 59 | NetDeviceContainer devices_source = p2p_source.Install(sim.GetRightNode(), source_node); 60 | Ipv4AddressHelper ipv4_source; 61 | ipv4_source.SetBase("193.167.49.0", "255.255.255.0"); 62 | Ipv4InterfaceContainer interfaces_source = ipv4_source.Assign(devices_source); 63 | 64 | QuicPointToPointHelper p2p_sink; 65 | p2p_sink.SetDeviceAttribute("DataRate", StringValue("100Mbps")); 66 | p2p_sink.SetChannelAttribute("Delay", StringValue("1ms")); 67 | 68 | NetDeviceContainer devices_sink = p2p_sink.Install(sink_node, sim.GetLeftNode()); 69 | Ipv4AddressHelper ipv4_sink; 70 | ipv4_sink.SetBase("193.167.51.0", "255.255.255.0"); 71 | Ipv4InterfaceContainer interfaces_sink = ipv4_sink.Assign(devices_sink); 72 | 73 | uint16_t port = 9; // Discard port (RFC 863) 74 | Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue (1448)); 75 | // use a large receive buffer, so that we don't become flow control blocked 76 | Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue (10*1024*1024)); 77 | // use a very large send buffer, otherwise ns3 will create sub-MTU size packets 78 | Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue (100*1024*1024)); 79 | Config::SetDefault("ns3::TcpSocketBase::Sack", BooleanValue(true)); 80 | 81 | BulkSendHelper source("ns3::TcpSocketFactory", InetSocketAddress(interfaces_sink.GetAddress(0), port)); 82 | source.SetAttribute("MaxBytes", UintegerValue(0)); // unlimited 83 | ApplicationContainer source_apps = source.Install(source_node); 84 | source_apps.Start(Seconds(0)); 85 | 86 | PacketSinkHelper sink("ns3::TcpSocketFactory", InetSocketAddress(Ipv4Address::GetAny(), port)); 87 | ApplicationContainer apps = sink.Install(sink_node); 88 | apps.Start(Seconds(0)); 89 | 90 | p2p_source.EnablePcapAll("trace-source"); 91 | p2p_sink.EnablePcapAll("trace-sink"); 92 | 93 | Simulator::Schedule(print_interval, &printBW, apps.Get(0)->GetObject()); 94 | 95 | sim.Run(Seconds(36000)); 96 | } 97 | -------------------------------------------------------------------------------- /sim/scenarios/udp-cross-traffic/udp-cross-traffic.cc: -------------------------------------------------------------------------------- 1 | #include "ns3/core-module.h" 2 | #include "ns3/internet-module.h" 3 | #include "ns3/point-to-point-module.h" 4 | #include "ns3/applications-module.h" 5 | #include "../helper/quic-network-simulator-helper.h" 6 | #include "../helper/quic-point-to-point-helper.h" 7 | 8 | using namespace ns3; 9 | 10 | NS_LOG_COMPONENT_DEFINE("ns3 simulator"); 11 | 12 | int main(int argc, char *argv[]) { 13 | std::string delay, bandwidth, queue, cross_data_rate; 14 | CommandLine cmd; 15 | cmd.AddValue("delay", "delay of the p2p link", delay); 16 | cmd.AddValue("bandwidth", "bandwidth of the p2p link", bandwidth); 17 | cmd.AddValue("queue", "queue size of the p2p link (in packets)", queue); 18 | cmd.AddValue("crossdatarate", "data rate of the cross traffic", cross_data_rate); 19 | cmd.Parse (argc, argv); 20 | 21 | NS_ABORT_MSG_IF(delay.length() == 0, "Missing parameter: delay"); 22 | NS_ABORT_MSG_IF(bandwidth.length() == 0, "Missing parameter: bandwidth"); 23 | NS_ABORT_MSG_IF(queue.length() == 0, "Missing parameter: queue"); 24 | NS_ABORT_MSG_IF(cross_data_rate.length() == 0, "Missing parameter: crossdatarate"); 25 | 26 | QuicNetworkSimulatorHelper sim; 27 | 28 | QuicPointToPointHelper p2p; 29 | p2p.SetDeviceAttribute("DataRate", StringValue(bandwidth)); 30 | p2p.SetChannelAttribute("Delay", StringValue(delay)); 31 | p2p.SetQueueSize(StringValue(queue + "p")); 32 | 33 | NetDeviceContainer devices = p2p.Install(sim.GetLeftNode(), sim.GetRightNode()); 34 | Ipv4AddressHelper ipv4; 35 | ipv4.SetBase("10.50.0.0", "255.255.0.0"); 36 | Ipv4InterfaceContainer interfaces = ipv4.Assign(devices); 37 | 38 | uint16_t port = 9; // Discard port (RFC 863) 39 | // Create a sink to receive the packets on the left node. 40 | PacketSinkHelper sink("ns3::UdpSocketFactory", InetSocketAddress(Ipv4Address::GetAny(), port)); 41 | sink.Install(sim.GetLeftNode()).Start(Seconds(20)); 42 | 43 | // Create a UDP packet source on the right node. 44 | OnOffHelper onoff("ns3::UdpSocketFactory", InetSocketAddress(interfaces.GetAddress(0), port)); 45 | onoff.SetConstantRate(DataRate(cross_data_rate)); 46 | onoff.Install(sim.GetRightNode()).Start(Seconds(20)); 47 | 48 | sim.Run(Seconds(36000)); 49 | } 50 | -------------------------------------------------------------------------------- /sim/wait-for-it-quic/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/marten-seemann/quic-network-simulator/sim/wait-for-it-quic 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /sim/wait-for-it-quic/wait-for-it.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net" 9 | "os" 10 | "time" 11 | ) 12 | 13 | // compose a packet that will elicit a Version Negotiation packet 14 | var packet = append([]byte{0xc0, 0x57, 0x41, 0x49, 0x54, 0, 0}, make([]byte, 1200)...) 15 | 16 | func main() { 17 | startTime := time.Now() 18 | var timeout time.Duration 19 | flag.DurationVar(&timeout, "t", 0, "timeout (e.g. 10s)") 20 | flag.Parse() 21 | host := flag.Arg(0) 22 | if flag.NArg() < 1 { 23 | flag.Usage() 24 | return 25 | } 26 | if timeout == 0 { 27 | fmt.Printf("waiting for %s without a timeout\n", host) 28 | } else { 29 | fmt.Printf("waiting %s for %s\n", timeout, host) 30 | } 31 | addr, err := net.ResolveUDPAddr("udp", host) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | ok := waitForIt(addr, timeout) 36 | dur := time.Since(startTime) 37 | if !ok { 38 | fmt.Printf("timeout occurred after waiting %s for %s\n", dur, host) 39 | os.Exit(1) 40 | } 41 | fmt.Printf("%s is available after %s\n", host, dur) 42 | } 43 | 44 | func waitForIt(addr *net.UDPAddr, timeout time.Duration) bool { 45 | conn, err := net.ListenUDP("udp", nil) 46 | if err != nil { 47 | log.Fatal("ListenUDP failed: %s", err) 48 | } 49 | done := make(chan struct{}) 50 | go func() { 51 | b := make([]byte, 1500) 52 | n, err := conn.Read(b) 53 | if err != nil { 54 | log.Fatalf("Read failed: %s", err) 55 | return 56 | } 57 | b = b[:n] 58 | if len(b) < 5 { 59 | log.Fatal("invalid packet") 60 | } 61 | if !bytes.Equal(b[1:5], []byte{0, 0, 0, 0}) { 62 | log.Fatal("expected Version Negotiation packet") 63 | } 64 | close(done) 65 | }() 66 | 67 | ticker := time.NewTicker(time.Second / 2) 68 | var timerChan <-chan time.Time 69 | if timeout > 0 { 70 | timer := time.NewTimer(timeout) 71 | defer timer.Stop() 72 | timerChan = timer.C 73 | } 74 | for { 75 | if _, err := conn.WriteTo(packet, addr); err != nil { 76 | log.Fatalf("Write failed: %s", err) 77 | return false 78 | } 79 | select { 80 | case <-ticker.C: 81 | case <-timerChan: 82 | return false 83 | case <-done: 84 | return true 85 | } 86 | } 87 | } 88 | --------------------------------------------------------------------------------