Recently while working on a Private cloud implementation, I
came across a scenario where I needed to test connectivity of a node to the
AD/DNS via multiple network adapters.
Many of us would know that having multiple network routes is usually done to take care of redundancy. So that if a network adapter goes down, one can use the other network interface to reach out to the node.
Many of us would know that having multiple network routes is usually done to take care of redundancy. So that if a network adapter goes down, one can use the other network interface to reach out to the node.
In order to make it easy for everyone to follow along, below
is an analogy for the above scenario:
My laptop has multiple network adapters (say Wi-Fi and Ethernet) connected to the same network. Now how do I test connectivity to a Server on the network only over say Wi-Fi network adapter?
So let’s get to it and explore some options at hand.
Below are the network adapters that list out on my laptop:
Now
I want to test connectivity to a Windows Server 2012R2 running in my network
having IP address 10.94.214.9
Using ping.exe
I can specify the source address to ping.exe using –S switch
and verify if the Server (IP 10.94.214.9) is responding to ping/ICMP requests on
a specific network interface.
But there is a gotcha with the above approach, what if the server response to ping is
disabled? Or the network firewall in place drops ICMP requests.
Using Test-NetConnection.
I initially thought of using Test-NetConnection cmdlet
(available on Server 2012 & Windows 8 above with NetTCPIP module) to do a winrm
port check to the server (port 5985), but the cmdlet doesn’t let you specify a
source address for doing a port query. It will automatically select a network
interface to perform the port query (based on the internal windows routing
table). See below the output of the cmdlet, it selects the Ethernet interface
to perform the port check.
See below the syntax of the cmdlet.
See below the syntax of the cmdlet.
PS>gcm test-netconnection -syntax Test-NetConnection [[-ComputerName]] [-TraceRoute] [-Hops ] [-InformationLevel ] [ ] Test-NetConnection [[-ComputerName] ] [-CommonTCPPort] [-InformationLevel ] [ ] Test-NetConnection [[-ComputerName] ] -Port [-InformationLevel ] [ ]
One could play with route.exe and change the network route
to the network where the server lies and then do a Test-NetConnection on the
winrm port, complicated way to handle such a small problem.
Or better as my friend Jaap Brasser told me on IM, disable the network adapter and then do the Test-NetConnection.
Or better as my friend Jaap Brasser told me on IM, disable the network adapter and then do the Test-NetConnection.
Using TCPClient
Now let’s talk about how we can do this in PowerShell.
I can create a TCP Client and connect to the server on winrm port but how do we make sure that it gets routed via a specific network interface.
The answer is really simple, we create a local endpoint (IP
+ port) and bind our TCP Client to it. All the communications then happen via
the socket.
Below is the code snippet and the explanation of it follows:
In the above code after assigning the source IP, destination IP & destination port, there is code which selects a local port to be used.
We have to be careful while selecting a random local port as it might be already be used in an active TCP connection. There is a clever .NET way of getting a list of already used ports on a local machine by using the GetActiveTcpListeners() method.
Below is the code snippet and the explanation of it follows:
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 |
$SourceIP = [IPAddress]'10.94.8.102'; # My WiFi Adapter IP address
$Destination = [IPAddress]'10.94.214.9' # Destination Server address $DestinationPort = 5985 # PSRemoting port to connect to over TCP # get an unused local port, used in local IP endpoint creation $UsedLocalPorts = ([System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()).GetActiveTcpListeners() | where -FilterScript {$PSitem.AddressFamily -eq 'Internetwork'} | Select -ExpandProperty Port do { $localport = $(Get-Random -Minimum 49152 -Maximum 65535 ) } until ( $UsedLocalPorts -notcontains $localport) # Create the local IP endpoint, this will bind to a specific N/W adapter for making the connection request $LocalIPEndPoint = New-Object -TypeName System.Net.IPEndPoint -ArgumentList $SourceIP,$localport # Create the TCP client and specify the local IP endpoint to be used. $TCPClient = New-Object -Typename System.Net.Sockets.TcpClient -ArgumentList $LocaIPEndPoint # by default the proto used is TCP to connect. # Connect to the Destination on the required port. $TCPClient.Connect($Destination, $DestinationPort) # Check the Connected property to see if the TCP connection succeeded. You can see netstat.exe output to verify the connection too $TCPClient.Connected |
In the above code after assigning the source IP, destination IP & destination port, there is code which selects a local port to be used.
We have to be careful while selecting a random local port as it might be already be used in an active TCP connection. There is a clever .NET way of getting a list of already used ports on a local machine by using the GetActiveTcpListeners() method.
$UsedLocalPorts = ([System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()).GetActiveTcpListeners() |
where -FilterScript {$PSitem.AddressFamily -eq 'Internetwork'} |
Select -ExpandProperty Port
Once I have a list of all the used local port, I can select
a non-used ephemeral port (range) using the code snippet below:
do {
$localport = $(Get-Random -Minimum 49152 -Maximum 65535 )
} until ( $UsedLocalPorts -notcontains $localport)
Now it is time to create the local endpoint using the source
IP of the network interface and the local unused port.
$LocalIPEndPoint = New-Object -TypeName
System.Net.IPEndPoint -ArgumentList $SourceIP,$localport
Once the Local endpoint is created, construct a TCP client
passing the local endpoint as an argument to it. This will ensure that the TCP connection request flows via that specific N/W adapter.
Once that is done, call the Connect() Method on the TCPclient to connect to the destination. Now the TCP connection uses the SourceIP (on a specific network adapter) to reach out to the destination.
Note – One can try specifying a source address which is not
assigned to the machine, it will let you create the local endpoint but when you
try creating the TCPClient it will throw an error saying that the address is
not valid in the context.
Using the above logic and creating an advanced function should be straight enough, that is an exercise left for the reader.
Using the above logic and creating an advanced function should be straight enough, that is an exercise left for the reader.