Get-NatType.psm1
function Get-NatType{ # Define Teredo server information $TeredoServerName = "win8.ipv6.microsoft.com" $TeredoServerPort = 3544 $OtherPort = 3543 # Define test pass results [uint32] $PrimaryExternalPort = 0xbadbeef [uint32] $SecondaryExternalPort = 0xbadbeef $TestPrimary = $false $TestCone = $false $TestAddressRestricted = $false $TestSymmetric = $false # Define RS packets [Byte[]] $Rs = 0x00, 0x01, 0x00, 0x00, 0xBB, 0x1B, 0x5F, 0xC5, 0x1B, 0x66, 0x50, 0x57, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3A, 0xFF, 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x85, 0x00, 0x7D, 0x38, 0x00, 0x00, 0x00, 0x00 [Byte[]] $RsCone = 0x00, 0x01, 0x00, 0x00, 0xBB, 0x1B, 0x5F, 0xC5, 0x1B, 0x66, 0x50, 0x57, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3A, 0xFF, 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x85, 0x00, 0xFD, 0x37, 0x00, 0x00, 0x00, 0x00 # Helper function to extract the external port from the OriginHeader # RaPacket is the contents of the IPv4 UDP payload function Get-ExternalPort([Byte[]] $RaPacket) { # The OriginPort is 15 bytes into the RaPacket and two bytes long # The RA packet must be 109 bytes long if ($RaPacket.Count -ne 109) { Write-Error "Cannot parse RA packet of unexpected length " $RaPacket.Count exit } # The ports are obfusticated $byte1 = $RaPacket[15] -bxor 0xff $byte2 = $RaPacket[16] -bxor 0xff [int] $port = $byte1 * 256 + $byte2 return $port } $OutputObject = New-Object -TypeName PsObject # Get the primary Teredo Server Address $primaryAddress = (Resolve-DnsName -Name $TeredoServerName).IPAddress Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "PrimaryTeredoServerIP" -Value $primaryAddress # Get the secondary address $addressParts = $primaryAddress.Split(".") $lastByte = [int]$addressParts[3] $lastByte += 1 $addressParts[3] = [string] $lastByte $secondaryAddress = "{0}.{1}.{2}.{3}" -f $addressParts[0], $addressParts[1], $addressParts[2], $addressParts[3] Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "SecondaryTeredoServerIP" -Value $SecondaryAddress # We don't save the IP endpoints so create one and reuse it $remoteEp = New-Object System.Net.IPEndPoint(0,0) # Create IP Endpoint objects for all send destinations $primaryEp = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Parse($primaryAddress), $TeredoServerPort) $secondaryEp = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Parse($secondaryAddress), $TeredoServerPort) $otherEp = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Parse($primaryAddress), $OtherPort) # Create IPv4 UDP sockets for communication with the primary and secondary server IPs $primarySocket = New-Object System.Net.Sockets.UdpClient $primarySocket.Client.ReceiveTimeout = 3000 # 3 seconds $secondarySocket = New-Object System.Net.Sockets.UdpClient $secondarySocket.Client.ReceiveTimeout = 3000 # 3 seconds # Perform primary RS RA Test try { $remoteEp = New-Object System.Net.IPEndPoint(0,0) $bytesSent = $primarySocket.Send($Rs, $Rs.Count, $primaryEp) $response = $primarySocket.Receive([ref] $remoteEp) $TestPrimary = $true $PrimaryExternalPort = Get-ExternalPort -RaPacket $response Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "PrimaryExternalPort" -Value $PrimaryExternalPort } catch { Write-Error "Teredo could not qualify on this network - Primary RS/RA blocked. Please check ACL for UDP port 3544" exit } # Perform Cone NAT Test # Transmit an RS from the primary socket with the cone bit set # If we receive an RA on the secondary socket then this is a CONE NAT try { $remoteEp = New-Object System.Net.IPEndPoint(0,0) $bytesSent = $primarySocket.Send($RsCone, $RsCone.Count, $primaryEp) $response = $primarySocket.Receive([ref] $remoteEp) Write-Host "Received Cone RA" $TestCone = $true Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "ReceiveConeRA" -Value $True } catch { Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "ReceiveConeRA" -Value $False Write-Warning "Did not receive cone RA. This is a non-fatal test failure. If you'd like to simulate a Cone NAT, please add your computer to the router's DMZ" } # Perform Address Restricted NAT test # Transmit an RS from the secondary socket to a Non-Teredo port ($OtherPort) # Transmit an RS from the primary socket with the cone bit set # If we receive the RA on the secondary socket then this is an Address Restricted NAT try { $remoteEp = New-Object System.Net.IPEndPoint(0,0) # Swap the port on the secondary socket $bytesSent = $secondarySocket.Send($Rs, $Rs.Count, $otherEp) # Wait 1 second to ensure in-order packet transmission Sleep -Seconds 1 $bytesSent = $primarySocket.Send($RsCone, $RsCone.Count, $primaryEp) $response = $primarySocket.Receive([ref] $remoteEp) Write-Debug "Received Address-Restricted RA" $TestAddressRestricted = $true Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "ReceiveAddressRestrictedRA" -Value $True } catch { Write-warning "Did not receive address restricted RA. This is a non-fatal test failure." Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "ReceiveAddressRestrictedRA" -Value $False } Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "NATType" -Value $null # Perform symmetric NAT test # Transmit an RS from the secondary socket # Compare the external NAT port from the RS / RA exchange to the one from the primary RS RA Test # REVIEW: Does not detect address symmetric try { $remoteEp = New-Object System.Net.IPEndPoint(0,0) $bytesSent = $PrimarySocket.Send($Rs, $Rs.Count, $secondaryEp) $response = $PrimarySocket.Receive([ref] $remoteEp) $SecondaryExternalPort = Get-ExternalPort -RaPacket $response $TestSymmetric = $PrimaryExternalPort -ne $SecondaryExternalPort Write-Debug "Received secondary RA with external port $SecondaryExternalPort" Add-Member -InputObject $OutputObject -MemberType NoteProperty -Name "SecondaryRAExternalPort" -Value $SecondaryExternalPort } catch { Write-Error "Teredo could not qualify on this network - Secondary RS / RA blocked. Please check ACL for UDP port 3544" $OutputObject.NATType = "Broken" } # Write the results if ($TestCone) { Write-Debug "NAT TYPE: Cone. Status: Green" $OutputObject.NATType = "Cone" } if ($TestAddressRestricted -and -not $TestCone) { Write-Debug "NAT TYPE: Address Restricted. Status: Green" $OutputObject.NATType = "AddressRestricted" } if (-not $TestAddressRestricted -and -not $TestSymmetric) { Write-Debug "NAT TYPE: Port Restricted. Status: Yellow" $OutputObject.NATType = "PortRestricted" } if ($TestSymmetric) { Write-Warning "Symmetric NAT detected" Write-Debug "NAT TYPE: Symmetric. Status: Red" $OutputObject.NATType = "Symmetric" } $OutputObject } export-modulemember -function Get-NatType |