ams::mitm::p2p::P2pProxyServer
ams::mitm::p2p::P2pProxyServer
Section titled “ams::mitm::p2p::P2pProxyServer”P2P Proxy Server.
TCP server that hosts direct P2P connections for LDN multiplayer. When a Switch creates a network (hosts), this server accepts connections from other players who join via P2P instead of through the relay server.Thread SafetyAll public methods are thread-safe. Internal state is protected by mutex. serverStart(port) - begin listening- open UPnP port (optional but recommended)Accept connections, validate tokensRoute proxy messages between sessions- cleanup and close
Members
Section titled “Members”PRIVATE_PORT_BASE
Section titled “PRIVATE_PORT_BASE”Type: constexpr uint16_t
PRIVATE_PORT_RANGE
Section titled “PRIVATE_PORT_RANGE”Type: constexpr int
PUBLIC_PORT_BASE
Section titled “PUBLIC_PORT_BASE”Type: constexpr uint16_t
PUBLIC_PORT_RANGE
Section titled “PUBLIC_PORT_RANGE”Type: constexpr int
PORT_LEASE_LENGTH
Section titled “PORT_LEASE_LENGTH”Type: constexpr int
PORT_LEASE_RENEW
Section titled “PORT_LEASE_RENEW”Type: constexpr int
AUTH_WAIT_SECONDS
Section titled “AUTH_WAIT_SECONDS”Type: constexpr int
MAX_PLAYERS
Section titled “MAX_PLAYERS”Type: constexpr int
Members
Section titled “Members”m_mutex
Section titled “m_mutex”Type: os::Mutex
m_listen_fd
Section titled “m_listen_fd”Type: int
m_private_port
Section titled “m_private_port”Type: uint16_t
m_public_port
Section titled “m_public_port”Type: uint16_t
m_running
Section titled “m_running”Type: bool
m_disposed
Section titled “m_disposed”Type: bool
m_accept_thread
Section titled “m_accept_thread”Type: os::ThreadType
m_accept_thread_stack
Section titled “m_accept_thread_stack”Type: uint8_t
m_lease_thread
Section titled “m_lease_thread”Type: os::ThreadType
m_lease_thread_stack
Section titled “m_lease_thread_stack”Type: uint8_t
m_lease_thread_running
Section titled “m_lease_thread_running”Type: bool
m_sessions
Section titled “m_sessions”Type: *
m_session_count
Section titled “m_session_count”Type: int
m_waiting_tokens
Section titled “m_waiting_tokens”Type: ryu_ldn::protocol::ExternalProxyToken
m_waiting_token_count
Section titled “m_waiting_token_count”Type: int
m_token_cv
Section titled “m_token_cv”Type: os::ConditionVariable
m_broadcast_address
Section titled “m_broadcast_address”Type: uint32_t
m_master_callback
Section titled “m_master_callback”Type: MasterSendCallback
m_callback_user_data
Section titled “m_callback_user_data”Type: void *
Members
Section titled “Members”MAX_WAITING_TOKENS
Section titled “MAX_WAITING_TOKENS”Type: constexpr int
Methods
Section titled “Methods”P2pProxyServer
Section titled “P2pProxyServer”void P2pProxyServer(MasterSendCallback master_callback, void * user_data)Constructor.
Constructor - initializes server state.The constructor initializes all member variables to safe defaults:
Parameters:
master_callback(MasterSendCallback)user_data(void *)
~P2pProxyServer
Section titled “~P2pProxyServer”void ~P2pProxyServer()Destructor - stops server and cleans up.
Destructor - cleanup all resources.Ensures clean shutdown:
P2pProxyServer
Section titled “P2pProxyServer”void P2pProxyServer(const&)Parameters:
param(const&)
operator=
Section titled “operator=”& operator=(const&)Parameters:
param(const&)
Returns: &
bool Start(uint16_t port)Start the TCP server.
Start the TCP server and begin accepting connections.portPort to listen on (default: auto-select from range)paramportSpecific port to bind to, or 0 for auto-select from rangeparamtrue if server started successfullyreturnTries ports 39990-39999 if port is 0notetrue if server started successfully, false on errorreturnSocket Creation Flow1::socket(AF_INET,SOCK_STREAM,0)│├─AF_INET=IPv4├─SOCK_STREAM=TCP(reliable,ordered)└─Protocol0=defaultforstream(TCP)normal2setsockopt(SO_REUSEADDR)│└─Allowbindtorecently-closedport(Importantforquickrestartaftercrash)normal3setsockopt(TCP_NODELAY)│└─DisableNagle’salgorithm(Criticalforlow-latencygametraffic)normal4bind(port)│└─Tryports39990-39999untilonesucceedsnormal5listen(backlog=8)│└─Allowupto8pendingconnectionsnormal6Createacceptthread│└─Threadloopscallingaccept()
Parameters:
port(uint16_t)
Returns: bool
void Stop()Stop the server and disconnect all sessions.
Stop the server and disconnect all clients.Performs clean shutdown:Thread-safe: Can be called from any thread.
IsRunning
Section titled “IsRunning”bool IsRunning()Check if server is running.
Check if the server is currently running.true if running, false if stoppedreturn
Returns: bool
GetPrivatePort
Section titled “GetPrivatePort”uint16_t GetPrivatePort()Get the private (local) port.
Returns: uint16_t
GetPublicPort
Section titled “GetPublicPort”uint16_t GetPublicPort()Get the public (UPnP) port.
Returns: uint16_t
NatPunch
Section titled “NatPunch”uint16_t NatPunch()Open a public port via UPnP.
Open a public port via UPnP for NAT traversal.Attempts to open a port mapping on the router using UPnP. Tries ports 39990-39999 until one succeeds.If successful, starts a lease renewal thread.The public port number if successful, 0 if UPnP failedreturnUPnP Port Mapping FlowSwitchRouter(IGD)Internet││││SSDPDiscovery──────────►│││◄────────IGDResponse────││││││AddPortMapping──────────►│││(39990,TCP,60s)│││◄─────────────────││││││[Nowport39990onrouterforwardstous]││││││◄────TCP───────││◄──────────────────────────│(torouter:39990)││││ Try Multiple Ports?A port might be:By trying 39990-39999, we increase the chance of finding an available port.
Returns: uint16_t
ReleaseNatPunch
Section titled “ReleaseNatPunch”void ReleaseNatPunch()Release UPnP port mapping.
Release the UPnP port mapping and stop lease renewal.Called during server shutdown to:Important: We clean up our mappings to be a good network citizen. Abandoned mappings waste router resources and can cause issues for other applications.
AddWaitingToken
Section titled “AddWaitingToken”void AddWaitingToken(const& token)Add a waiting token for an expected joiner.
Called when master server notifies us someone is about to connect.tokenToken received from master serverparamToken FlowWhen a player wants to join via P2P, the master server:When the joiner connects, they send the same token in ExternalProxyConfig. We match tokens to validate the connection and assign the virtual IP. Full HandlingIf the token queue is full (MAX_WAITING_TOKENS = 16), we drop the oldest token. This handles edge cases like:
Parameters:
token(const&)
TryRegisterUser
Section titled “TryRegisterUser”bool TryRegisterUser(* session, const ryu_ldn::protocol::ExternalProxyConfig & config, uint32_t remote_ip)Try to register a connecting user.
Try to authenticate a connecting client.Called bywhen a client sends ExternalProxyConfig. Validates the token, assigns virtual IP, and adds to player list.sessionTheattempting to authenticateconfigThe ExternalProxyConfig sent by the clientremote_ipThe client’s physical IP address (network byte order)paramtrue if authentication succeeded, false if no matching token foundreturnAuthentication ProcessWait up to AUTH_WAIT_SECONDS (1 second) for a matching tokenFor each waiting token, check:If match found: IP HandlingThe master server sends all-zeros for PhysicalIP if the client has a private IP address (192.168.x.x, 10.x.x.x, etc.). This allows clients behind NAT to connect without IP validation. SafetyUses condition variable (m_token_cv) to efficiently wait for new tokens. The thread sleeps until either:
Parameters:
session(*)config(const ryu_ldn::protocol::ExternalProxyConfig &)remote_ip(uint32_t)
Returns: bool
Configure
Section titled “Configure”void Configure(const& config)Configure broadcast address from ProxyConfig.
Calculates the broadcast address for the virtual network. Formula: broadcast = IP | ~maskExample for 10.114.0.1 with /16 mask:
Parameters:
config(const&)
HandleProxyData
Section titled “HandleProxyData”void HandleProxyData(* sender, ryu_ldn::protocol::ProxyDataHeader & header, const uint8_t * data, size_t data_len)Handle ProxyData message from a session.
ProxyData is the main data transfer message used for UDP game traffic. Routes the data to the destination session(s) based on ProxyInfo.
Parameters:
sender(*)header(ryu_ldn::protocol::ProxyDataHeader &)data(const uint8_t *)data_len(size_t)
HandleProxyConnect
Section titled “HandleProxyConnect”void HandleProxyConnect(* sender, ryu_ldn::protocol::ProxyConnectRequest & request)Handle ProxyConnect message from a session.
ProxyConnect initiates a virtual TCP connection between two players. The target responds with ProxyConnectReply (accept/reject).
Parameters:
sender(*)request(ryu_ldn::protocol::ProxyConnectRequest &)
HandleProxyConnectReply
Section titled “HandleProxyConnectReply”void HandleProxyConnectReply(* sender, ryu_ldn::protocol::ProxyConnectResponse & response)Handle ProxyConnectReply message from a session.
Response to ProxyConnect - indicates whether the connection was accepted.
Parameters:
sender(*)response(ryu_ldn::protocol::ProxyConnectResponse &)
HandleProxyDisconnect
Section titled “HandleProxyDisconnect”void HandleProxyDisconnect(* sender, ryu_ldn::protocol::ProxyDisconnectMessage & message)Handle ProxyDisconnect message from a session.
Notifies the target that a virtual connection has been closed.
Parameters:
sender(*)message(ryu_ldn::protocol::ProxyDisconnectMessage &)
OnSessionDisconnected
Section titled “OnSessionDisconnected”void OnSessionDisconnected(* session)Called when a session disconnects.
Handles cleanup when a client disconnects:The master server notification allows it to:
Parameters:
session(*)
Methods
Section titled “Methods”AcceptLoop
Section titled “AcceptLoop”void AcceptLoop()Accept loop thread function.
Main loop for accepting incoming TCP connections.This function runs in a dedicated thread and loops continuously:The loop exits when:Accept Behavioraccept() is a blocking call. Whencloses the listen socket, accept() returns with an error (EBADF or similar), which we detect and break out of the loop.
RouteMessage
Section titled “RouteMessage”void RouteMessage(* sender, ryu_ldn::protocol::ProxyInfo & info, SendFunc send_func)Route a message to appropriate session(s)
Route a proxy message to appropriate destination(s)senderThe session that sent the messageinfoProxy info with source/dest IPssend_funcFunction to call for each target sessionparamSendFuncCallable type for sending to a sessiontemplateparamsenderThe session that sent the messageinfoProxyInfo containing source and destination IPssend_funcFunction to call for each target sessionparamRouting LogicFix source IP if zero (unbound socket sends as 0.0.0.0)Reject spoofing (source IP must match sender’s virtual IP)Translate legacy broadcast (0xc0a800ff -> actual broadcast)Route to destination: NoteWe validate that the source IP matches the sender’s virtual IP. This prevents a malicious client from impersonating another player.
Parameters:
sender(*)info(ryu_ldn::protocol::ProxyInfo &)send_func(SendFunc)
NotifyMasterDisconnect
Section titled “NotifyMasterDisconnect”void NotifyMasterDisconnect(uint32_t virtual_ip)Notify master server of connection state change.
Notify master server of a client disconnection.Sends ExternalProxyConnectionState with Connected=false to the master server. This allows the server to update its records and notify other players.
Parameters:
virtual_ip(uint32_t)
LeaseRenewalLoop
Section titled “LeaseRenewalLoop”void LeaseRenewalLoop()Lease renewal thread function.
Lease renewal thread main loop.Runs continuously until m_lease_thread_running is set to false. Wakes up every PORT_LEASE_RENEW seconds (50s) to refresh the UPnP port mapping.Why 50 seconds?The lease duration is 60 seconds. By renewing at 50 seconds, we have a 10-second safety margin. If renewal fails, we have time to retry before the mapping expires.This matches Ryujinx’s timing exactly for compatibility.
StartLeaseRenewal
Section titled “StartLeaseRenewal”void StartLeaseRenewal()Start lease renewal background thread.
Start the UPnP lease renewal background thread.Internal helper called after successful. Creates a low-priority thread that periodically refreshes the UPnP port mapping.