![]() |
VOOZH | about |
dotnet add package NATTunnel --version 0.7.3
NuGet\Install-Package NATTunnel -Version 0.7.3
<PackageReference Include="NATTunnel" Version="0.7.3" />
<PackageVersion Include="NATTunnel" Version="0.7.3" />Directory.Packages.props
<PackageReference Include="NATTunnel" />Project file
paket add NATTunnel --version 0.7.3
#r "nuget: NATTunnel, 0.7.3"
#:package NATTunnel@0.7.3
#addin nuget:?package=NATTunnel&version=0.7.3Install as a Cake Addin
#tool nuget:?package=NATTunnel&version=0.7.3Install as a Cake Tool
This program is currently a work in progress.
This is intended to be used to create a (mostly) decentralized peer-to-peer mesh network regardless of the NAT types that may be encountered. It's akin to tools like Hamachi and ZeroTier, but it implements some methods that are more capable of handling symmetric and CGNAT specifically, unlike these tools which often give up way too easily and simply fallback to relaying. Much of the info about how it works can be found in the OVERVIEW.md.
There are two ways to use NATTunnel:
The only prerequisite is the WireGuard client, which can be downloaded here.
Here is an example config with the only settings you really need to care about:
The embedded library lets a host application (ex. game) join a NAT-traversed mesh network and treat each connected peer as a normal local UDP endpoint. Built atop System.Security.Cryptography's ChaCha20-Poly1305 and a Noise XX handshake (via Noise.NET).
dotnet add package NATTunnel
using NATTunnel;
using System.Net;
using System.Net.Sockets;
using System.Text;
// 1. Host app binds its own UDP socket on a known loopback port — this is where
// incoming packets from peers will arrive.
const int hostGamePort = 51000;
var hostSocket = new UdpClient(new IPEndPoint(IPAddress.Loopback, hostGamePort));
_ = Task.Run(async () =>
{
while (true)
{
var pkt = await hostSocket.ReceiveAsync();
// pkt.RemoteEndPoint is the loopback endpoint of the sending peer.
Console.WriteLine($"Got {pkt.Buffer.Length} bytes from {pkt.RemoteEndPoint}");
}
});
// 2. Configure + start the mesh node.
using var node = new MeshNode(new MeshConfig
{
NetworkID = "my-game-lobby-42",
NetworkSecret = "shared-secret",
MediationEndpoint = "sync.milesthenerd.net:6510",
HostGamePort = hostGamePort,
// Optional: persistent identity, relay capacity, etc.
// PersistentPeerID = playerAccountGuid,
// MinLogLevel = LogLevel.Warning
// Logger = line => Console.WriteLine(line),
});
node.PeerConnected += peer =>
{
Console.WriteLine($"Peer joined: {peer.PeerID} via {peer.LoopbackEndpoint}");
// Send to peer.LoopbackEndpoint as if it were any remote UDP endpoint.
byte[] hello = Encoding.UTF8.GetBytes("hello!");
hostSocket.Send(hello, hello.Length, peer.LoopbackEndpoint);
};
node.PeerDisconnected += peer =>
{
Console.WriteLine($"Peer left: {peer.PeerID}");
};
node.Start();
// Block until you're done — e.g. wait for a shutdown signal.
await Task.Delay(-1);
using NATTunnel;
using System.Net;
using System.Net.Sockets;
using System.Text;
// 1. Host app binds its own UDP socket on a known loopback port — this is where
// incoming packets from peers will arrive.
const int hostGamePort = 51000;
var hostSocket = new UdpClient(new IPEndPoint(IPAddress.Loopback, hostGamePort));
_ = Task.Run(async () =>
{
while (true)
{
var pkt = await hostSocket.ReceiveAsync();
// pkt.RemoteEndPoint is the loopback endpoint of the sending peer.
Console.WriteLine($"Got {pkt.Buffer.Length} bytes from {pkt.RemoteEndPoint}");
}
});
// 2. Configure + start the mesh node.
using var node = new MeshNode(new MeshConfig
{
NetworkID = "my-game-lobby-42",
NetworkSecret = "shared-secret",
MediationEndpoint = "sync.milesthenerd.net:6510",
HostGamePort = hostGamePort,
LocalIdentity = Encoding.UTF8.GetBytes("server"), // 256 byte limit
ReliableMessageTimeout = TimeSpan.FromSeconds(10), // default 5s
});
node.PeerConnected += peer =>
{
var identity = Encoding.UTF8.GetString(peer.Identity);
if (identity == "server")
{
// client connect logic here
}
};
node.MessageReceived += async (peer, bytes) => {
var str = Encoding.UTF8.GetString(bytes);
Console.WriteLine($"Got msg {str} from {peer.PeerID}");
// send same bytes back
await node.SendMessageAsync(peer, bytes, reliable: true);
// or
await node.BroadcastAsync(bytes, reliable: false);
}
node.Start();
// Block until you're done — e.g. wait for a shutdown signal.
await Task.Delay(-1);
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.7.3 | 129 | 6/3/2026 |
| 0.7.2 | 96 | 6/2/2026 |
| 0.7.1 | 108 | 5/31/2026 |
| 0.7.0 | 98 | 5/31/2026 |
| 0.6.0 | 95 | 5/31/2026 |
| 0.5.2 | 105 | 5/31/2026 |
| 0.5.1 | 99 | 5/30/2026 |
| 0.5.0 | 97 | 5/30/2026 |
| 0.4.1 | 99 | 5/30/2026 |
| 0.4.0 | 98 | 5/30/2026 |
| 0.3.0 | 105 | 5/30/2026 |
| 0.2.5 | 96 | 5/26/2026 |
| 0.2.4 | 93 | 5/25/2026 |
| 0.2.3 | 89 | 5/25/2026 |
| 0.2.2 | 101 | 5/25/2026 |
| 0.2.1 | 96 | 5/24/2026 |
| 0.2.0 | 93 | 5/24/2026 |
| 0.1.3 | 93 | 5/24/2026 |
| 0.1.2 | 93 | 5/24/2026 |
| 0.1.1 | 93 | 5/24/2026 |