3.5 KiB
Hole punch tests
How to run locally
npm install
make
npm run test
Client configuration
env variable | possible values |
---|---|
MODE | listen | dial |
TRANSPORT | tcp | quic |
- For TCP, the client MUST use noise + yamux to upgrade the connection.
- The relayed connection MUST use noise + yamux.
Test flow
- The relay starts and pushes its address to the following redis keys:
RELAY_TCP_ADDRESS
for the TCP testRELAY_QUIC_ADDRESS
for the QUIC test
- Upon start-up, clients connect to a redis server at
redis:6379
and block until this redis key comes available. They then dial the relay on the provided address. - The relay supports identify. Implementations SHOULD use that to figure out their external address next.
- Once connected to the relay, a client in
MODE=listen
should listen on the relay and make a reservation. Once the reservation is made, it pushes itsPeerId
to the redis keyLISTEN_CLIENT_PEER_ID
. - A client in
MODE=dial
blocks on the availability ofLISTEN_CLIENT_PEER_ID
. Once available, it dials<relay_addr>/p2p-circuit/<listen-client-peer-id>
. - Upon a successful hole-punch, the peer in
MODE=dial
measures the RTT across the newly established connection. - The RTT MUST be printed to stdout in the following format:
{ "rtt_to_holepunched_peer_millis": 12 }
- Once printed, the dialer MUST exit with
0
.
Requirements for implementations
- Docker containers MUST have a binary called
hole-punch-client
in their $PATH - MUST have
dig
,curl
,jq
andtcpdump
installed - Listener MUST NOT early-exit but wait to be killed by test runner
- Logs MUST go to stderr, RTT json MUST go to stdout
- Dialer and lister both MUST use 0RTT negotiation for protocols
- Implementations SHOULD disable timeouts on the redis client, i.e. use
0
- Implementations SHOULD exit early with a non-zero exit code if anything goes wrong
- Implementations MUST set
TCP_NODELAY
for the TCP transport - Implements MUST make sure connections are being kept alive
Design notes
The design of this test runner is heavily influenced by multidim-interop but differs in several ways.
All files related to test runs will be written to the ./runs directory.
This includes the docker-compose.yml
files of each individual run as well as logs and tcpdump
's for the dialer and listener.
The docker-compose file uses 6 containers in total:
- 1 redis container for orchestrating the test
- 1 relay
- 1 hole-punch client in
MODE=dial
- 1 hole-punch client in
MODE=listen
- 2 routers: 1 per client
The networks are allocated by docker-compose. We dynamically fetch the IPs and subnets as part of a startup script to set the correct IP routes.
In total, we have three networks:
lan_dialer
lan_listener
internet
The two LANs host a router and a client each whereas the relay is connected (without a router) to the internet
network.
On startup of the clients, we add an ip route
that redirects all traffic to the corresponding router
container.
The router container masquerades all traffic upon forwarding, see the README for details.
Running a single test
- Build all containers using
make
- Generate all test definitions using
npm run test -- --no-run
- Pick the desired test from the runs directory
- Execute it using
docker compose up