Compare commits

..
2 Commits
Author SHA1 Message Date
rmanach 5edc20539d switch jaymod pk3 url to et.sonak.fr 2026-09-26 14:53:52 +02:00
rmanach 6b9d14f86b add etrelay UDP relay + etmasters script + update README 2026-09-26 14:53:52 +02:00
10 changed files with 33 additions and 36 deletions
+9 -9
View File
@@ -34,15 +34,15 @@ Masters are independent: a server is only listed on the masters it heartbeats, a
What it looks like in production (`--debug`):
```
heartbeat EnemyTerritory-1 -> etmaster.net:27950 (198.51.100.10:27950)
session + 198.51.100.10:27950 (2 active)
198.51.100.10:27950 -> ET getChallenge (27 B)
198.51.100.10:27950 <- ET challengeResponse (32 B)
198.51.100.10:27950 -> ET getInfo (22 B)
198.51.100.10:27950 <- ET infoResponse (303 B)
session + 203.0.113.7:45488 (3 active)
203.0.113.7:45488 -> ET getstatus (14 B)
203.0.113.7:45488 <- ET statusResponse (993 B)
heartbeat EnemyTerritory-1 -> etmaster.net:27950 (91.220.53.121:27950)
session + 91.220.53.121:27950 (2 active)
91.220.53.121:27950 -> ET getChallenge (27 B)
91.220.53.121:27950 <- ET challengeResponse (32 B)
91.220.53.121:27950 -> ET getInfo (22 B)
91.220.53.121:27950 <- ET infoResponse (303 B)
session + 138.201.36.184:45488 (3 active)
138.201.36.184:45488 -> ET getstatus (14 B)
138.201.36.184:45488 <- ET statusResponse (993 B)
```
The last two lines are a server tracker that got the address from the master.
+3 -6
View File
@@ -10,10 +10,9 @@ DIST_DIR := $(ROOT_DIR)dist
DYNAMIC_BIN := $(DIST_DIR)/$(NAME)-v$(VERSION)-$(GNU)
STATIC_BIN := $(DIST_DIR)/$(NAME)-v$(VERSION)-$(MUSL)
# Deployment target (front server SSH host) and ET server behind the tunnel (ip:port),
# e.g. make deploy HOST=front UPSTREAM=10.0.0.2:27960
HOST ?=
UPSTREAM ?=
# Deployment target (front server) and ET server behind the tunnel.
HOST :=
UPSTREAM :=
# Extra etrelay flags, e.g. make deploy ARGS=--debug
ARGS ?=
@@ -44,7 +43,6 @@ static: check
release: build static
deploy: static
@test -n "$(HOST)" -a -n "$(UPSTREAM)" || (echo "usage: make deploy HOST=<ssh host> UPSTREAM=<ip:port>" && exit 1)
scp $(STATIC_BIN) $(HOST):/tmp/$(NAME)
sed -e 's#@UPSTREAM@#$(UPSTREAM)#' -e 's#@ARGS@#$(ARGS)#' $(NAME).service | ssh $(HOST) 'cat > /etc/systemd/system/$(NAME).service'
ssh $(HOST) 'install -m 755 /tmp/$(NAME) /usr/local/bin/$(NAME) && rm /tmp/$(NAME) \
@@ -52,7 +50,6 @@ deploy: static
&& systemctl --no-pager status $(NAME)'
logs:
@test -n "$(HOST)" || (echo "usage: make logs HOST=<ssh host>" && exit 1)
ssh $(HOST) 'journalctl -u $(NAME) -n 50 --no-pager'
# PlantUML sources (diagrams/*.puml) -> PNG next to them, used by ENGINE.md.
+13 -13
View File
@@ -3,7 +3,7 @@
UDP relay + master heartbeat for the Wolfenstein: Enemy Territory server, running on the front server (replaces its UDP 27960 DNAT rule / nginx `stream` block).
```
players / masters ──> front :27960 (etrelay) ══wg══> <wg-peer>:27960 (nginx stream) ──> ET <et-server>:27960
players / masters ──> front :27960 (etrelay) ══wg══> 10.66.0.10:27960 (nginx stream) ──> ET 10.0.1.103:27960
```
- **Proxy**: one upstream socket per client address, packets forwarded as-is both ways, session dropped after 2 min of silence.
@@ -24,18 +24,18 @@ Default masters: `etmaster.idsoftware.com:27950` (id Software, the one ET 2.60b
`--debug` logs sessions (open/close, packet counts), connectionless packets by command name only (`getinfo`, `getstatus`, `connect`...: no arguments, `connect` carries the userinfo), probes and heartbeats. In-game packets are only counted.
```
probe <wg-peer>:27960: up
ET server <wg-peer>:27960 is up
heartbeat EnemyTerritory-1 -> etmaster.net:27950 (198.51.100.10:27950)
session + 198.51.100.10:27950 (2 active)
198.51.100.10:27950 -> ET getChallenge (27 B)
198.51.100.10:27950 <- ET challengeResponse (32 B)
198.51.100.10:27950 -> ET getInfo (22 B)
198.51.100.10:27950 <- ET infoResponse (303 B)
probe 10.66.0.10:27960: up
ET server 10.66.0.10:27960 is up
heartbeat EnemyTerritory-1 -> etmaster.net:27950 (91.220.53.121:27950)
session + 91.220.53.121:27950 (2 active)
91.220.53.121:27950 -> ET getChallenge (27 B)
91.220.53.121:27950 <- ET challengeResponse (32 B)
91.220.53.121:27950 -> ET getInfo (22 B)
91.220.53.121:27950 <- ET infoResponse (303 B)
session - <player>:27960: 5321 packets to ET, 4876 from ET
```
Deploy with it: `make deploy HOST=<front> UPSTREAM=<wg-peer>:27960 ARGS=--debug`, back to normal without `ARGS`.
Deploy with it: `make deploy ARGS=--debug`, back to normal with `make deploy`.
## Build
@@ -54,7 +54,7 @@ dist/etrelay-v0.1.0-x86_64-unknown-linux-musl # static
## Deploy
1. Relay (static binary + systemd unit on the front, `HOST` is its SSH host): `make deploy HOST=<front> UPSTREAM=<wg-peer>:27960`
1. Relay (static binary + systemd unit on `ionos`): `make deploy`
2. Front: remove what captured UDP 27960 before etrelay, or players bypass it:
- the DNAT rule (`PostUp`/`PostDown` in `/etc/wireguard/wg0.conf`, then `wg-quick down wg0 && wg-quick up wg0`); `iptables -t nat -S | grep 27960` must print nothing
- the nginx `stream` block (`proxy/nginx/nginx.conf` in the `pve` repo): `make -C proxy deploy-nginx` from there
@@ -70,8 +70,8 @@ dist/etrelay-v0.1.0-x86_64-unknown-linux-musl # static
## Check
```bash
make logs HOST=<front> # "ET server ... is up"
ssh <front> tcpdump -ni any udp port 27950 # heartbeat out, getChallenge/getInfo in, answers out
make logs # "ET server ... is up"
ssh ionos tcpdump -ni any udp port 27950 # heartbeat out, getChallenge/getInfo in, answers out
```
The server then shows up on the master (in-game server browser).
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

+3 -3
View File
@@ -5,13 +5,13 @@ title After: everything goes through the front
actor Player as player
node "Master\netmaster.net:27950" as master
node "Front (public IP)" {
node "Front (74.208.250.147)" {
component "etrelay\n0.0.0.0:27960" as relay
}
node "Home" {
component "nginx stream\n<wg-peer>:27960" as nginx
component "ET server\n<et-server>:27960" as et
component "nginx stream\n10.66.0.10:27960" as nginx
component "ET server\n10.0.1.103:27960" as et
}
player <--> relay : game traffic
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

+3 -3
View File
@@ -3,9 +3,9 @@ skinparam shadowing false
title Before: heartbeat leaves through the home box
node "Master\netmaster.net:27950" as master
node "Home box\nhome IP" as box
node "Front\npublic IP" as front
node "ET server\n<et-server>:27960" as et
node "Home box\n86.x.x.x" as box
node "Front\n74.208.250.147" as front
node "ET server\n10.0.1.103:27960" as et
et --> box : heartbeat
box --> master : heartbeat (source = home IP)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

+1 -1
View File
@@ -24,5 +24,5 @@ master -> relay : getInfo
relay -> et : getInfo
et --> relay : infoResponse (hostname, map, players)
relay --> master : infoResponse\nsource = front:27960
note left of master : lists <front-ip>:27960
note left of master : lists 74.208.250.147:27960
@enduml
+1 -1
View File
@@ -45,7 +45,7 @@ macro_rules! debug {
#[derive(Parser)]
#[command(version, name = "etrelay")]
struct Cli {
/// ET server address, e.g. 10.0.0.2:27960
/// ET server address, e.g. 10.66.0.10:27960
upstream: SocketAddr,
/// Public address to listen on