4.5 KiB
UDPGW (NapsterV framing) — udpgw.go
A lightweight TCP↔UDP gateway that speaks the “NapsterV” UDPGW framing:
- Listens on TCP (default
0.0.0.0:7400). - For each TCP client, opens one UDP socket (stable source port) and forwards framed payloads to UDP targets.
- Tracks a short-lived mapping so UDP replies are routed back to the correct
connID+Xbyte. - IPv4 only (UDP replies are dropped if they’re not IPv4).
Build
Single-file build:
# Linux static-ish build (recommended)
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o udpgw ./udpgw.go
Run help:
./udpgw -h
Minimal RAM/CPU start command (recommended profile)
This profile is tuned to minimize per-client kernel buffer memory and reduce background CPU (slower map reaping), while keeping behavior safe for most traffic:
GOMAXPROCS=1 ./udpgw -listen 0.0.0.0:7400 -debug=false -write-chan 256 -udp-rbuf $((256*1024)) -udp-wbuf $((256*1024)) -map-ttl 60s -reap-every 30s
Notes:
GOMAXPROCS=1limits Go’s scheduler to one OS thread for CPU predictability.-udp-rbuf/-udp-wbufare the biggest memory knobs because they are applied per TCP client UDP socket.- If you see packet loss under load, raise
-udp-rbuf/-udp-wbufto524288(512 KiB) or1048576(1 MiB) first.
How framing works
Each TCP frame is:
LEN(2 bytes, little-endian)PAYLOAD(LENbytes)
PAYLOAD format for both directions:
connID(2 bytes, big-endian)X(1 byte)IPv4(4 bytes)port(2 bytes, big-endian)data(remaining bytes)
(See readPayload() and buildFrame() in the source.)
Configuration flags
All runtime knobs are flags (defaults shown):
-
-listen 0.0.0.0:7400
TCP listen address. -
-max-frame 65536
Max frame payload length (DoS protection). Frames larger than this are rejected. -
-debug=false
Verbose logs. -
-hexdump 64
When-debug=true, hex-dump the first N bytes of payload. -
-write-chan 4096
TCP write queue depth. Larger can help bursty traffic (e.g., games) but increases worst-case RAM. -
-udp-bind ""
Bind the per-client UDP socket to a specific local IP (optional). -
-udp-rbuf 8388608
UDP read buffer bytes (socket option). Applied per TCP client. -
-udp-wbuf 8388608
UDP write buffer bytes (socket option). Applied per TCP client. -
-map-ttl 90s
Destination→connIDmapping TTL. -
-reap-every 10s
How often to delete expired mappings.
Tuning guide (what affects RAM/CPU the most)
RAM hotspots
-
Kernel UDP buffers per TCP client
Roughly proportional to:udp-rbuf + udp-wbufper connected client.
Defaults are high (8 MiB + 8 MiB). If you expect many concurrent TCP clients, reduce these. -
TCP write queue (
-write-chan)
Each queued element is a[]byteframe. Bigger-write-chanallows more buffering but can grow memory. -
Per-client TCP reader buffer (fixed)
The code usesbufio.NewReaderSize(conn, 256*1024)per client (256 KiB). This is not configurable via flags.
CPU hotspots
-
-debug=true(logging)
Turn off for production. -
Reaper frequency (
-reap-every)
A longer interval reduces wakeups/CPU at the cost of keeping expired mappings a bit longer.
Recommended profiles
1) Ultra-low memory (many clients, light traffic)
GOMAXPROCS=1 ./udpgw -listen 0.0.0.0:7400 -write-chan 128 -udp-rbuf $((128*1024)) -udp-wbuf $((128*1024)) -map-ttl 45s -reap-every 45s
2) Balanced (good default for most servers)
./udpgw -listen 0.0.0.0:7400 -write-chan 512 -udp-rbuf $((1024*1024)) -udp-wbuf $((1024*1024)) -map-ttl 90s -reap-every 15s
3) Low-latency / games (avoid drops under bursts)
./udpgw -listen 0.0.0.0:7400 -write-chan 4096 -udp-rbuf $((4*1024*1024)) -udp-wbuf $((4*1024*1024)) -map-ttl 120s -reap-every 10s
Operational notes
- Run behind a firewall; expose only the TCP listen port you need.
- If you bind
-udp-bind, ensure that IP is present on the host. - The program will drop UDP replies that are not IPv4.
systemd (optional)
Example unit (adjust paths/flags):
[Unit]
Description=udpgw (NapsterV)
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/udpgw
Environment=GOMAXPROCS=1
ExecStart=/opt/udpgw/udpgw -listen 0.0.0.0:7400 -write-chan 256 -udp-rbuf 262144 -udp-wbuf 262144 -map-ttl 60s -reap-every 30s
Restart=on-failure
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target