Russian Gepetto Russian Gepetto - 26 days ago 12
C# Question

Cant connect to own IPv6 TCP server app running on ubuntu with mono

My test infrastructure consist of 3 machines: 1 physical machine with windows 10 running hyperv and 2 virtual machines with ubuntu OS installed. There is full network connectivity between all machines (they ping each other). I wrote 2 simple C# programs: TCP client and TCP server (below I attached minimal code that reproduces the problem). When I run the client on the windows machine and the server on one of the ubuntu machines everything is working good. However when I try to run the client on one of the ubuntu machines and server on the other ubuntu machine then I get an error at the client side:


TCPClientTest.exe Information: 0 : System.Net.Sockets.SocketException (0x80004005): Invalid arguments
at System.Net.Sockets.TcpClient.Connect (System.Net.IPAddress[] ipAddresses, System.Int32 port) [0x000e9] in <59be416de143456b88b9988284f43350>:0
at System.Net.Sockets.TcpClient.Connect (System.String hostname, System.Int32 port) [0x00007] in <59be416de143456b88b9988284f43350>:0
at System.Net.Sockets.TcpClient..ctor (System.String hostname, System.Int32 port) [0x00006] in <59be416de143456b88b9988284f43350>:0
at TCPClientTest.Program.Main (System.String[] args) [0x00002] in :0
DateTime=2016-11-09T21:25:42.4641950Z


TCP client:

TcpClient client = new TcpClient("fe80::3", 15000);
NetworkStream stream = client.GetStream();
int number = stream.ReadByte();
stream.Close();
client.Close();


TCP server:

TcpListener server = new TcpListener(IPAddress.IPv6Any, 15000);
server.Start();

TcpClient client = server.AcceptTcpClient();
NetworkStream stream = client.GetStream();
stream.WriteByte(199);
stream.Close();
client.Close();


Ubuntu version: 16.04 LTS

Mono version: 4.6 Service Release 0.1 (4.6.1.5)

What can be the problem?

Answer

I have found the problem. IPv6 link local addresses are assosiated with a number called scope id that specifies the network interface that we would like to use for the connection. It looks like at Linux systems we must explicitly provide this information (even using ping6 we need to do that) but at Windows there is no such requirement and according to this article https://technet.microsoft.com/pl-pl/ms739166 it's using Neighbor Discovery and tries to get the proper interface for us.

For example: at Windows ping fe80::3 will work correct but at Linux we need to do ping6 -I eth0 fe80::3 or ping6 fe80::3%2

TCP client had to be slightly modified to respect the scope id and specify the network interface that it will be using to work correctly:

IPAddress ipAddress = null;

foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
{
    IPInterfaceProperties ipIntProperties = networkInterface.GetIPProperties();
    foreach (UnicastIPAddressInformation addr in ipIntProperties.UnicastAddresses)
    {
        String addressWithoutScopeId = addr.Address.ToString().Split('%')[0];
        if (addressWithoutScopeId.Equals("fe80::2"))
        {
            ipAddress = addr.Address;
            break;
         }

     }
     if (ipAddress != null)
         break;
}

var endPoint = new IPEndPoint(ipAddress, 0);
TcpClient client = new TcpClient(endPoint);
client.Connect(IPAddress.Parse("fe80::3"), 15000);
NetworkStream stream = client.GetStream();
int number = stream.ReadByte();
stream.Close();
client.Close();