
UDP Relay
Muti Metroo supports tunneling UDP traffic through the mesh network using SOCKS5 UDP ASSOCIATE (RFC 1928).
Overview
UDP relay enables connectionless UDP traffic (DNS, NTP, game protocols) to be tunneled through the encrypted mesh network. Traffic flows from a SOCKS5 client through the ingress agent, across transit nodes, and out through an exit node.
All UDP datagrams are encrypted end-to-end between ingress and exit using ChaCha20-Poly1305.
Requirements
UDP relay requires:
- Ingress agent with SOCKS5 enabled
- Exit agent with UDP relay enabled
- A route from ingress to exit
Configuration
Exit Node
Enable UDP relay on exit nodes:
udp:
enabled: true
allowed_ports:
- "53" # DNS
- "123" # NTP
max_associations: 1000
idle_timeout: 5m
max_datagram_size: 1472
See Configuration - UDP for full reference.
Ingress Node
No special configuration needed. UDP ASSOCIATE is automatically available when SOCKS5 is enabled and an exit with UDP support is reachable.
socks5:
enabled: true
address: "127.0.0.1:1080"
The UDP relay socket binds to the same IP address as the SOCKS5 TCP listener. If SOCKS5 listens on 127.0.0.1:1080, UDP relay sockets will bind to 127.0.0.1. If SOCKS5 listens on 0.0.0.0:1080, UDP relay sockets will bind to 0.0.0.0 (all interfaces).
Usage
DNS Queries
Use standard DNS tools through the SOCKS5 proxy:
# Using proxychains
proxychains4 dig @8.8.8.8 example.com
# Using socksify
socksify dig @8.8.8.8 example.com
Python Example
import socket
import struct
# Connect to SOCKS5 proxy
proxy = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
proxy.connect(('127.0.0.1', 1080))
# SOCKS5 greeting (no auth)
proxy.send(b'\x05\x01\x00')
proxy.recv(2) # Method selection
# UDP ASSOCIATE request
proxy.send(b'\x05\x03\x00\x01\x00\x00\x00\x00\x00\x00')
reply = proxy.recv(10)
# Parse relay address
relay_ip = socket.inet_ntoa(reply[4:8])
relay_port = struct.unpack('>H', reply[8:10])[0]
# Create UDP socket
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Build SOCKS5 UDP header + DNS query
# RSV(2) + FRAG(1) + ATYP(1) + ADDR + PORT(2) + DATA
header = b'\x00\x00\x00\x01' # RSV, FRAG=0, ATYP=IPv4
header += socket.inet_aton('8.8.8.8') # Destination IP
header += struct.pack('>H', 53) # Destination port
header += dns_query # Your DNS query bytes
# Send through relay
udp.sendto(header, (relay_ip, relay_port))
# Receive response
response, _ = udp.recvfrom(4096)
dns_response = response[10:] # Skip SOCKS5 header
Port Whitelist
For security, exit nodes use a port whitelist:
udp:
allowed_ports:
- "53" # DNS only
- "123" # NTP
Special values:
| Value | Description |
|---|---|
"53" | Specific port |
"*" | All ports (dangerous - see warning below) |
[] | No ports allowed (UDP disabled) |
Never use ["*"] (all ports) in production environments. This allows:
- Arbitrary UDP tunneling: Attackers can tunnel any UDP-based protocol through your exit node
- Amplification attacks: Your node can be used for UDP-based DDoS amplification (DNS, NTP, memcached)
- Abuse exposure: Your exit IP becomes liable for malicious traffic
- Data exfiltration: Unrestricted UDP enables covert data channels
Always use an explicit port whitelist limited to protocols you actually need (e.g., DNS on 53, NTP on 123).
Limitations
| Limitation | Value | Description |
|---|---|---|
| Max datagram size | 1472 bytes | MTU minus IP/UDP headers |
| Fragmentation | Not supported | Fragmented datagrams are rejected |
| Association lifetime | Tied to TCP | Ends when TCP control connection closes |
No Fragmentation Support
UDP datagrams with FRAG > 0 are rejected. Most applications do not use SOCKS5-level fragmentation. If you encounter issues, check if your application is fragmenting at the SOCKS5 layer.
End-to-End Encryption
UDP traffic is encrypted between ingress and exit:
- Ingress generates ephemeral X25519 keypair
- Exit generates ephemeral X25519 keypair
- Both derive shared secret via ECDH
- Each datagram encrypted with ChaCha20-Poly1305
Transit nodes cannot decrypt UDP payloads.
Troubleshooting
UDP ASSOCIATE Fails
Error: UDP relay is disabled
- Verify exit node has
udp.enabled: true - Check that a route exists to the exit node
- Ensure exit node is connected to the mesh
Port Not Allowed
Error: port 5000 not allowed
- Add port to
udp.allowed_portson exit node - Or use
["*"]to allow all ports (testing only)
Datagram Too Large
Error: datagram too large
- Reduce payload size to under 1472 bytes
- Check
udp.max_datagram_sizeconfiguration
Association Timeout
UDP associations expire after idle_timeout (default 5 minutes) of inactivity. Send keepalive datagrams if needed.
Security Considerations
- Port whitelist: Only allow necessary ports (53, 123)
- Avoid wildcard: Don't use
["*"]in production - Authentication: Use SOCKS5 authentication to control access
- Monitor associations: Check
max_associationslimit
Related
- Configuration - UDP - Full configuration reference
- Features - SOCKS5 Proxy - SOCKS5 ingress setup
- Features - Exit Routing - Route configuration
- Configuration - Exit - Exit node setup