Reputation: 20707
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.
Upvotes: 4
Views: 2629
Reputation: 13233
The following works by querying the routing table. This is the same way Socket.Connect
determines what local endpoint to use. Differences:
.
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
.
Upvotes: 1
Reputation: 6883
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;
}
Upvotes: 0
Reputation: 3299
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.
Upvotes: 10