Use IPv6 as an IPv4 Bridge to Revive Legacy TCP Games
Nginx delivers high performance but demands careful configuration. Many legacy games cannot support online multiplayer without a public IPv4 address, and using forwarding services from cloud providers adds cost and performance overhead due to NAT translation. Direct public IPv4 access is often blocked by carrier-grade NAT, making this approach unfeasible.
To address this, we can build an IPv4-IPv6-IPv4 TCP relay, using IPv6 as a bridge between two IPv4 endpoints. Below are two Python scripts to implement this relay for TCP traffic, which works for retro games such as Minecraft 1.7.2 servers.
Relay Server Script
This script runs on a machine with public IPv6 access, listening for IPv6 connections and forwarding traffic to a local IPv4 game server.
import socket
import select
import threading
def handle_client_session(client_sock):
print("New client connection established")
try:
with socket.create_connection((REMOTE_HOST, REMOTE_PORT)) as remote_sock:
monitored_socks = [client_sock, remote_sock]
while monitored_socks:
ready_socks, _, _ = select.select(monitored_socks, [], [])
for sock in ready_socks:
data = sock.recv(4096)
if sock == client_sock:
if data:
remote_sock.sendall(data)
else:
monitored_socks.remove(sock)
remote_sock.close()
break
else:
if data:
client_sock.sendall(data)
else:
monitored_socks.remove(sock)
client_sock.close()
break
finally:
client_sock.close()
def run_relay_server():
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as server_sock:
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_sock.bind((LISTEN_ADDR, LISTEN_PORT))
server_sock.listen(5)
print(f"Relay server listening on [{LISTEN_ADDR}]:{LISTEN_PORT}")
while True:
client_conn, client_addr = server_sock.accept()
print(f"Accepted connection from {client_addr}")
worker_thread = threading.Thread(target=handle_client_session, args=(client_conn,))
worker_thread.start()
if __name__ == "__main__":
# Configure listening address (use :: to bind all IPv6 interfaces)
LISTEN_ADDR = "::"
# Port for the relay server to accept client connections
LISTEN_PORT = 43325
# Local IPv4 address of the legacy game server
REMOTE_HOST = "127.0.0.1"
# Port of the local legacy game server
REMOTE_PORT = 64871
run_relay_server()
Relay Client Script
This script runs on the client machine, listening for local game client connections and forwarding traffic to the remote IPv6 relay server.
import socket
import select
import threading
def handle_client_session(client_sock):
print("New local client connection established")
try:
with socket.create_connection((RELAY_SERVER_ADDR, RELAY_SERVER_PORT)) as relay_sock:
monitored_socks = [client_sock, relay_sock]
while monitored_socks:
ready_socks, _, _ = select.select(monitored_socks, [], [])
for sock in ready_socks:
data = sock.recv(4096)
if sock == client_sock:
if data:
relay_sock.sendall(data)
else:
monitored_socks.remove(sock)
relay_sock.close()
break
else:
if data:
client_sock.sendall(data)
else:
monitored_socks.remove(sock)
client_sock.close()
break
finally:
client_sock.close()
def run_local_proxy():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as proxy_sock:
proxy_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
proxy_sock.bind((LOCAL_LISTEN_ADDR, LOCAL_LISTEN_PORT))
proxy_sock.listen(5)
print(f"Local proxy listening on {LOCAL_LISTEN_ADDR}:{LOCAL_LISTEN_PORT}")
while True:
client_conn, client_addr = proxy_sock.accept()
print(f"Accepted local connection from {client_addr}")
worker_thread = threading.Thread(target=handle_client_session, args=(client_conn,))
worker_thread.start()
if __name__ == "__main__":
# Local listening address for the game client to connect to
LOCAL_LISTEN_ADDR = "127.0.0.1"
# Local port for the game client to use when connecting
LOCAL_LISTEN_PORT = 46666
# IPv6 address of the remote relay server
RELAY_SERVER_ADDR = "2001:db8::1234"
# Port of the remote relay server
RELAY_SERVER_PORT = 43325
run_local_proxy()
Configuration Notes
- On the relay server: Udpate
REMOTE_HOSTandREMOTE_PORTto match your local game server's address and port, then adjustLISTEN_PORTif needed. - On the client machine: Replace
RELAY_SERVER_ADDRwith the public IPv6 address of the relay server, and setLOCAL_LISTEN_PORTto the port your game client will connect to. - Launch the relay server first, then start the local proxy on the client machine, and configure your game client to connect to
127.0.0.1:[LOCAL_LISTEN_PORT].
While the core logic was assisted by an AI, refining the code to handle edge cases and improve readability required additional work. Future updates will focus on simplifying configuration and adding support for additional protocols.