4

With a homemade discovery tools, I've discovered a list of services that I'm interested to.

I've their IP, service name, port, host, ... But to use them, I've to specify to the client library the IP we will be using.

Since I've several network cards, I need to detect which interface is used to communicate with the destination IP that I know, and then give this IPAddress to my library.

But how could I detected which interface IP I should use?

I tried to make some search over the internet, but I think I don't have the right keywords because I don't find anything relevant.

1

3 Answers 3

10

I've pulled the relevant method out for you from the network library I develop for, networkComms.net:

/// <summary>
/// Determines the most appropriate local end point to contact the provided remote end point. 
/// Testing shows this method takes on average 1.6ms to return.
/// </summary>
/// <param name="remoteIPEndPoint">The remote end point</param>
/// <returns>The selected local end point</returns>
public static IPEndPoint BestLocalEndPoint(IPEndPoint remoteIPEndPoint)
{    
    Socket testSocket = new Socket(remoteIPEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
    testSocket.Connect(remoteIPEndPoint);
    return (IPEndPoint)testSocket.LocalEndPoint;
}

Once you have the correct IP and you could then iterate over NetworkInterface.GetAllNetworkInterfaces() to locate the matching adapter.

Sign up to request clarification or add additional context in comments.

2 Comments

Great, and how this choose the endpoint if we have several endpoint that allow me to ping my remote IP? Choose the fastest?
The decision is made by the base operating system. The operating system will generally make this decision using the routing tables, specifically router metrics (en.wikipedia.org/wiki/Router_metrics). You could force a manual decision by accessing the routing tables yourself, as in the other answer but this is generally more hassle than its worth.
1

The following works by querying the routing table. This is the same way Socket.Connect determines what local endpoint to use. Differences:

  • can't fail due to firewall
  • can't fail due to nonexistent remote endpoint
  • doesn't reserve a local port
  • faster

.

private static IPEndPoint QueryRoutingInterface(
          Socket socket,
          IPEndPoint remoteEndPoint)
{
    SocketAddress address = remoteEndPoint.Serialize();

    byte[] remoteAddrBytes = new byte[address.Size];
    for (int i = 0; i < address.Size; i++) {
        remoteAddrBytes[i] = address[i];
    }

    byte[] outBytes = new byte[remoteAddrBytes.Length];
    socket.IOControl(
                IOControlCode.RoutingInterfaceQuery, 
                remoteAddrBytes, 
                outBytes);
    for (int i = 0; i < address.Size; i++) {
        address[i] = outBytes[i];
    }

    EndPoint ep = remoteEndPoint.Create(address);
    return (IPEndPoint)ep;
}

which is used like:

IPAddress remoteIp = IPAddress.Parse("192.168.1.55");
IpEndPoint remoteEndPoint = new IPEndPoint(remoteIp, 0);
Socket socket = new Socket(
                      AddressFamily.InterNetwork, 
                      SocketType.Dgram, 
                      ProtocolType.Udp);
IPEndPoint localEndPoint = QueryRoutingInterface(socket, remoteEndPoint );
Console.WriteLine("Local EndPoint is: {0}", localEndPoint);

(source code copied from here)

Please note that although one is specifying an IpEndPoint with a port, the port is irrelevant. Also, the returned IpEndPoint.Port is always 0.

Comments

0

I have spent a couple of hours to find short answer for that question and, thanks to BatteryBackupUnit, I have made this solution and hope It helps other programmers:

    [DllImport("ws2_32.dll", SetLastError = true)]
    private static extern SocketError WSAIoctl([In] IntPtr socketHandle, uint ioControlCode, [In] byte[] inBuffer, int inBufferSize, [Out] byte[] outBuffer, int outBufferSize, out int bytesTransferred, IntPtr overlapped, IntPtr completionRoutine);

    /// <summary>Get local IP-address for remote address</summary>
    /// <param name="remoteAddress">Remote Address</param>
    /// <returns></returns>
    public static IPAddress GetLocalAddressForRemote(IPAddress remoteAddress)
    {
        if (remoteAddress == null) return null;

        long rm = remoteAddress.Address;
        //Temporary socket only for handle of it
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        byte[] src = new byte[16];

        src[0] = 2;
        src[4] = (byte)rm;
        src[5] = (byte)(rm >> 8);
        src[6] = (byte)(rm >> 16);
        src[7] = (byte)(rm >> 24);

        int transeferred = 0;
        if (WSAIoctl(s.Handle, 3355443220, src, 16, src, 16, out transeferred, IntPtr.Zero, IntPtr.Zero) == SocketError.Success)
        {
            s.Dispose();
            rm = src[4];
            rm |= ((long)src[5]) << 8;
            rm |= ((long)src[6]) << 16;
            rm |= ((long)src[7]) << 24;
            return new IPAddress(rm);
        }
        s.Dispose();
        return null;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.