deterministic mirror ports derived from server MAC + upstream port

mirror = (upstream_port ^ (mac[0]<<8 | mac[5])); clamped to >=1024.
Same server+upstream always yields the same local port so the user
knows where to connect without checking the UI each time. Falls back
to OS-assigned port if the deterministic one is already in use.
This commit is contained in:
2026-08-13 08:09:47 +00:00
parent 3336a08543
commit 54c804f81f
+35 -2
View File
@@ -88,15 +88,48 @@ sealed class SessionManager : IDisposable
} }
} }
/// <summary>
/// Compute a deterministic mirror port from the server MAC and the
/// upstream port. XOR the upstream port with (mac[0]<<8 | mac[5]),
/// then ensure the result is outside the privileged range.
/// </summary>
static ushort ComputeMirrorPort(byte[] serverMac, ushort upstreamPort)
{
var k = (ushort)((serverMac[0] << 8) | serverMac[5]);
var port = (ushort)(upstreamPort ^ k);
if (port < 1024)
port += 1024;
return port;
}
/// <summary> /// <summary>
/// Start a local TCP listener for the given upstream. Returns the mirror /// Start a local TCP listener for the given upstream. Returns the mirror
/// port, or 0 on failure. /// port, or 0 on failure.
/// </summary> /// </summary>
public int StartListener(UpstreamEntry upstream) public int StartListener(UpstreamEntry upstream)
{ {
var listener = new TcpListener(IPAddress.Loopback, 0); if (_serverMac == null)
return 0;
var preferred = ComputeMirrorPort(_serverMac, upstream.Port);
// Try the deterministic port first; fall back to OS assignment.
TcpListener listener;
int port;
try
{
listener = new TcpListener(IPAddress.Loopback, preferred);
listener.Start(); listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port; port = ((IPEndPoint)listener.LocalEndpoint).Port;
}
catch
{
listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
port = ((IPEndPoint)listener.LocalEndpoint).Port;
Log?.Invoke($"port {preferred} in use, fell back to {port}");
}
var state = new ListenerState(listener, upstream); var state = new ListenerState(listener, upstream);
_listeners[port] = state; _listeners[port] = state;
_ = AcceptLoop(state); _ = AcceptLoop(state);