# port number validation function Test-PortNumber { param([string]$Port) if ($Port -match '^\d+$' -and [int]$Port -ge 1 -and [int]$Port -le 65535) { return $true } return $false } # port check with portchecker.io api function Test-PortOpen { param( [int]$Port, [string]$PublicIP ) Write-Host "`n[INFO] Checking if port $Port is accessible from the internet..." -ForegroundColor Blue Write-Host "[INFO] Note: Make sure you have a service listening on port $Port for accurate results." -ForegroundColor Yellow if (-not $PublicIP) { Write-Host "[WARNING] Public IP is unknown, skipping port check." -ForegroundColor Yellow return $false } try { $encodedIP = [Uri]::EscapeDataString($PublicIP) $uri = "https://api.portchecker.io/v2/?ip=$encodedIP&port=$Port&protocol=tcp" $response = Invoke-RestMethod -Uri $uri -TimeoutSec 15 -ErrorAction Stop switch ($response.status) { "open" { Write-Host "[SUCCESS] Port $Port is OPEN and reachable from the internet!" -ForegroundColor Green Write-Host "[INFO] This suggests you are NOT behind CGNAT (or port forwarding is configured)." -ForegroundColor Green return $true } "closed" { Write-Host "[WARNING] Port $Port is CLOSED or not reachable from the internet." -ForegroundColor Red Write-Host "[INFO] This could indicate CGNAT or that port forwarding is not configured." -ForegroundColor Yellow return $false } default { Write-Host "[WARNING] Unable to determine port status definitively." -ForegroundColor Yellow Write-Host "[INFO] Response: $(ConvertTo-Json $response -Depth 5)" -ForegroundColor Yellow return $false } } } catch { Write-Host "[WARNING] Failed to connect to port checking service: $_" -ForegroundColor Yellow return $false } } # test if local ip in cgnat range function Test-LocalCGNATIP { param([string]$LocalIP) if (-not $LocalIP) { return $false } if ($LocalIP -match '^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.\d+\.\d+$') { Write-Host "[WARNING] Local IP ($LocalIP) is in CGNAT range (100.64.0.0/10)!" -ForegroundColor Red Write-Host "[INFO] This is a strong indicator that you are behind CGNAT." -ForegroundColor Red return $true } return $false } # nat layer counter function Get-NATLayerCount { param([string[]]$TracerouteOutput) $natCount = 0 $prevWasPrivate = $false foreach ($line in $TracerouteOutput) { if ($line -match '10\.\d+\.\d+\.\d+|172\.(1[6-9]|2[0-9]|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.\d+\.\d+') { if (-not $prevWasPrivate) { $natCount++ $prevWasPrivate = $true } } elseif ($line -match '\d+\.\d+\.\d+\.\d+') { $prevWasPrivate = $false } } return $natCount } Write-Host "==========================================" -ForegroundColor Cyan Write-Host " CGNAT Detection Tool - Windows" -ForegroundColor Cyan Write-Host "==========================================" -ForegroundColor Cyan Write-Host "" # use ipinfo api to get public ip Write-Host "[INFO] Retrieving network information..." -ForegroundColor Blue try { $public_ipv4 = (Invoke-RestMethod -Uri "http://ipinfo.io/ip").Trim() # response validation if ($public_ipv4 -notmatch '^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$') { Write-Host "[ERROR] Invalid IP address format received: $public_ipv4" -ForegroundColor Red Write-Host "Press Enter to continue..." Read-Host exit 1 } } catch { Write-Host "[ERROR] Failed to retrieve public IPv4 address. Ensure you are connected to the internet." -ForegroundColor Red Write-Host "Press Enter to continue..." Read-Host exit 1 } # get default gateway try { $gateway = (Get-NetRoute | Where-Object { $_.DestinationPrefix -eq "0.0.0.0/0" } | Select-Object -First 1).NextHop if (-not $gateway) { Write-Host "[WARNING] Could not determine default gateway." -ForegroundColor Yellow $gateway = "Unknown" } } catch { Write-Host "[WARNING] Error retrieving default gateway." -ForegroundColor Yellow $gateway = "Unknown" } # get iface information try { $interface = Get-NetRoute | Where-Object { $_.DestinationPrefix -eq "0.0.0.0/0" } | Select-Object -First 1 -ExpandProperty InterfaceAlias if (-not $interface) { $interface = "Unknown" } } catch { $interface = "Unknown" } # get local ip addr try { $localIP = (Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.InterfaceAlias -eq $interface -and $_.PrefixOrigin -ne "WellKnown" } | Select-Object -First 1).IPAddress if (-not $localIP) { $localIP = "Unknown" } } catch { $localIP = "Unknown" } Write-Host "" Write-Host "[INFO] Network Information:" -ForegroundColor Blue Write-Host "[INFO] Public IP: $public_ipv4" -ForegroundColor Blue Write-Host "[INFO] Local IP: $localIP" -ForegroundColor Blue Write-Host "[INFO] Gateway: $gateway" -ForegroundColor Blue Write-Host "[INFO] Interface: $interface" -ForegroundColor Blue Write-Host "" $cgnatDetected = $false Write-Host "==========================================" -ForegroundColor Cyan Write-Host " Local IP Range Check" -ForegroundColor Cyan Write-Host "==========================================" -ForegroundColor Cyan if (Test-LocalCGNATIP -LocalIP $localIP) { $cgnatDetected = $true } else { Write-Host "[SUCCESS] Local IP is not in CGNAT range." -ForegroundColor Green } Write-Host "" # traceroute check Write-Host "==========================================" -ForegroundColor Cyan Write-Host " Traceroute Analysis" -ForegroundColor Cyan Write-Host "==========================================" -ForegroundColor Cyan Write-Host "[INFO] Performing traceroute to $public_ipv4 (this may take a moment)..." -ForegroundColor Yellow try { $traceroute_output = tracert -h 8 $public_ipv4 2>&1 if ($LASTEXITCODE -ne 0 -or -not $traceroute_output) { Write-Host "[ERROR] Failed to perform traceroute. Check your network settings and permissions." -ForegroundColor Red Write-Host "Press Enter to continue..." Read-Host exit 1 } # check for private IP addresses in traceroute # 10.0.0.0/8 (private class A: 10.0.0.0 - 10.255.255.255) # 172.16.0.0/12 (private class B: 172.16.0.0 - 172.31.255.255) # 192.168.0.0/16 (private class C: 192.168.0.0 - 192.168.255.255) # 100.64.0.0/10 (shared address space / cgnat: 100.64.0.0 - 100.127.255.255) $private_ip_pattern = '10\.\d+\.\d+\.\d+|172\.(1[6-9]|2[0-9]|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\.\d+\.\d+' $found_private_ip = $false foreach ($line in $traceroute_output) { if ($line -match $private_ip_pattern) { $found_private_ip = $true break } } if ($found_private_ip) { Write-Host "[WARNING] Private IP addresses detected in traceroute path!" -ForegroundColor Red $cgnatDetected = $true # nat layer count $natLayers = Get-NATLayerCount -TracerouteOutput $traceroute_output if ($natLayers -gt 1) { Write-Host "[WARNING] Multiple NAT layers detected ($natLayers layers)!" -ForegroundColor Red Write-Host "[INFO] This strongly suggests CGNAT or multiple routers." -ForegroundColor Red } } else { Write-Host "[SUCCESS] No private IP addresses detected in traceroute." -ForegroundColor Green } # traceroute output Write-Host "`n[INFO] Traceroute output (first 8 hops):" -ForegroundColor Blue Write-Host $traceroute_output Write-Host "" } catch { Write-Host "[ERROR] Error during traceroute. Check your network settings." -ForegroundColor Red Write-Host "Press Enter to continue..." Read-Host exit 1 } # optional port check Write-Host "==========================================" -ForegroundColor Cyan Write-Host " Port Reachability Test (Optional)" -ForegroundColor Cyan Write-Host "==========================================" -ForegroundColor Cyan Write-Host "[INFO] This test checks if a port on your machine is reachable from the internet." -ForegroundColor Yellow Write-Host "[INFO] If you're behind CGNAT without port forwarding, ports will be unreachable." -ForegroundColor Yellow Write-Host "" $portCheckResponse = Read-Host "Would you like to perform a port check? (y/n)" if ($portCheckResponse -match '^[Yy]$') { $validPort = $false while (-not $validPort) { $portInput = Read-Host "Enter a TCP port number to test (1-65535)" if (Test-PortNumber -Port $portInput) { Write-Host "" Write-Host "[INFO] You selected port: $portInput" -ForegroundColor Yellow Write-Host "[INFO] Make sure you have a service listening on this port for accurate results." -ForegroundColor Yellow $confirmResponse = Read-Host "Confirm and proceed with port check? (y/n)" if ($confirmResponse -match '^[Yy]$') { Test-PortOpen -Port ([int]$portInput) -PublicIP $public_ipv4 $validPort = $true } else { Write-Host "[INFO] Port check cancelled." -ForegroundColor Yellow $validPort = $true } } else { Write-Host "[ERROR] Invalid port number. Please enter a number between 1 and 65535." -ForegroundColor Red } } } else { Write-Host "[INFO] Port check skipped." -ForegroundColor Yellow } Write-Host "" Write-Host "==========================================" -ForegroundColor Cyan Write-Host " Results" -ForegroundColor Cyan Write-Host "==========================================" -ForegroundColor Cyan if ($cgnatDetected) { Write-Host "[WARNING] CGNAT LIKELY DETECTED!" -ForegroundColor Red Write-Host "[INFO] Based on the tests performed, you appear to be behind CGNAT." -ForegroundColor Yellow Write-Host "[INFO] For absolute confirmation, log into your router at $gateway" -ForegroundColor Yellow Write-Host "[INFO] and check if your WAN IP matches your public IP ($public_ipv4)." -ForegroundColor Yellow } else { Write-Host "[SUCCESS] You do not appear to be behind CGNAT." -ForegroundColor Green Write-Host "[INFO] Your connection seems to have a direct public IP address." -ForegroundColor Green } Write-Host "" Write-Host "[INFO] Additional Notes:" -ForegroundColor Blue Write-Host "[INFO] - CGNAT uses the 100.64.0.0/10 IP range (RFC 6598)" -ForegroundColor Blue Write-Host "[INFO] - Private ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16" -ForegroundColor Blue Write-Host "[INFO] - If behind CGNAT, inbound connections and port forwarding won't work" -ForegroundColor Blue Write-Host "[INFO] - Contact your ISP if you need a public IP address" -ForegroundColor Blue Write-Host "" Write-Host "Press Enter to continue..." Read-Host