Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation should include info about TCP/NAT hole punching (Dissociation error). #4191

Open
MaximG1234 opened this issue Feb 5, 2020 · 0 comments

Comments

@MaximG1234
Copy link

MaximG1234 commented Feb 5, 2020

When creating a cluster using Akka.NET to facilitiate real-time UI type applications it is extremely likely you will run into a situation where nodes running behind firewalls will break your cluster. This will exhibit strangly in that the cluster will typically successfully form (because you might have multiple nodes on the local subnet) but eventually 'intermittently' disassociate when a 'hidden' external node joins.

This occurs when nodes behind firewalls propogate through your cluster and 'visible nodes' attempt to communicate back to your 'hidden' nodes. Worse yet, Akka.NET errors are not always clear and while in hindsight this situation is obvious it is not necessarily intuitive when you run into it, particularly when you're new to the technology.

After some time learning to understand how Akka.NET works and finding a hole punching implementation that could be leveraged I am hoping to save other developers this headache.

I was able to find this stackoverflow question which basically provides a complete working solution using Open.NAT.

I ended up with a class that looks like this


    public class PortBuster : IPortBuster
    {
        public static int[] DefaultPorts = new int[]
        {
            5283,
            5284,
            5285,
            5286,
            5287,
            5288,
            5289,
            5290,
            5291,
            5292,
            5293,
            5294,
            5295,
            5296,
            5297
        };
        public async Task<int> BustPortAsync()
        {
            int port = this.GetUnusedPort();

            try
            {
                var discoverer = new NatDiscoverer();
                var device = await discoverer.DiscoverDeviceAsync();
                await device.CreatePortMapAsync(new Mapping(Open.Nat.Protocol.Tcp, port, port));
            }
            catch { }

            return port;
        }

        public int GetUnusedPort()
        {
            string localIp = Networking.GetLocalIPAddress();
           
            IPAddress localAddr = IPAddress.Parse(localIp);
            int result = -1;
            for (int i = 0; i < DefaultPorts.Length; i++)
            {
                try
                {
                    using (Socket tempServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
                    {
                        tempServer.Bind(new IPEndPoint(localAddr, DefaultPorts[i]));
                        tempServer.Close();
                        result = DefaultPorts[i];
                    }
                        
                    break;
                }
                catch
                {
                    // Binding failed, port is in use, try next one
                }
            }

            return result > -1 ? result : throw new OperationCanceledException("Could not find unused port.");
        }
    }

I then leverage the port created when I create my configuration with something like this.

int port = await this._portBuster.BustPortAsync();
var config = AkkaConfigManager.GetAkkaClientConfig(isSubnetDeployment,
                                                                            port, 
                                                                            this._appSettings.Host, 
                                                                            this._appSettings.Port.Value,
                                                                            this._appSettings.Roles);

The only other caveat I ran into was that I needed Open.NAT to run in a xamarin application and this required me recompiling the project in .NET standard as the nuget packages didnt work.

@MaximG1234 MaximG1234 changed the title Documentation should include info about NAT hole punching (Dissociation error). Documentation should include info about TCP/NAT hole punching (Dissociation error). Feb 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant