Last active
January 14, 2026 18:19
-
-
Save WinSe7en/3f80309200ae227603e484367c6ea67c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| GlobalProtect VPN Troubleshooting Tool | |
| .DESCRIPTION | |
| Easy-to-use diagnostic tool for GlobalProtect VPN connectivity issues. | |
| Designed for end users - just run and follow the prompts! | |
| .NOTES | |
| University of Denver - IT Help | |
| Questions? Contact the IT Help Desk | |
| #> | |
| # ============================================================ | |
| # SETUP - Don't modify below this line | |
| # ============================================================ | |
| # Ensure we can run | |
| $ErrorActionPreference = "SilentlyContinue" | |
| $ProgressPreference = "SilentlyContinue" | |
| # Check if running as admin (some features need it) | |
| $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) | |
| # Results storage | |
| $script:results = [ordered]@{ | |
| Timestamp = Get-Date -Format "o" | |
| Hostname = $env:COMPUTERNAME | |
| Username = $env:USERNAME | |
| Tests = @{} | |
| } | |
| # ============================================================ | |
| # HELPER FUNCTIONS | |
| # ============================================================ | |
| function Show-Banner { | |
| Clear-Host | |
| Write-Host "" | |
| Write-Host " ╔════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan | |
| Write-Host " ║ ║" -ForegroundColor Cyan | |
| Write-Host " ║ GlobalProtect VPN Troubleshooting Tool ║" -ForegroundColor Cyan | |
| Write-Host " ║ University of Denver ║" -ForegroundColor Cyan | |
| Write-Host " ║ ║" -ForegroundColor Cyan | |
| Write-Host " ╚════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan | |
| Write-Host "" | |
| Write-Host " Computer: $env:COMPUTERNAME" -ForegroundColor Gray | |
| Write-Host " User: $env:USERNAME" -ForegroundColor Gray | |
| Write-Host " Time: $(Get-Date -Format 'MMM dd, yyyy h:mm tt')" -ForegroundColor Gray | |
| if (-not $isAdmin) { | |
| Write-Host " Mode: Standard (some features limited)" -ForegroundColor Yellow | |
| } else { | |
| Write-Host " Mode: Administrator" -ForegroundColor Green | |
| } | |
| Write-Host "" | |
| } | |
| function Write-Step { | |
| param([string]$Message) | |
| Write-Host " → $Message" -ForegroundColor White | |
| } | |
| function Write-Status { | |
| param( | |
| [string]$Test, | |
| [string]$Result, | |
| [string]$Status # "pass", "warn", "fail", "info" | |
| ) | |
| $icon = switch ($Status) { | |
| "pass" { "✓"; "Green" } | |
| "warn" { "⚠"; "Yellow" } | |
| "fail" { "✗"; "Red" } | |
| "info" { "●"; "Cyan" } | |
| default { "○"; "Gray" } | |
| } | |
| $padding = 45 - $Test.Length | |
| if ($padding -lt 1) { $padding = 1 } | |
| Write-Host " $($icon[0]) " -ForegroundColor $icon[1] -NoNewline | |
| Write-Host "$Test" -NoNewline | |
| Write-Host (" " * $padding) -NoNewline | |
| Write-Host "[$Result]" -ForegroundColor $icon[1] | |
| } | |
| function Write-SectionHeader { | |
| param([string]$Title, [int]$Number) | |
| Write-Host "" | |
| Write-Host " ┌────────────────────────────────────────────────────────────┐" -ForegroundColor Cyan | |
| Write-Host " │ STEP $Number : $Title$(" " * (47 - $Title.Length))│" -ForegroundColor Cyan | |
| Write-Host " └────────────────────────────────────────────────────────────┘" -ForegroundColor Cyan | |
| Write-Host "" | |
| } | |
| function Write-Progress-Dots { | |
| param([string]$Message, [int]$Seconds = 2) | |
| Write-Host " $Message" -NoNewline | |
| for ($i = 0; $i -lt $Seconds; $i++) { | |
| Start-Sleep -Milliseconds 500 | |
| Write-Host "." -NoNewline | |
| } | |
| Write-Host "" | |
| } | |
| function Pause-ForUser { | |
| param([string]$Message = "Press any key to continue...") | |
| Write-Host "" | |
| Write-Host " $Message" -ForegroundColor Yellow | |
| $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | |
| } | |
| function Show-Menu { | |
| param( | |
| [string]$Title, | |
| [string[]]$Options | |
| ) | |
| Write-Host "" | |
| Write-Host " $Title" -ForegroundColor Cyan | |
| Write-Host " $("-" * $Title.Length)" -ForegroundColor Cyan | |
| Write-Host "" | |
| for ($i = 0; $i -lt $Options.Count; $i++) { | |
| Write-Host " [$($i + 1)] $($Options[$i])" -ForegroundColor White | |
| } | |
| Write-Host "" | |
| $choice = Read-Host " Enter your choice (1-$($Options.Count))" | |
| return $choice | |
| } | |
| # ============================================================ | |
| # TEST FUNCTIONS | |
| # ============================================================ | |
| function Test-InternetHealth { | |
| Write-SectionHeader "Checking Your Internet Connection" 1 | |
| Write-Step "This checks if your internet is working before we test the VPN..." | |
| Write-Host "" | |
| $score = 0 | |
| $maxScore = 4 | |
| # Test 1: DNS | |
| Write-Progress-Dots "Testing DNS (website name lookup)" 2 | |
| $dnsWorks = $false | |
| try { | |
| $dns = Resolve-DnsName -Name "google.com" -Server "8.8.8.8" -DnsOnly -QuickTimeout -ErrorAction Stop | |
| $dnsWorks = $true | |
| Write-Status "DNS Resolution" "Working" "pass" | |
| $score++ | |
| } catch { | |
| Write-Status "DNS Resolution" "Failed" "fail" | |
| } | |
| $script:results.Tests["Internet_DNS"] = $dnsWorks | |
| # Test 2: Can reach websites | |
| Write-Progress-Dots "Testing connection to websites" 2 | |
| $webWorks = $false | |
| try { | |
| $tcp = New-Object System.Net.Sockets.TcpClient | |
| $connect = $tcp.BeginConnect("www.google.com", 443, $null, $null) | |
| $wait = $connect.AsyncWaitHandle.WaitOne(3000, $false) | |
| if ($wait -and $tcp.Connected) { | |
| $webWorks = $true | |
| Write-Status "Website Connectivity" "Working" "pass" | |
| $score++ | |
| } else { | |
| Write-Status "Website Connectivity" "Timeout" "fail" | |
| } | |
| $tcp.Close() | |
| } catch { | |
| Write-Status "Website Connectivity" "Failed" "fail" | |
| } | |
| $script:results.Tests["Internet_Web"] = $webWorks | |
| # Test 3: Speed/Latency | |
| Write-Progress-Dots "Checking connection speed" 3 | |
| $pingResult = Test-Connection -ComputerName "8.8.8.8" -Count 4 -ErrorAction SilentlyContinue | |
| if ($pingResult) { | |
| $avgLatency = [math]::Round(($pingResult | Measure-Object -Property ResponseTime -Average).Average, 0) | |
| $script:results.Tests["Internet_Latency"] = $avgLatency | |
| if ($avgLatency -lt 50) { | |
| Write-Status "Connection Speed" "Excellent ($avgLatency ms)" "pass" | |
| $score++ | |
| } elseif ($avgLatency -lt 100) { | |
| Write-Status "Connection Speed" "Good ($avgLatency ms)" "pass" | |
| $score++ | |
| } elseif ($avgLatency -lt 200) { | |
| Write-Status "Connection Speed" "Slow ($avgLatency ms)" "warn" | |
| } else { | |
| Write-Status "Connection Speed" "Very Slow ($avgLatency ms)" "fail" | |
| } | |
| } else { | |
| Write-Status "Connection Speed" "Could not test" "fail" | |
| $script:results.Tests["Internet_Latency"] = -1 | |
| } | |
| # Test 4: Packet Loss | |
| Write-Progress-Dots "Checking for dropped connections" 3 | |
| $lossTest = Test-Connection -ComputerName "8.8.8.8" -Count 10 -ErrorAction SilentlyContinue | |
| if ($lossTest) { | |
| $lossPercent = [math]::Round((10 - $lossTest.Count) / 10 * 100, 0) | |
| $script:results.Tests["Internet_PacketLoss"] = $lossPercent | |
| if ($lossPercent -eq 0) { | |
| Write-Status "Connection Stability" "Stable (0% loss)" "pass" | |
| $score++ | |
| } elseif ($lossPercent -le 10) { | |
| Write-Status "Connection Stability" "Minor issues ($lossPercent% loss)" "warn" | |
| } else { | |
| Write-Status "Connection Stability" "Unstable ($lossPercent% loss)" "fail" | |
| } | |
| } else { | |
| Write-Status "Connection Stability" "Could not test" "fail" | |
| $script:results.Tests["Internet_PacketLoss"] = 100 | |
| } | |
| # Test 5: MTU (hidden complexity from user) | |
| Write-Progress-Dots "Checking network configuration" 2 | |
| $mtuSizes = @(1472, 1450, 1400, 1350, 1300, 1250, 1200, 1100, 1000) | |
| $maxMTU = 0 | |
| foreach ($size in $mtuSizes) { | |
| $pingTest = ping -n 1 -f -l $size 8.8.8.8 2>&1 | |
| if ($pingTest -match "Reply from" -and $pingTest -notmatch "fragmented") { | |
| $maxMTU = $size | |
| break | |
| } | |
| } | |
| $script:results.Tests["Internet_MTU"] = $maxMTU | |
| if ($maxMTU -ge 1400) { | |
| Write-Status "Network Configuration" "Normal" "pass" | |
| $script:results.Tests["ISP_Type"] = "Standard" | |
| } elseif ($maxMTU -ge 1300) { | |
| Write-Status "Network Configuration" "DSL/PPPoE detected" "info" | |
| $script:results.Tests["ISP_Type"] = "PPPoE" | |
| } elseif ($maxMTU -gt 0) { | |
| Write-Status "Network Configuration" "Mobile hotspot or restricted" "warn" | |
| $script:results.Tests["ISP_Type"] = "Mobile/Restricted" | |
| } else { | |
| Write-Status "Network Configuration" "Could not determine" "warn" | |
| $script:results.Tests["ISP_Type"] = "Unknown" | |
| } | |
| # Summary | |
| Write-Host "" | |
| $script:results.Tests["Internet_Score"] = $score | |
| $script:results.Tests["Internet_MaxScore"] = $maxScore | |
| if ($score -eq $maxScore) { | |
| Write-Host " ✓ Your internet connection looks healthy!" -ForegroundColor Green | |
| $script:results.Tests["Internet_Health"] = "Healthy" | |
| return $true | |
| } elseif ($score -ge 2) { | |
| Write-Host " ⚠ Your internet is working but has some issues." -ForegroundColor Yellow | |
| $script:results.Tests["Internet_Health"] = "Degraded" | |
| return $true | |
| } else { | |
| Write-Host " ✗ Your internet connection has problems." -ForegroundColor Red | |
| Write-Host " The VPN issue might be caused by your internet, not the VPN itself." -ForegroundColor Red | |
| $script:results.Tests["Internet_Health"] = "Unhealthy" | |
| return $false | |
| } | |
| } | |
| function Test-GlobalProtect { | |
| Write-SectionHeader "Checking GlobalProtect VPN" 2 | |
| Write-Step "Looking for GlobalProtect on your computer..." | |
| Write-Host "" | |
| # Check GP Service | |
| Write-Progress-Dots "Checking GlobalProtect service" 2 | |
| $gpService = Get-Service -Name "PanGPS" -ErrorAction SilentlyContinue | |
| if ($gpService) { | |
| if ($gpService.Status -eq "Running") { | |
| Write-Status "GlobalProtect Service" "Running" "pass" | |
| $script:results.Tests["GP_Service"] = "Running" | |
| } else { | |
| Write-Status "GlobalProtect Service" "Stopped" "fail" | |
| $script:results.Tests["GP_Service"] = "Stopped" | |
| } | |
| } else { | |
| Write-Status "GlobalProtect Service" "Not Installed" "fail" | |
| $script:results.Tests["GP_Service"] = "Not Installed" | |
| Write-Host "" | |
| Write-Host " ✗ GlobalProtect doesn't appear to be installed." -ForegroundColor Red | |
| Write-Host " Please install GlobalProtect from: https://vpn.du.edu" -ForegroundColor Yellow | |
| return $false | |
| } | |
| # Check GP Adapter (VPN connection) | |
| Write-Progress-Dots "Checking VPN connection" 2 | |
| $gpAdapter = Get-NetAdapter | Where-Object { | |
| $_.InterfaceDescription -like "*PANGP*" -or | |
| $_.InterfaceDescription -like "*GlobalProtect*" | |
| } | Select-Object -First 1 | |
| if ($gpAdapter -and $gpAdapter.Status -eq "Up") { | |
| Write-Status "VPN Connection" "Connected" "pass" | |
| $script:results.Tests["GP_Connected"] = $true | |
| # Get tunnel IP | |
| $gpIP = (Get-NetIPAddress -InterfaceIndex $gpAdapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).IPAddress | |
| if ($gpIP) { | |
| Write-Status "VPN IP Address" $gpIP "info" | |
| $script:results.Tests["GP_IP"] = $gpIP | |
| } | |
| # Get MTU | |
| $gpMTU = (Get-NetIPInterface -InterfaceIndex $gpAdapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).NlMtu | |
| $script:results.Tests["GP_MTU"] = $gpMTU | |
| if ($gpMTU -le 1300) { | |
| Write-Status "VPN Configuration" "Low-MTU mode ($gpMTU)" "pass" | |
| } else { | |
| Write-Status "VPN Configuration" "Standard mode ($gpMTU)" "info" | |
| } | |
| } else { | |
| Write-Status "VPN Connection" "Not Connected" "warn" | |
| $script:results.Tests["GP_Connected"] = $false | |
| Write-Host "" | |
| Write-Host " ⚠ GlobalProtect is installed but not connected." -ForegroundColor Yellow | |
| Write-Host " Please connect to the VPN and run this tool again." -ForegroundColor Yellow | |
| } | |
| # Check connection type from logs | |
| Write-Progress-Dots "Analyzing connection type" 2 | |
| $gpLogPath = "C:\Program Files\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| if (Test-Path $gpLogPath) { | |
| $recentLog = Get-Content $gpLogPath -Tail 100 -ErrorAction SilentlyContinue | Out-String | |
| if ($recentLog -match "IPSec" -and $recentLog -notmatch "failed.*IPSec|IPSec.*failed") { | |
| Write-Status "Connection Type" "IPSec (faster)" "pass" | |
| $script:results.Tests["GP_TunnelType"] = "IPSec" | |
| } elseif ($recentLog -match "SSL") { | |
| Write-Status "Connection Type" "SSL (more compatible)" "info" | |
| $script:results.Tests["GP_TunnelType"] = "SSL" | |
| } else { | |
| Write-Status "Connection Type" "Unknown" "info" | |
| $script:results.Tests["GP_TunnelType"] = "Unknown" | |
| } | |
| } | |
| Write-Host "" | |
| return $script:results.Tests["GP_Connected"] | |
| } | |
| function Test-UniversityAccess { | |
| Write-SectionHeader "Testing Access to University Resources" 3 | |
| Write-Step "Checking if you can reach DU systems..." | |
| Write-Host "" | |
| $score = 0 | |
| $maxScore = 3 | |
| # Test 1: DU DNS | |
| Write-Progress-Dots "Testing DU network lookup" 2 | |
| try { | |
| $duDNS = Resolve-DnsName -Name "du.edu" -Server "130.253.2.14" -DnsOnly -QuickTimeout -ErrorAction Stop | |
| Write-Status "DU DNS Server" "Working" "pass" | |
| $script:results.Tests["DU_DNS"] = $true | |
| $score++ | |
| } catch { | |
| Write-Status "DU DNS Server" "Failed" "fail" | |
| $script:results.Tests["DU_DNS"] = $false | |
| } | |
| # Test 2: DU Website | |
| Write-Progress-Dots "Testing DU website" 2 | |
| $duPing = Test-Connection -ComputerName "du.edu" -Count 2 -ErrorAction SilentlyContinue | |
| if ($duPing) { | |
| $duLatency = [math]::Round(($duPing | Measure-Object -Property ResponseTime -Average).Average, 0) | |
| Write-Status "DU Website (du.edu)" "Reachable ($duLatency ms)" "pass" | |
| $script:results.Tests["DU_Website"] = $true | |
| $script:results.Tests["DU_Latency"] = $duLatency | |
| $score++ | |
| } else { | |
| Write-Status "DU Website (du.edu)" "Not Reachable" "fail" | |
| $script:results.Tests["DU_Website"] = $false | |
| } | |
| # Test 3: Banner (apps28) | |
| Write-Progress-Dots "Testing Banner/Student Services" 3 | |
| try { | |
| # First resolve DNS | |
| $bannerIP = (Resolve-DnsName -Name "apps28.du.edu" -ErrorAction Stop).IPAddress | Select-Object -First 1 | |
| $script:results.Tests["Banner_IP"] = $bannerIP | |
| # Then test connection | |
| $tcp = New-Object System.Net.Sockets.TcpClient | |
| $connect = $tcp.BeginConnect("apps28.du.edu", 8445, $null, $null) | |
| $wait = $connect.AsyncWaitHandle.WaitOne(5000, $false) | |
| if ($wait -and $tcp.Connected) { | |
| Write-Status "Banner/Student Services" "Accessible" "pass" | |
| $script:results.Tests["Banner_Access"] = $true | |
| $score++ | |
| } else { | |
| Write-Status "Banner/Student Services" "Connection Timeout" "fail" | |
| $script:results.Tests["Banner_Access"] = $false | |
| } | |
| $tcp.Close() | |
| } catch { | |
| Write-Status "Banner/Student Services" "Failed" "fail" | |
| $script:results.Tests["Banner_Access"] = $false | |
| } | |
| # Test MTU to DU | |
| Write-Progress-Dots "Checking network path to DU" 2 | |
| $mtuSizes = @(1472, 1450, 1400, 1350, 1300, 1250, 1200, 1100, 1000) | |
| $maxMTU = 0 | |
| foreach ($size in $mtuSizes) { | |
| $pingTest = ping -n 1 -f -l $size login.du.edu 2>&1 | |
| if ($pingTest -match "Reply from" -and $pingTest -notmatch "fragmented") { | |
| $maxMTU = $size | |
| break | |
| } | |
| } | |
| $script:results.Tests["DU_MTU"] = $maxMTU | |
| # Calculate recommended MTU | |
| if ($maxMTU -gt 0) { | |
| $recommendedMTU = [Math]::Floor(($maxMTU - 28) / 10) * 10 | |
| if ($recommendedMTU -lt 1000) { $recommendedMTU = 1000 } | |
| if ($recommendedMTU -gt 1400) { $recommendedMTU = 1400 } | |
| $script:results.Tests["Recommended_MTU"] = $recommendedMTU | |
| } | |
| # Summary | |
| Write-Host "" | |
| $script:results.Tests["DU_Score"] = $score | |
| if ($score -eq $maxScore) { | |
| Write-Host " ✓ All DU resources are accessible!" -ForegroundColor Green | |
| return $true | |
| } elseif ($score -gt 0) { | |
| Write-Host " ⚠ Some DU resources are not accessible." -ForegroundColor Yellow | |
| return $false | |
| } else { | |
| Write-Host " ✗ Cannot reach DU resources." -ForegroundColor Red | |
| return $false | |
| } | |
| } | |
| function Show-Results { | |
| Write-Host "" | |
| Write-Host " ╔════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan | |
| Write-Host " ║ TEST RESULTS ║" -ForegroundColor Cyan | |
| Write-Host " ╚════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan | |
| Write-Host "" | |
| # Internet Health | |
| $inetStatus = $script:results.Tests["Internet_Health"] | |
| $inetIcon = switch ($inetStatus) { "Healthy" { "✓"; "Green" } "Degraded" { "⚠"; "Yellow" } default { "✗"; "Red" } } | |
| Write-Host " $($inetIcon[0]) Internet Connection: " -ForegroundColor $inetIcon[1] -NoNewline | |
| Write-Host $inetStatus -ForegroundColor $inetIcon[1] | |
| # GP Status | |
| $gpConnected = $script:results.Tests["GP_Connected"] | |
| if ($gpConnected) { | |
| Write-Host " ✓ GlobalProtect VPN: " -ForegroundColor Green -NoNewline | |
| Write-Host "Connected ($($script:results.Tests["GP_TunnelType"]) tunnel)" -ForegroundColor Green | |
| } else { | |
| Write-Host " ⚠ GlobalProtect VPN: " -ForegroundColor Yellow -NoNewline | |
| Write-Host "Not Connected" -ForegroundColor Yellow | |
| } | |
| # DU Access | |
| $duScore = $script:results.Tests["DU_Score"] | |
| if ($duScore -eq 3) { | |
| Write-Host " ✓ DU Resources: " -ForegroundColor Green -NoNewline | |
| Write-Host "All accessible" -ForegroundColor Green | |
| } elseif ($duScore -gt 0) { | |
| Write-Host " ⚠ DU Resources: " -ForegroundColor Yellow -NoNewline | |
| Write-Host "Partial access ($duScore/3)" -ForegroundColor Yellow | |
| } else { | |
| Write-Host " ✗ DU Resources: " -ForegroundColor Red -NoNewline | |
| Write-Host "Not accessible" -ForegroundColor Red | |
| } | |
| Write-Host "" | |
| Write-Host " ─────────────────────────────────────────────────────────────" -ForegroundColor Gray | |
| Write-Host "" | |
| # Recommendations | |
| Write-Host " WHAT THIS MEANS:" -ForegroundColor Cyan | |
| Write-Host "" | |
| $hasIssues = $false | |
| # Check for issues and provide friendly explanations | |
| if ($script:results.Tests["Internet_Health"] -eq "Unhealthy") { | |
| $hasIssues = $true | |
| Write-Host " ✗ Your internet connection has problems." -ForegroundColor Red | |
| Write-Host " → Try restarting your router/modem" -ForegroundColor White | |
| Write-Host " → Try connecting to a different network" -ForegroundColor White | |
| Write-Host " → Contact your internet provider if the problem persists" -ForegroundColor White | |
| Write-Host "" | |
| } | |
| if ($script:results.Tests["Internet_PacketLoss"] -gt 10) { | |
| $hasIssues = $true | |
| Write-Host " ✗ Your connection is dropping packets ($($script:results.Tests["Internet_PacketLoss"])% loss)." -ForegroundColor Red | |
| Write-Host " → If on WiFi, try moving closer to your router" -ForegroundColor White | |
| Write-Host " → Try using a wired (ethernet) connection if possible" -ForegroundColor White | |
| Write-Host "" | |
| } | |
| if ($script:results.Tests["ISP_Type"] -eq "Mobile/Restricted") { | |
| $hasIssues = $true | |
| Write-Host " ⚠ You appear to be on a mobile hotspot or restricted network." -ForegroundColor Yellow | |
| Write-Host " → This is OK! But VPN performance may be slower." -ForegroundColor White | |
| Write-Host " → The IT team may need to adjust your VPN settings." -ForegroundColor White | |
| Write-Host "" | |
| } | |
| if (-not $script:results.Tests["GP_Connected"]) { | |
| $hasIssues = $true | |
| Write-Host " ⚠ GlobalProtect VPN is not connected." -ForegroundColor Yellow | |
| Write-Host " → Open GlobalProtect and click Connect" -ForegroundColor White | |
| Write-Host " → Use your DU credentials to sign in" -ForegroundColor White | |
| Write-Host " → Run this tool again after connecting" -ForegroundColor White | |
| Write-Host "" | |
| } | |
| if ($script:results.Tests["Banner_Access"] -eq $false -and $script:results.Tests["GP_Connected"]) { | |
| $hasIssues = $true | |
| Write-Host " ✗ Cannot access Banner/Student Services through VPN." -ForegroundColor Red | |
| Write-Host " → This may be an MTU (network packet size) issue." -ForegroundColor White | |
| Write-Host " → Please share the results file with IT Help Desk." -ForegroundColor White | |
| Write-Host "" | |
| } | |
| if (-not $hasIssues) { | |
| Write-Host " ✓ Everything looks good!" -ForegroundColor Green | |
| Write-Host " Your VPN connection appears to be working correctly." -ForegroundColor White | |
| Write-Host "" | |
| } | |
| # Show recommended MTU if there's an issue | |
| if ($script:results.Tests["Recommended_MTU"] -and $script:results.Tests["GP_MTU"]) { | |
| $currentMTU = $script:results.Tests["GP_MTU"] | |
| $recMTU = $script:results.Tests["Recommended_MTU"] | |
| if ($recMTU -lt $currentMTU -and $script:results.Tests["Banner_Access"] -eq $false) { | |
| Write-Host " ─────────────────────────────────────────────────────────────" -ForegroundColor Gray | |
| Write-Host "" | |
| Write-Host " TECHNICAL INFO FOR IT:" -ForegroundColor Cyan | |
| Write-Host " Current VPN MTU: $currentMTU" -ForegroundColor Gray | |
| Write-Host " Recommended MTU: $recMTU" -ForegroundColor Gray | |
| Write-Host " Path MTU to DU: $($script:results.Tests["DU_MTU"])" -ForegroundColor Gray | |
| Write-Host "" | |
| } | |
| } | |
| } | |
| function Save-Results { | |
| param([switch]$Quiet) | |
| $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" | |
| $filename = "GP-Troubleshoot-$timestamp.txt" | |
| $filepath = "$env:USERPROFILE\Desktop\$filename" | |
| # Create friendly text report | |
| $report = @" | |
| ═══════════════════════════════════════════════════════════════ | |
| GLOBALPROTECT VPN TROUBLESHOOTING REPORT | |
| University of Denver | |
| ═══════════════════════════════════════════════════════════════ | |
| Generated: $(Get-Date -Format "MMMM dd, yyyy 'at' h:mm tt") | |
| Computer: $env:COMPUTERNAME | |
| User: $env:USERNAME | |
| ─────────────────────────────────────────────────────────────── | |
| SUMMARY | |
| ─────────────────────────────────────────────────────────────── | |
| Internet Connection: $($script:results.Tests["Internet_Health"]) | |
| - Latency: $($script:results.Tests["Internet_Latency"]) ms | |
| - Packet Loss: $($script:results.Tests["Internet_PacketLoss"])% | |
| - ISP Type: $($script:results.Tests["ISP_Type"]) | |
| - External MTU: $($script:results.Tests["Internet_MTU"]) bytes | |
| GlobalProtect VPN: $(if ($script:results.Tests["GP_Connected"]) { "Connected" } else { "Not Connected" }) | |
| - Tunnel Type: $($script:results.Tests["GP_TunnelType"]) | |
| - VPN IP: $($script:results.Tests["GP_IP"]) | |
| - VPN MTU: $($script:results.Tests["GP_MTU"]) bytes | |
| DU Resources: $($script:results.Tests["DU_Score"])/3 accessible | |
| - DU DNS: $(if ($script:results.Tests["DU_DNS"]) { "Working" } else { "Failed" }) | |
| - DU Website: $(if ($script:results.Tests["DU_Website"]) { "Reachable" } else { "Failed" }) | |
| - Banner Access: $(if ($script:results.Tests["Banner_Access"]) { "Working" } else { "Failed" }) | |
| - Path MTU to DU: $($script:results.Tests["DU_MTU"]) bytes | |
| ─────────────────────────────────────────────────────────────── | |
| RECOMMENDED SETTINGS | |
| ─────────────────────────────────────────────────────────────── | |
| Recommended VPN MTU: $($script:results.Tests["Recommended_MTU"]) bytes | |
| If IT needs to adjust settings: | |
| .\GP-Troubleshoot.ps1 -SetMTU $($script:results.Tests["Recommended_MTU"]) | |
| ─────────────────────────────────────────────────────────────── | |
| Please share this file with IT Help Desk if you need assistance. | |
| Email: [email protected] | Phone: 303-871-4700 | |
| ═══════════════════════════════════════════════════════════════ | |
| "@ | |
| $report | Out-File $filepath -Encoding UTF8 | |
| # Also save JSON for technical analysis | |
| $jsonPath = "$env:USERPROFILE\Desktop\GP-Troubleshoot-$timestamp.json" | |
| $script:results | ConvertTo-Json -Depth 5 | Out-File $jsonPath -Encoding UTF8 | |
| if (-not $Quiet) { | |
| Write-Host "" | |
| Write-Host " ✓ Results saved to your Desktop:" -ForegroundColor Green | |
| Write-Host " $filename" -ForegroundColor White | |
| Write-Host "" | |
| } | |
| return $filepath | |
| } | |
| function Try-AutoFix { | |
| Write-Host "" | |
| Write-Host " ╔════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan | |
| Write-Host " ║ ATTEMPTING FIX ║" -ForegroundColor Cyan | |
| Write-Host " ╚════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan | |
| Write-Host "" | |
| if (-not $isAdmin) { | |
| Write-Host " ⚠ Some fixes require Administrator access." -ForegroundColor Yellow | |
| Write-Host " Right-click PowerShell and select 'Run as Administrator'" -ForegroundColor White | |
| Write-Host " Then run this tool again." -ForegroundColor White | |
| Write-Host "" | |
| return | |
| } | |
| # Try DNS flush | |
| Write-Step "Clearing DNS cache..." | |
| ipconfig /flushdns | Out-Null | |
| Clear-DnsClientCache -ErrorAction SilentlyContinue | |
| Write-Host " ✓ DNS cache cleared" -ForegroundColor Green | |
| # Try MTU adjustment if we have a recommendation | |
| if ($script:results.Tests["Recommended_MTU"] -and $script:results.Tests["GP_MTU"]) { | |
| $currentMTU = $script:results.Tests["GP_MTU"] | |
| $recMTU = $script:results.Tests["Recommended_MTU"] | |
| if ($recMTU -lt $currentMTU) { | |
| Write-Host "" | |
| Write-Step "Adjusting VPN MTU from $currentMTU to $recMTU..." | |
| $gpAdapter = Get-NetAdapter | Where-Object { | |
| $_.InterfaceDescription -like "*PANGP*" -or | |
| $_.InterfaceDescription -like "*GlobalProtect*" | |
| } | Where-Object Status -eq "Up" | Select-Object -First 1 | |
| if ($gpAdapter) { | |
| try { | |
| Set-NetIPInterface -InterfaceIndex $gpAdapter.ifIndex -AddressFamily IPv4 -NlMtu $recMTU -ErrorAction Stop | |
| Write-Host " ✓ VPN MTU adjusted to $recMTU" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host " Please try accessing Banner/DU resources again." -ForegroundColor Cyan | |
| Write-Host " Note: This change is temporary and will reset when VPN reconnects." -ForegroundColor Gray | |
| } catch { | |
| Write-Host " ✗ Could not adjust MTU: $($_.Exception.Message)" -ForegroundColor Red | |
| } | |
| } | |
| } | |
| } | |
| Write-Host "" | |
| } | |
| function Upload-Results { | |
| Write-Host "" | |
| Write-Host " ╔════════════════════════════════════════════════════════════╗" -ForegroundColor Cyan | |
| Write-Host " ║ SHARE RESULTS ║" -ForegroundColor Cyan | |
| Write-Host " ╚════════════════════════════════════════════════════════════╝" -ForegroundColor Cyan | |
| Write-Host "" | |
| $token = $env:GITHUB_TOKEN | |
| if (-not $token) { | |
| Write-Host " To automatically share results, IT needs to configure a token." -ForegroundColor Yellow | |
| Write-Host "" | |
| Write-Host " Instead, please:" -ForegroundColor White | |
| Write-Host " 1. Find the results file on your Desktop" -ForegroundColor White | |
| Write-Host " 2. Email it to [email protected]" -ForegroundColor White | |
| Write-Host " 3. Or bring your laptop to the IT Help Desk" -ForegroundColor White | |
| Write-Host "" | |
| return | |
| } | |
| Write-Step "Uploading results..." | |
| try { | |
| $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm" | |
| $jsonContent = $script:results | ConvertTo-Json -Depth 5 | |
| $summaryContent = @" | |
| GP Troubleshooting - $($script:results.Hostname) - $timestamp | |
| User: $($script:results.Username) | |
| Internet: $($script:results.Tests["Internet_Health"]) (Latency: $($script:results.Tests["Internet_Latency"])ms, Loss: $($script:results.Tests["Internet_PacketLoss"])%) | |
| ISP Type: $($script:results.Tests["ISP_Type"]) | |
| GP Connected: $($script:results.Tests["GP_Connected"]) | |
| GP Tunnel: $($script:results.Tests["GP_TunnelType"]) | |
| GP MTU: $($script:results.Tests["GP_MTU"]) | |
| Banner Access: $($script:results.Tests["Banner_Access"]) | |
| Recommended MTU: $($script:results.Tests["Recommended_MTU"]) | |
| "@ | |
| $gistBody = @{ | |
| description = "GP Troubleshooting - $($script:results.Hostname) - $timestamp" | |
| public = $false | |
| files = @{ | |
| "summary.txt" = @{ content = $summaryContent } | |
| "full-results.json" = @{ content = $jsonContent } | |
| } | |
| } | ConvertTo-Json -Depth 5 | |
| $response = Invoke-RestMethod -Uri "https://api.github.com/gists" ` | |
| -Method Post ` | |
| -Headers @{ Authorization = "token $token"; Accept = "application/vnd.github.v3+json" } ` | |
| -Body $gistBody ` | |
| -ContentType "application/json" | |
| Write-Host "" | |
| Write-Host " ✓ Results uploaded successfully!" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host " Share this link with IT:" -ForegroundColor Cyan | |
| Write-Host " $($response.html_url)" -ForegroundColor White | |
| Write-Host "" | |
| # Copy to clipboard | |
| $response.html_url | Set-Clipboard | |
| Write-Host " (Link copied to clipboard)" -ForegroundColor Gray | |
| } catch { | |
| Write-Host " ✗ Upload failed: $($_.Exception.Message)" -ForegroundColor Red | |
| Write-Host " Please email the results file from your Desktop instead." -ForegroundColor Yellow | |
| } | |
| Write-Host "" | |
| } | |
| # ============================================================ | |
| # MAIN MENU | |
| # ============================================================ | |
| function Show-MainMenu { | |
| while ($true) { | |
| Show-Banner | |
| Write-Host " What would you like to do?" -ForegroundColor Cyan | |
| Write-Host "" | |
| Write-Host " [1] Run Full Diagnostic" -ForegroundColor White | |
| Write-Host " (Recommended - checks everything)" -ForegroundColor Gray | |
| Write-Host "" | |
| Write-Host " [2] Quick Internet Check" -ForegroundColor White | |
| Write-Host " (Just test your internet connection)" -ForegroundColor Gray | |
| Write-Host "" | |
| Write-Host " [3] Try Auto-Fix" -ForegroundColor White | |
| Write-Host " (Attempt to fix common issues)" -ForegroundColor Gray | |
| Write-Host "" | |
| Write-Host " [4] Save Results to Desktop" -ForegroundColor White | |
| Write-Host " (Create a file to share with IT)" -ForegroundColor Gray | |
| Write-Host "" | |
| Write-Host " [5] Exit" -ForegroundColor White | |
| Write-Host "" | |
| $choice = Read-Host " Enter your choice (1-5)" | |
| switch ($choice) { | |
| "1" { | |
| Show-Banner | |
| $internetOK = Test-InternetHealth | |
| Pause-ForUser | |
| Show-Banner | |
| $gpOK = Test-GlobalProtect | |
| Pause-ForUser | |
| if ($script:results.Tests["GP_Connected"]) { | |
| Show-Banner | |
| $duOK = Test-UniversityAccess | |
| Pause-ForUser | |
| } | |
| Show-Banner | |
| Show-Results | |
| Write-Host "" | |
| Write-Host " ─────────────────────────────────────────────────────────────" -ForegroundColor Gray | |
| $saveChoice = Read-Host " Would you like to save these results? (Y/n)" | |
| if ($saveChoice -notmatch "^[Nn]") { | |
| $filepath = Save-Results | |
| } | |
| Pause-ForUser "Press any key to return to menu..." | |
| } | |
| "2" { | |
| Show-Banner | |
| Test-InternetHealth | |
| Pause-ForUser "Press any key to return to menu..." | |
| } | |
| "3" { | |
| Show-Banner | |
| if (-not $script:results.Tests["Internet_Health"]) { | |
| Write-Host " Please run a diagnostic first (Option 1)." -ForegroundColor Yellow | |
| } else { | |
| Try-AutoFix | |
| } | |
| Pause-ForUser "Press any key to return to menu..." | |
| } | |
| "4" { | |
| Show-Banner | |
| if (-not $script:results.Tests["Internet_Health"]) { | |
| Write-Host " Please run a diagnostic first (Option 1)." -ForegroundColor Yellow | |
| } else { | |
| Save-Results | |
| $uploadChoice = Read-Host " Would you like to upload results for IT? (y/N)" | |
| if ($uploadChoice -match "^[Yy]") { | |
| Upload-Results | |
| } | |
| } | |
| Pause-ForUser "Press any key to return to menu..." | |
| } | |
| "5" { | |
| Show-Banner | |
| Write-Host " Thank you for using the GlobalProtect Troubleshooting Tool!" -ForegroundColor Cyan | |
| Write-Host "" | |
| Write-Host " If you need help:" -ForegroundColor White | |
| Write-Host " Email: [email protected]" -ForegroundColor Gray | |
| Write-Host " Phone: 303-871-4700" -ForegroundColor Gray | |
| Write-Host " Visit: Anderson Academic Commons, Level 1" -ForegroundColor Gray | |
| Write-Host "" | |
| exit | |
| } | |
| default { | |
| Write-Host " Please enter a number between 1 and 5." -ForegroundColor Yellow | |
| Start-Sleep -Seconds 1 | |
| } | |
| } | |
| } | |
| } | |
| # ============================================================ | |
| # START | |
| # ============================================================ | |
| # Check for command-line parameters for IT use | |
| if ($args -contains "-auto" -or $args -contains "--auto") { | |
| # Silent mode - run all tests and save | |
| Show-Banner | |
| Test-InternetHealth | Out-Null | |
| Test-GlobalProtect | Out-Null | |
| if ($script:results.Tests["GP_Connected"]) { | |
| Test-UniversityAccess | Out-Null | |
| } | |
| Show-Results | |
| $filepath = Save-Results -Quiet | |
| Write-Host " Results saved to: $filepath" -ForegroundColor Cyan | |
| exit | |
| } | |
| # Interactive mode | |
| Show-MainMenu |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| GlobalProtect IPSec/MTU Troubleshooting Script | |
| .DESCRIPTION | |
| Diagnoses connectivity issues with GlobalProtect VPN, focusing on | |
| MTU/fragmentation problems common with IPSec tunnels. | |
| Common culprits: CenturyLink/PPPoE (MTU 1492), T-Mobile, hotel networks, | |
| or any ISP with underlying tunneling that reduces effective MTU. | |
| .PARAMETER ResetWinsock | |
| Resets Winsock catalog to clean state. Requires restart. | |
| .PARAMETER ResetNetworkStack | |
| Full network stack reset (Winsock + IP + DNS cache). Requires restart. | |
| .PARAMETER ResetDNSOnly | |
| Flushes DNS cache only. No restart required. | |
| .PARAMETER CollectLogs | |
| Copies all GP logs to desktop for analysis. | |
| .PARAMETER GPGateway | |
| GlobalProtect gateway IP/hostname for UDP 4501 IPSec test. | |
| .PARAMETER BannerHost | |
| Banner ERP server hostname (default: apps28.du.edu) | |
| .PARAMETER BannerPort | |
| Banner ERP port (default: 8445) | |
| .PARAMETER SetMTU | |
| Manually set GlobalProtect adapter MTU to specified value. | |
| .PARAMETER AutoMTU | |
| Automatically calculate and set optimal MTU based on path discovery. | |
| .PARAMETER RestoreMTU | |
| Restore GlobalProtect adapter MTU to default (1400). | |
| .PARAMETER Force | |
| Skip confirmation prompts for MTU changes. | |
| .PARAMETER UploadResults | |
| Upload results to a new GitHub Gist for sharing/review. | |
| .PARAMETER GitHubToken | |
| GitHub personal access token with gist scope. Can also use GITHUB_TOKEN env var. | |
| .NOTES | |
| University of Denver - Information Security | |
| Run as Administrator for full functionality and reset options. | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| [string[]]$TestHosts = @("login.du.edu", "du.edu"), | |
| [string]$InternalDNS = "130.253.2.14", | |
| [string]$GPGateway = "", | |
| [string]$BannerHost = "apps28.du.edu", | |
| [int]$BannerPort = 8445, | |
| [switch]$ExportResults, | |
| [switch]$ResetWinsock, | |
| [switch]$ResetNetworkStack, | |
| [switch]$ResetDNSOnly, | |
| [switch]$CollectLogs, | |
| [int]$SetMTU = 0, | |
| [switch]$AutoMTU, | |
| [switch]$RestoreMTU, | |
| [switch]$Force, | |
| [switch]$UploadResults, | |
| [string]$GitHubToken = $env:GITHUB_TOKEN | |
| ) | |
| # Colors for output | |
| $colors = @{ | |
| Success = "Green" | |
| Failure = "Red" | |
| Warning = "Yellow" | |
| Info = "Cyan" | |
| } | |
| # ============================================================ | |
| # MTU Adjustment Function | |
| # ============================================================ | |
| function Set-GPAdapterMTU { | |
| param( | |
| [int]$NewMTU, | |
| [int]$CurrentMTU, | |
| [string]$AdapterName, | |
| [int]$InterfaceIndex, | |
| [switch]$ForceChange | |
| ) | |
| # Validate MTU range | |
| if ($NewMTU -lt 576 -or $NewMTU -gt 1500) { | |
| Write-Host "ERROR: MTU must be between 576 and 1500" -ForegroundColor Red | |
| return $false | |
| } | |
| # Check if already set | |
| if ($NewMTU -eq $CurrentMTU) { | |
| Write-Host "MTU is already set to $NewMTU" -ForegroundColor Cyan | |
| return $true | |
| } | |
| # Show what we're about to do | |
| Write-Host "`n" -NoNewline | |
| Write-Host ("="*60) -ForegroundColor Yellow | |
| Write-Host " MTU CHANGE REQUEST" -ForegroundColor Yellow | |
| Write-Host ("="*60) -ForegroundColor Yellow | |
| Write-Host " Adapter: $AdapterName (Index: $InterfaceIndex)" | |
| Write-Host " Current MTU: $CurrentMTU" | |
| Write-Host " New MTU: $NewMTU" | |
| $changeDir = if ($NewMTU -lt $CurrentMTU) { "Decrease" } else { "Increase" } | |
| Write-Host " Change: $changeDir by $([Math]::Abs($NewMTU - $CurrentMTU)) bytes" | |
| Write-Host ("="*60) -ForegroundColor Yellow | |
| # Warn about implications | |
| if ($NewMTU -lt 1280) { | |
| Write-Host "`n[!] WARNING: MTU below 1280 may cause issues with IPv6" -ForegroundColor Yellow | |
| } | |
| if ($NewMTU -gt $CurrentMTU) { | |
| Write-Host "`n[!] WARNING: Increasing MTU may cause fragmentation issues" -ForegroundColor Yellow | |
| } | |
| Write-Host "`n[i] This change is temporary and will reset when GP reconnects" -ForegroundColor Gray | |
| Write-Host " or when the adapter is restarted." -ForegroundColor Gray | |
| # Confirm unless -Force | |
| if (-not $ForceChange) { | |
| $confirm = Read-Host "`nProceed with MTU change? (y/N)" | |
| if ($confirm -notmatch "^[Yy]") { | |
| Write-Host "MTU change cancelled." -ForegroundColor Cyan | |
| return $false | |
| } | |
| } | |
| # Check for admin rights | |
| $adminCheck = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) | |
| if (-not $adminCheck) { | |
| Write-Host "ERROR: Administrator privileges required to change MTU" -ForegroundColor Red | |
| Write-Host " Please run PowerShell as Administrator" -ForegroundColor Gray | |
| return $false | |
| } | |
| # Apply the change | |
| try { | |
| Write-Host "`nApplying MTU change..." -ForegroundColor Cyan | |
| # Set for both IPv4 and IPv6 | |
| Set-NetIPInterface -InterfaceIndex $InterfaceIndex -AddressFamily IPv4 -NlMtu $NewMTU -ErrorAction Stop | |
| Set-NetIPInterface -InterfaceIndex $InterfaceIndex -AddressFamily IPv6 -NlMtu $NewMTU -ErrorAction SilentlyContinue | |
| # Verify the change | |
| Start-Sleep -Milliseconds 500 | |
| $verifyMTU = (Get-NetIPInterface -InterfaceIndex $InterfaceIndex -AddressFamily IPv4).NlMtu | |
| if ($verifyMTU -eq $NewMTU) { | |
| Write-Host "SUCCESS: MTU changed from $CurrentMTU to $NewMTU" -ForegroundColor Green | |
| Write-Host "`n[i] Test connectivity now. If issues occur, reconnect GP to restore default." -ForegroundColor Gray | |
| return $true | |
| } else { | |
| Write-Host "WARNING: MTU change may not have applied correctly (showing $verifyMTU)" -ForegroundColor Yellow | |
| return $false | |
| } | |
| } catch { | |
| Write-Host "ERROR: Failed to change MTU - $($_.Exception.Message)" -ForegroundColor Red | |
| return $false | |
| } | |
| } | |
| # ============================================================ | |
| # ADMIN CHECK & NETWORK RESETS | |
| # ============================================================ | |
| $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) | |
| # Handle reset operations first (if requested) | |
| if ($ResetDNSOnly) { | |
| Write-Host "`n[DNS CACHE FLUSH]" -ForegroundColor Cyan | |
| Write-Host "-"*40 | |
| ipconfig /flushdns | |
| Clear-DnsClientCache | |
| Write-Host "`nDNS cache flushed. No restart required." -ForegroundColor Green | |
| Write-Host "Continuing with diagnostics...`n" -ForegroundColor Gray | |
| } | |
| if ($ResetWinsock -or $ResetNetworkStack) { | |
| if (-not $isAdmin) { | |
| Write-Host "`n[ERROR] Network reset operations require Administrator privileges." -ForegroundColor Red | |
| Write-Host "Please re-run PowerShell as Administrator and try again." -ForegroundColor Yellow | |
| Write-Host "`nTo run as admin: Right-click PowerShell > 'Run as administrator'`n" -ForegroundColor Gray | |
| exit 1 | |
| } | |
| Write-Host "`n" + "="*60 -ForegroundColor Yellow | |
| Write-Host " NETWORK STACK RESET OPERATIONS" -ForegroundColor Yellow | |
| Write-Host "="*60 -ForegroundColor Yellow | |
| Write-Host "`nWARNING: These operations will require a system restart." -ForegroundColor Yellow | |
| Write-Host "Press Ctrl+C within 10 seconds to cancel...`n" -ForegroundColor Yellow | |
| Start-Sleep -Seconds 10 | |
| Write-Host "[1/6] Flushing DNS cache..." -ForegroundColor Cyan | |
| ipconfig /flushdns | Out-Null | |
| Clear-DnsClientCache | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[2/6] Releasing IP address..." -ForegroundColor Cyan | |
| ipconfig /release | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[3/6] Resetting Winsock catalog..." -ForegroundColor Cyan | |
| netsh winsock reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| if ($ResetNetworkStack) { | |
| Write-Host "[4/6] Resetting TCP/IP stack..." -ForegroundColor Cyan | |
| netsh int ip reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[5/6] Resetting IPv6 stack..." -ForegroundColor Cyan | |
| netsh int ipv6 reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[6/6] Resetting Winsock2 registry keys..." -ForegroundColor Cyan | |
| netsh int tcp reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| } else { | |
| Write-Host "[4-6] Skipped (use -ResetNetworkStack for full reset)" -ForegroundColor DarkGray | |
| } | |
| Write-Host "`n" + "="*60 -ForegroundColor Green | |
| Write-Host " RESET COMPLETE" -ForegroundColor Green | |
| Write-Host "="*60 -ForegroundColor Green | |
| $restart = Read-Host "`nRestart now? (Y/N)" | |
| if ($restart -eq 'Y' -or $restart -eq 'y') { | |
| Write-Host "Restarting in 5 seconds..." -ForegroundColor Yellow | |
| Start-Sleep -Seconds 5 | |
| Restart-Computer -Force | |
| } else { | |
| Write-Host "`nPlease restart the computer manually before testing connectivity." -ForegroundColor Yellow | |
| Write-Host "Run this script again after restart (without reset flags) to diagnose.`n" -ForegroundColor Gray | |
| exit 0 | |
| } | |
| } | |
| # Check admin status for informational purposes | |
| if (-not $isAdmin) { | |
| Write-Host "[Note] Running without admin rights - some tests may be limited" -ForegroundColor DarkYellow | |
| Write-Host " Reset options require admin: -ResetWinsock, -ResetNetworkStack`n" -ForegroundColor DarkGray | |
| } | |
| # ============================================================ | |
| # LOG COLLECTION (if requested) | |
| # ============================================================ | |
| if ($CollectLogs) { | |
| Write-Host "`n[LOG COLLECTION]" -ForegroundColor Cyan | |
| Write-Host "-"*40 | |
| $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" | |
| $logDest = "$env:USERPROFILE\Desktop\GP-Logs-$timestamp" | |
| New-Item -ItemType Directory -Path $logDest -Force | Out-Null | |
| # GP Installation directory logs | |
| $gpInstallPath = "C:\Program Files\Palo Alto Networks\GlobalProtect" | |
| if (Test-Path $gpInstallPath) { | |
| Write-Host "Copying logs from installation directory..." -ForegroundColor Gray | |
| Copy-Item "$gpInstallPath\*.log" $logDest -ErrorAction SilentlyContinue | |
| } | |
| # GP User AppData logs | |
| $gpUserPath = "$env:LOCALAPPDATA\Palo Alto Networks\GlobalProtect" | |
| if (Test-Path $gpUserPath) { | |
| Write-Host "Copying logs from user AppData..." -ForegroundColor Gray | |
| Copy-Item "$gpUserPath\*.log" $logDest -ErrorAction SilentlyContinue | |
| Copy-Item "$gpUserPath\*.dat" $logDest -ErrorAction SilentlyContinue | |
| } | |
| # Network diagnostics | |
| Write-Host "Capturing network diagnostics..." -ForegroundColor Gray | |
| ipconfig /all > "$logDest\ipconfig.txt" | |
| route print > "$logDest\routes.txt" | |
| netsh interface ipv4 show subinterfaces > "$logDest\mtu-interfaces.txt" | |
| netsh interface ipv4 show destinationcache > "$logDest\mtu-destinationcache.txt" | |
| netsh winsock show catalog > "$logDest\winsock-catalog.txt" | |
| netsh interface tcp show global > "$logDest\tcp-global.txt" | |
| Get-NetAdapter | Format-List * > "$logDest\adapters.txt" | |
| Get-NetAdapterStatistics | Format-List * > "$logDest\adapter-stats.txt" 2>$null | |
| Get-Service | Where-Object { $_.Name -match "Dnscache|Dhcp|NlaSvc|netprofm|Netman|PanGPS" } | Format-List * > "$logDest\services.txt" | |
| Write-Host "`nLogs collected to: $logDest" -ForegroundColor Green | |
| Write-Host "Continuing with diagnostics...`n" -ForegroundColor Gray | |
| } | |
| function Write-Status { | |
| param([string]$Message, [string]$Status, [string]$Color = "White") | |
| $timestamp = Get-Date -Format "HH:mm:ss" | |
| Write-Host "[$timestamp] " -NoNewline -ForegroundColor DarkGray | |
| Write-Host "$Message " -NoNewline | |
| if ($Status) { | |
| Write-Host "[$Status]" -ForegroundColor $Color | |
| } else { | |
| Write-Host "" | |
| } | |
| } | |
| function Get-MTUSize { | |
| param([string]$Target, [int]$StartSize = 1500, [int]$MinSize = 1200) | |
| $size = $StartSize | |
| $lastGood = $MinSize | |
| while ($size -ge $MinSize) { | |
| $result = ping -n 1 -f -l $size $Target 2>&1 | |
| if ($result -match "Reply from" -and $result -notmatch "fragmented") { | |
| $lastGood = $size | |
| break | |
| } | |
| $size -= 10 | |
| } | |
| return $lastGood | |
| } | |
| # Banner | |
| Write-Host "`n" + "="*60 -ForegroundColor $colors.Info | |
| Write-Host " GlobalProtect VPN Troubleshooting Script" -ForegroundColor $colors.Info | |
| Write-Host " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor DarkGray | |
| Write-Host "="*60 + "`n" -ForegroundColor $colors.Info | |
| $results = [ordered]@{ | |
| Timestamp = Get-Date -Format "o" | |
| Hostname = $env:COMPUTERNAME | |
| Username = $env:USERNAME | |
| Tests = @{} | |
| } | |
| # ============================================================ | |
| # SECTION 0: Local Internet Health (Pre-VPN) | |
| # ============================================================ | |
| Write-Host "[0] LOCAL INTERNET HEALTH CHECK" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| Write-Host "Testing connectivity independent of VPN..." -ForegroundColor Gray | |
| # Check if GP is connected (to contextualize results) | |
| $gpAdapter = Get-NetAdapter | Where-Object { | |
| $_.InterfaceDescription -like "*PANGP*" -or | |
| $_.InterfaceDescription -like "*GlobalProtect*" | |
| } | Where-Object Status -eq "Up" | Select-Object -First 1 | |
| $gpConnected = $null -ne $gpAdapter | |
| Write-Status "GlobalProtect VPN" $(if ($gpConnected) { "Connected (results go through tunnel)" } else { "Not connected (testing raw internet)" }) $(if ($gpConnected) { $colors.Info } else { $colors.Success }) | |
| $results.Tests["GP_Connected_During_Test"] = $gpConnected | |
| # Test public DNS servers | |
| Write-Host "`nPublic DNS Resolution:" -ForegroundColor White | |
| $publicDNS = @( | |
| @{ Server = "8.8.8.8"; Name = "Google DNS" }, | |
| @{ Server = "1.1.1.1"; Name = "Cloudflare DNS" }, | |
| @{ Server = "9.9.9.9"; Name = "Quad9 DNS" } | |
| ) | |
| foreach ($dns in $publicDNS) { | |
| try { | |
| $dnsResult = Resolve-DnsName -Name "google.com" -Server $dns.Server -DnsOnly -QuickTimeout -ErrorAction Stop | |
| Write-Status " $($dns.Name) ($($dns.Server))" "Working" $colors.Success | |
| $results.Tests["PublicDNS_$($dns.Server)"] = $true | |
| } catch { | |
| Write-Status " $($dns.Name) ($($dns.Server))" "Failed" $colors.Failure | |
| $results.Tests["PublicDNS_$($dns.Server)"] = $false | |
| } | |
| } | |
| # Test connectivity to major external sites | |
| Write-Host "`nExternal Site Connectivity:" -ForegroundColor White | |
| $externalSites = @( | |
| @{ Host = "www.google.com"; Name = "Google" }, | |
| @{ Host = "www.microsoft.com"; Name = "Microsoft" }, | |
| @{ Host = "www.cloudflare.com"; Name = "Cloudflare" } | |
| ) | |
| $externalFailures = 0 | |
| foreach ($site in $externalSites) { | |
| try { | |
| $tcp = New-Object System.Net.Sockets.TcpClient | |
| $connect = $tcp.BeginConnect($site.Host, 443, $null, $null) | |
| $wait = $connect.AsyncWaitHandle.WaitOne(3000, $false) | |
| if ($wait -and $tcp.Connected) { | |
| $tcp.EndConnect($connect) | |
| Write-Status " $($site.Name)" "Reachable" $colors.Success | |
| $results.Tests["External_$($site.Name)"] = $true | |
| } else { | |
| Write-Status " $($site.Name)" "Timeout" $colors.Failure | |
| $results.Tests["External_$($site.Name)"] = $false | |
| $externalFailures++ | |
| } | |
| $tcp.Close() | |
| } catch { | |
| Write-Status " $($site.Name)" "Failed" $colors.Failure | |
| $results.Tests["External_$($site.Name)"] = $false | |
| $externalFailures++ | |
| } | |
| } | |
| # Latency check to external host | |
| Write-Host "`nInternet Latency:" -ForegroundColor White | |
| $pingTargets = @("8.8.8.8", "1.1.1.1") | |
| $latencies = @() | |
| foreach ($target in $pingTargets) { | |
| $pingResult = Test-Connection -ComputerName $target -Count 3 -ErrorAction SilentlyContinue | |
| if ($pingResult) { | |
| $avgLatency = [math]::Round(($pingResult | Measure-Object -Property ResponseTime -Average).Average, 1) | |
| $latencies += $avgLatency | |
| $latencyColor = if ($avgLatency -lt 50) { $colors.Success } | |
| elseif ($avgLatency -lt 100) { $colors.Warning } | |
| else { $colors.Failure } | |
| Write-Status " Ping to $target" "$avgLatency ms avg" $latencyColor | |
| $results.Tests["Latency_$target"] = $avgLatency | |
| } else { | |
| Write-Status " Ping to $target" "Failed" $colors.Failure | |
| $results.Tests["Latency_$target"] = -1 | |
| } | |
| } | |
| if ($latencies.Count -gt 0) { | |
| $avgOverall = [math]::Round(($latencies | Measure-Object -Average).Average, 1) | |
| $results.Tests["Latency_Average"] = $avgOverall | |
| } | |
| # Packet loss check | |
| Write-Host "`nPacket Loss Test:" -ForegroundColor White | |
| $lossTest = Test-Connection -ComputerName "8.8.8.8" -Count 10 -ErrorAction SilentlyContinue | |
| if ($lossTest) { | |
| $received = $lossTest.Count | |
| $lossPercent = [math]::Round((10 - $received) / 10 * 100, 0) | |
| $lossColor = if ($lossPercent -eq 0) { $colors.Success } | |
| elseif ($lossPercent -le 10) { $colors.Warning } | |
| else { $colors.Failure } | |
| Write-Status " Packet loss (10 pings)" "$lossPercent%" $lossColor | |
| $results.Tests["Packet_Loss_Percent"] = $lossPercent | |
| } else { | |
| Write-Status " Packet loss test" "Failed - no responses" $colors.Failure | |
| $results.Tests["Packet_Loss_Percent"] = 100 | |
| } | |
| # External MTU path discovery (to compare with university path) | |
| Write-Host "`nExternal MTU Path (to 8.8.8.8):" -ForegroundColor White | |
| $extMtuSizes = @(1472, 1450, 1400, 1350, 1300, 1250, 1200) | |
| $extMaxWorking = 0 | |
| foreach ($size in $extMtuSizes) { | |
| $pingTest = ping -n 1 -f -l $size 8.8.8.8 2>&1 | |
| if ($pingTest -match "Reply from" -and $pingTest -notmatch "fragmented") { | |
| $extMaxWorking = $size | |
| break | |
| } | |
| } | |
| if ($extMaxWorking -gt 0) { | |
| $extMtuColor = if ($extMaxWorking -ge 1472) { $colors.Success } | |
| elseif ($extMaxWorking -ge 1400) { $colors.Warning } | |
| else { $colors.Failure } | |
| Write-Status " Max payload without fragmentation" "$extMaxWorking bytes" $extMtuColor | |
| $results.Tests["External_Max_MTU"] = $extMaxWorking | |
| # Infer ISP type | |
| if ($extMaxWorking -ge 1464 -and $extMaxWorking -le 1472) { | |
| Write-Host " [i] Normal MTU - standard ethernet/cable" -ForegroundColor Gray | |
| $results.Tests["ISP_Type_Inferred"] = "Standard" | |
| } elseif ($extMaxWorking -ge 1452 -and $extMaxWorking -lt 1464) { | |
| Write-Host " [!] Slightly reduced MTU - possible PPPoE or light tunneling" -ForegroundColor Yellow | |
| $results.Tests["ISP_Type_Inferred"] = "PPPoE_Light" | |
| } elseif ($extMaxWorking -lt 1452 -and $extMaxWorking -ge 1400) { | |
| Write-Host " [!] Reduced MTU - likely PPPoE (DSL/CenturyLink) or carrier tunnel" -ForegroundColor Yellow | |
| $results.Tests["ISP_Type_Inferred"] = "PPPoE" | |
| } elseif ($extMaxWorking -lt 1400) { | |
| Write-Host " [!] Severely constrained MTU - mobile hotspot, CGNAT, or double-tunnel" -ForegroundColor Red | |
| $results.Tests["ISP_Type_Inferred"] = "Mobile_Or_Tunnel" | |
| } | |
| } else { | |
| Write-Status " Max payload without fragmentation" "Could not determine (all sizes failed)" $colors.Failure | |
| $results.Tests["External_Max_MTU"] = 0 | |
| } | |
| # Summary of local internet health | |
| Write-Host "`nLocal Internet Summary:" -ForegroundColor White | |
| $dnsOK = ($results.Tests["PublicDNS_8.8.8.8"] -or $results.Tests["PublicDNS_1.1.1.1"]) | |
| $connectOK = $externalFailures -lt 2 | |
| $latencyOK = $results.Tests["Latency_Average"] -and $results.Tests["Latency_Average"] -lt 100 | |
| $lossOK = $results.Tests["Packet_Loss_Percent"] -le 5 | |
| if ($dnsOK -and $connectOK -and $latencyOK -and $lossOK) { | |
| Write-Host " [OK] Local internet connection appears healthy" -ForegroundColor $colors.Success | |
| $results.Tests["Local_Internet_Health"] = "Healthy" | |
| } elseif ($dnsOK -and $connectOK) { | |
| Write-Host " [WARN] Internet working but with latency/loss issues" -ForegroundColor $colors.Warning | |
| $results.Tests["Local_Internet_Health"] = "Degraded" | |
| } else { | |
| Write-Host " [FAIL] Local internet connection has issues - problem is NOT just VPN" -ForegroundColor $colors.Failure | |
| $results.Tests["Local_Internet_Health"] = "Unhealthy" | |
| } | |
| # ============================================================ | |
| # SECTION 1: GlobalProtect Status | |
| # ============================================================ | |
| Write-Host "`n[1] GLOBALPROTECT STATUS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Check GP Service | |
| $gpService = Get-Service -Name "PanGPS" -ErrorAction SilentlyContinue | |
| if ($gpService) { | |
| $status = if ($gpService.Status -eq "Running") { "Running"; "Success" } else { "Stopped"; "Failure" } | |
| Write-Status "GlobalProtect Service (PanGPS)" $gpService.Status $colors[$status[1]] | |
| $results.Tests["GP_Service"] = $gpService.Status | |
| } else { | |
| Write-Status "GlobalProtect Service" "NOT FOUND" $colors.Failure | |
| } | |
| # Check GP Network Adapter | |
| $gpAdapter = Get-NetAdapter | Where-Object { $_.InterfaceDescription -like "*PANGP*" -or $_.Name -like "*GlobalProtect*" } | |
| if ($gpAdapter) { | |
| Write-Status "GP Virtual Adapter" $gpAdapter.Status $colors.Success | |
| Write-Status " Adapter Name" $gpAdapter.Name "White" | |
| Write-Status " Link Speed" $gpAdapter.LinkSpeed "White" | |
| # Get adapter MTU | |
| $adapterMTU = (Get-NetIPInterface -InterfaceIndex $gpAdapter.ifIndex -ErrorAction SilentlyContinue).NlMtu | Select-Object -First 1 | |
| Write-Status " Adapter MTU" $adapterMTU $(if ($adapterMTU -lt 1400) { $colors.Warning } else { "White" }) | |
| $results.Tests["GP_Adapter_MTU"] = $adapterMTU | |
| # Get adapter IP | |
| $gpIP = (Get-NetIPAddress -InterfaceIndex $gpAdapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).IPAddress | |
| if ($gpIP) { | |
| Write-Status " Tunnel IP" $gpIP "White" | |
| $results.Tests["GP_Tunnel_IP"] = $gpIP | |
| } | |
| } else { | |
| Write-Status "GP Virtual Adapter" "NOT FOUND (not connected?)" $colors.Warning | |
| } | |
| # Check for SSL-Only preference | |
| $gpUserPrefs = "$env:LOCALAPPDATA\Palo Alto Networks\GlobalProtect" | |
| $sslOnlyPref = $false | |
| if (Test-Path "$gpUserPrefs\PanPortalCfg_*.dat") { | |
| # Check if SSL-only might be configured (heuristic) | |
| $prefFiles = Get-ChildItem "$gpUserPrefs\PanPortalCfg_*.dat" -ErrorAction SilentlyContinue | |
| if ($prefFiles) { | |
| Write-Status "Portal config cached" "Yes" "White" | |
| } | |
| } | |
| # Check registry for GP settings | |
| $gpRegPath = "HKLM:\SOFTWARE\Palo Alto Networks\GlobalProtect\Settings" | |
| if (Test-Path $gpRegPath) { | |
| $gpSettings = Get-ItemProperty -Path $gpRegPath -ErrorAction SilentlyContinue | |
| if ($gpSettings.Portal) { | |
| Write-Status "Configured Portal" $gpSettings.Portal "White" | |
| $results.Tests["GP_Portal"] = $gpSettings.Portal | |
| } | |
| } | |
| # Check connection type from GP logs if accessible | |
| $gpLogPath = "C:\Program Files\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| if (-not (Test-Path $gpLogPath)) { | |
| $gpLogPath = "$env:ProgramData\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| } | |
| if (Test-Path $gpLogPath) { | |
| $recentLog = Get-Content $gpLogPath -Tail 100 -ErrorAction SilentlyContinue | |
| $connType = if ($recentLog -match "IPSec") { "IPSec" } | |
| elseif ($recentLog -match "SSL") { "SSL" } | |
| else { "Unknown" } | |
| # SSL is fine if on low MTU network/policy, IPSec is preferred otherwise | |
| $gpMtu = (Get-NetIPInterface -InterfaceIndex $gpAdapter.ifIndex -ErrorAction SilentlyContinue).NlMtu | Select-Object -First 1 | |
| $typeColor = if ($connType -eq "SSL" -and $gpMtu -le 1300) { $colors.Success } | |
| elseif ($connType -eq "SSL") { $colors.Warning } | |
| elseif ($connType -eq "IPSec") { $colors.Success } | |
| else { "White" } | |
| Write-Status "Detected Tunnel Type" $connType $typeColor | |
| $results.Tests["Tunnel_Type"] = $connType | |
| } | |
| # ============================================================ | |
| # SECTION 1.5: GLOBALPROTECT LOG ANALYSIS | |
| # ============================================================ | |
| Write-Host "`n[1.5] GLOBALPROTECT LOG ANALYSIS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| $gpLogPaths = @( | |
| "C:\Program Files\Palo Alto Networks\GlobalProtect\PanGPS.log", | |
| "$env:ProgramData\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| ) | |
| $gpLog = $null | |
| foreach ($path in $gpLogPaths) { | |
| if (Test-Path $path) { | |
| $gpLog = Get-Content $path -Tail 500 -ErrorAction SilentlyContinue | |
| Write-Status "Found PanGPS.log" $path "White" | |
| break | |
| } | |
| } | |
| if ($gpLog) { | |
| # Check for IPSec vs SSL indicators | |
| $ipsecAttempts = ($gpLog | Select-String -Pattern "keep alive to ipsec socket" -AllMatches).Matches.Count | |
| $sslFallback = ($gpLog | Select-String -Pattern "SSL tunnel|SSL connection" -AllMatches).Matches.Count | |
| $ipsecSuccess = ($gpLog | Select-String -Pattern "IPSec tunnel established|ESP" -AllMatches).Matches.Count | |
| Write-Status "IPSec keep-alive attempts (recent)" $ipsecAttempts "White" | |
| Write-Status "SSL tunnel indicators" $sslFallback $(if ($sslFallback -gt 0) { $colors.Warning } else { "White" }) | |
| if ($ipsecAttempts -gt 0 -and $sslFallback -gt 0 -and $ipsecSuccess -eq 0) { | |
| Write-Host " [!] Pattern suggests IPSec failed, fell back to SSL" -ForegroundColor $colors.Warning | |
| $results.Tests["IPSec_Fallback_Detected"] = $true | |
| } | |
| # Check for fragmentation in logs | |
| $fragCount = ($gpLog | Select-String -Pattern "fragment 0x" -AllMatches).Matches.Count | |
| if ($fragCount -gt 0) { | |
| Write-Status "Fragmentation events in log" $fragCount $colors.Warning | |
| $results.Tests["Fragmentation_In_Logs"] = $fragCount | |
| } else { | |
| Write-Status "Fragmentation events in log" "None detected" $colors.Success | |
| } | |
| # Check MTU settings applied | |
| $mtuSettings = $gpLog | Select-String -Pattern "set VA MTU|set MTU:" | Select-Object -Last 3 | |
| if ($mtuSettings) { | |
| Write-Host "`n Recent MTU settings applied:" -ForegroundColor White | |
| foreach ($line in $mtuSettings) { | |
| Write-Host " $($line.Line.Trim())" -ForegroundColor Gray | |
| } | |
| } | |
| # Check for connection errors | |
| $connErrors = $gpLog | Select-String -Pattern "error|failed|timeout" -AllMatches | Select-Object -Last 5 | |
| if ($connErrors) { | |
| Write-Host "`n Recent errors/warnings:" -ForegroundColor $colors.Warning | |
| foreach ($err in $connErrors) { | |
| $shortErr = $err.Line.Trim() | |
| if ($shortErr.Length -gt 80) { $shortErr = $shortErr.Substring(0, 77) + "..." } | |
| Write-Host " $shortErr" -ForegroundColor Gray | |
| } | |
| } | |
| # Extract gateway IP if present | |
| $gwMatch = $gpLog | Select-String -Pattern "Connected to: (\d+\.\d+\.\d+\.\d+)" | Select-Object -Last 1 | |
| if ($gwMatch -and $gwMatch.Matches.Groups[1]) { | |
| $detectedGW = $gwMatch.Matches.Groups[1].Value | |
| Write-Status "`n Detected Gateway IP" $detectedGW "White" | |
| $results.Tests["Detected_Gateway"] = $detectedGW | |
| # Use detected gateway for UDP test if not specified | |
| if (-not $GPGateway) { | |
| $GPGateway = $detectedGW | |
| } | |
| } | |
| } else { | |
| Write-Status "PanGPS.log" "NOT FOUND - GP may not be installed" $colors.Warning | |
| } | |
| # ============================================================ | |
| # SECTION 1.7: NETWORK STACK HEALTH CHECK | |
| # ============================================================ | |
| Write-Host "`n[1.7] NETWORK STACK HEALTH CHECK" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Check critical network services | |
| $criticalServices = @( | |
| @{ Name = "Dnscache"; Display = "DNS Client" }, | |
| @{ Name = "Dhcp"; Display = "DHCP Client" }, | |
| @{ Name = "NlaSvc"; Display = "Network Location Awareness" }, | |
| @{ Name = "netprofm"; Display = "Network List Service" }, | |
| @{ Name = "Netman"; Display = "Network Connections" } | |
| ) | |
| Write-Host "Network Services:" -ForegroundColor White | |
| $serviceIssues = 0 | |
| foreach ($svc in $criticalServices) { | |
| $service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue | |
| if ($service) { | |
| $status = if ($service.Status -eq "Running") { "Running"; $colors.Success } else { "STOPPED"; $colors.Failure; $serviceIssues++ } | |
| Write-Status " $($svc.Display)" $status[0] $status[1] | |
| } else { | |
| Write-Status " $($svc.Display)" "NOT FOUND" $colors.Warning | |
| } | |
| } | |
| $results.Tests["Network_Service_Issues"] = $serviceIssues | |
| # Check Winsock LSP catalog for corruption/third-party hooks | |
| Write-Host "`nWinsock Catalog:" -ForegroundColor White | |
| $winsockCatalog = netsh winsock show catalog 2>&1 | |
| $lspCount = ($winsockCatalog | Select-String -Pattern "Layered Chain Entry" -AllMatches).Matches.Count | |
| $nonMsLsp = ($winsockCatalog | Select-String -Pattern "Provider Path.*(?<!Microsoft|Windows)" -AllMatches).Matches.Count | |
| Write-Status " Layered Service Providers" $lspCount $(if ($lspCount -gt 10) { $colors.Warning } else { "White" }) | |
| # Check for known problematic LSPs | |
| $problematicLSPs = @("avgnt", "NOD32", "Kaspersky", "Avast", "Norton", "McAfee", "WebRoot", "Symantec") | |
| $foundProblematic = @() | |
| foreach ($lsp in $problematicLSPs) { | |
| if ($winsockCatalog -match $lsp) { | |
| $foundProblematic += $lsp | |
| } | |
| } | |
| if ($foundProblematic.Count -gt 0) { | |
| Write-Status " Third-party LSPs detected" ($foundProblematic -join ", ") $colors.Warning | |
| $results.Tests["ThirdParty_LSPs"] = $foundProblematic | |
| } else { | |
| Write-Status " Third-party LSPs" "None detected" $colors.Success | |
| } | |
| # Check network adapter statistics for errors | |
| Write-Host "`nAdapter Health:" -ForegroundColor White | |
| $adapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" } | |
| foreach ($adapter in $adapters) { | |
| $stats = Get-NetAdapterStatistics -Name $adapter.Name -ErrorAction SilentlyContinue | |
| if ($stats) { | |
| $errors = $stats.InErrors + $stats.OutErrors | |
| $discards = $stats.InDiscards + $stats.OutDiscards | |
| $adapterHealth = if ($errors -gt 100 -or $discards -gt 100) { "Issues"; $colors.Warning } | |
| elseif ($errors -gt 0 -or $discards -gt 0) { "Minor errors"; $colors.Info } | |
| else { "Healthy"; $colors.Success } | |
| Write-Status " $($adapter.Name)" $adapterHealth[0] $adapterHealth[1] | |
| if ($errors -gt 0 -or $discards -gt 0) { | |
| Write-Host " Errors: $errors | Discards: $discards" -ForegroundColor Gray | |
| } | |
| $results.Tests["Adapter_$($adapter.Name)_Errors"] = $errors | |
| $results.Tests["Adapter_$($adapter.Name)_Discards"] = $discards | |
| } | |
| } | |
| # Check TCP/IP parameters | |
| Write-Host "`nTCP/IP Parameters:" -ForegroundColor White | |
| # Check for TCP auto-tuning (can cause issues with some VPNs) | |
| $autoTuning = netsh interface tcp show global 2>&1 | Out-String | |
| $autoTuningLevel = "Unknown" | |
| if ($autoTuning -match "Receive Window Auto-Tuning Level\s*:\s*(\w+)") { | |
| $autoTuningLevel = $Matches[1] | |
| } | |
| Write-Status " TCP Auto-Tuning" $autoTuningLevel $(if ($autoTuningLevel -eq "disabled") { $colors.Warning } else { "White" }) | |
| # Check ECN capability | |
| $ecn = "Unknown" | |
| if ($autoTuning -match "ECN Capability\s*:\s*(\w+)") { | |
| $ecn = $Matches[1] | |
| } | |
| Write-Status " ECN Capability" $ecn "White" | |
| # Check for RSS (Receive Side Scaling) | |
| $rss = "Unknown" | |
| if ($autoTuning -match "Receive-Side Scaling State\s*:\s*(\w+)") { | |
| $rss = $Matches[1] | |
| } | |
| Write-Status " Receive-Side Scaling" $rss "White" | |
| # Check proxy settings (can interfere with VPN) | |
| Write-Host "`nProxy Settings:" -ForegroundColor White | |
| $proxyReg = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -ErrorAction SilentlyContinue | |
| if ($proxyReg.ProxyEnable -eq 1) { | |
| Write-Status " System Proxy" "ENABLED - $($proxyReg.ProxyServer)" $colors.Warning | |
| $results.Tests["Proxy_Enabled"] = $true | |
| $results.Tests["Proxy_Server"] = $proxyReg.ProxyServer | |
| } else { | |
| Write-Status " System Proxy" "Disabled" $colors.Success | |
| $results.Tests["Proxy_Enabled"] = $false | |
| } | |
| # Check for VPN-related WFP filters that might cause issues | |
| Write-Host "`nNetwork Filters:" -ForegroundColor White | |
| $wfpFilters = Get-NetFirewallProfile -ErrorAction SilentlyContinue | |
| foreach ($profile in $wfpFilters) { | |
| $status = if ($profile.Enabled) { "Enabled"; "White" } else { "Disabled"; $colors.Warning } | |
| Write-Status " Windows Firewall ($($profile.Name))" $status[0] $status[1] | |
| } | |
| # Check for third-party network filter drivers | |
| $filterDrivers = Get-CimInstance -ClassName Win32_SystemDriver -ErrorAction SilentlyContinue | | |
| Where-Object { $_.DisplayName -match "filter|firewall|vpn|tunnel" -and $_.State -eq "Running" -and $_.DisplayName -notmatch "Microsoft|Windows|PANGP|GlobalProtect" } | | |
| Select-Object -First 5 | |
| if ($filterDrivers) { | |
| Write-Host " Third-party network drivers:" -ForegroundColor $colors.Warning | |
| foreach ($drv in $filterDrivers) { | |
| Write-Host " - $($drv.DisplayName)" -ForegroundColor Gray | |
| } | |
| $results.Tests["ThirdParty_Drivers"] = $filterDrivers.DisplayName | |
| } | |
| # Check for IP forwarding (unusual for workstations) | |
| $ipForward = (Get-NetIPInterface | Where-Object { $_.Forwarding -eq "Enabled" }).InterfaceAlias | |
| if ($ipForward) { | |
| Write-Status " IP Forwarding" "Enabled on: $($ipForward -join ', ')" $colors.Warning | |
| $results.Tests["IP_Forwarding"] = $ipForward | |
| } | |
| # Check hosts file for suspicious entries | |
| $hostsFile = Get-Content "$env:SystemRoot\System32\drivers\etc\hosts" -ErrorAction SilentlyContinue | | |
| Where-Object { $_ -notmatch "^\s*#" -and $_ -match "\S" } | |
| $hostsEntries = ($hostsFile | Measure-Object).Count | |
| if ($hostsEntries -gt 5) { | |
| Write-Status " Custom hosts entries" "$hostsEntries entries" $colors.Warning | |
| } else { | |
| Write-Status " Custom hosts entries" "$hostsEntries entries" "White" | |
| } | |
| $results.Tests["Hosts_Entries"] = $hostsEntries | |
| # Check IPv6 status (can sometimes cause VPN issues) | |
| $ipv6Adapters = Get-NetAdapterBinding -ComponentID ms_tcpip6 -ErrorAction SilentlyContinue | | |
| Where-Object { $_.Enabled -eq $true } | |
| $ipv6Count = ($ipv6Adapters | Measure-Object).Count | |
| Write-Status " IPv6 enabled adapters" $ipv6Count "White" | |
| # Quick connectivity sanity check - can we reach the gateway? | |
| $defaultGateway = (Get-NetRoute -DestinationPrefix "0.0.0.0/0" -ErrorAction SilentlyContinue | | |
| Sort-Object RouteMetric | Select-Object -First 1).NextHop | |
| if ($defaultGateway) { | |
| $gwPing = Test-Connection -ComputerName $defaultGateway -Count 1 -Quiet -ErrorAction SilentlyContinue | |
| $gwStatus = if ($gwPing) { "Reachable"; $colors.Success } else { "Unreachable"; $colors.Failure } | |
| Write-Status " Default Gateway ($defaultGateway)" $gwStatus[0] $gwStatus[1] | |
| $results.Tests["Default_Gateway_Reachable"] = $gwPing | |
| $results.Tests["Default_Gateway"] = $defaultGateway | |
| # Detect mobile hotspot based on common gateway ranges | |
| $hotspotRanges = @( | |
| "172.20.10.", # iPhone hotspot | |
| "192.168.43.", # Android hotspot | |
| "192.168.137.", # Windows Mobile hotspot | |
| "10.0.0." # Some carrier hotspots | |
| ) | |
| $isHotspot = $false | |
| foreach ($range in $hotspotRanges) { | |
| if ($defaultGateway.StartsWith($range)) { | |
| $isHotspot = $true | |
| break | |
| } | |
| } | |
| if ($isHotspot) { | |
| Write-Status " [!] Mobile Hotspot Detected" "MTU will be severely constrained" $colors.Warning | |
| $results.Tests["Mobile_Hotspot"] = $true | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 1.6: UDP 4501 (IPSec) CONNECTIVITY TEST | |
| # ============================================================ | |
| if ($GPGateway) { | |
| Write-Host "`n[1.6] UDP 4501 (IPSec) CONNECTIVITY TEST" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| Write-Host "Testing IPSec port to gateway: $GPGateway" -ForegroundColor White | |
| # UDP 4501 test | |
| try { | |
| $udpClient = New-Object System.Net.Sockets.UdpClient | |
| $udpClient.Client.ReceiveTimeout = 3000 | |
| $udpClient.Connect($GPGateway, 4501) | |
| # Send a small packet | |
| $bytes = [System.Text.Encoding]::ASCII.GetBytes("test") | |
| $udpClient.Send($bytes, $bytes.Length) | Out-Null | |
| Write-Status "UDP 4501 send" "Packet sent (no guarantee of delivery)" $colors.Info | |
| $udpClient.Close() | |
| $results.Tests["UDP_4501_Send"] = $true | |
| } catch { | |
| Write-Status "UDP 4501 test" "Failed - $($_.Exception.Message)" $colors.Failure | |
| $results.Tests["UDP_4501_Send"] = $false | |
| } | |
| # Check if any firewall rules might block UDP 4501 | |
| $fwRules = Get-NetFirewallRule -Direction Outbound -Action Block -Enabled True -ErrorAction SilentlyContinue | | |
| Where-Object { $_.DisplayName -match "GlobalProtect|UDP|4501" } | |
| if ($fwRules) { | |
| Write-Status "Potential blocking FW rules" $fwRules.Count $colors.Warning | |
| } | |
| # Test TCP 443 to same gateway for comparison | |
| $tcp443 = Test-NetConnection -ComputerName $GPGateway -Port 443 -WarningAction SilentlyContinue | |
| $tcpStatus = if ($tcp443.TcpTestSucceeded) { "Open"; $colors.Success } else { "Closed"; $colors.Failure } | |
| Write-Status "TCP 443 (SSL fallback port)" $tcpStatus[0] $tcpStatus[1] | |
| $results.Tests["Gateway_TCP_443"] = $tcp443.TcpTestSucceeded | |
| } | |
| # ============================================================ | |
| # SECTION 2: Network Configuration | |
| # ============================================================ | |
| Write-Host "`n[2] NETWORK CONFIGURATION" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Check for direct PPPoE connections (rare - usually on modem/router) | |
| $pppoeAdapter = Get-NetAdapter | Where-Object { $_.InterfaceDescription -like "*PPP*" -or $_.Name -like "*PPP*" } | |
| if ($pppoeAdapter) { | |
| Write-Status "Direct PPPoE Connection" "Detected on this machine" $colors.Warning | |
| Write-Host " PPPoE has MTU of 1492 (8 bytes less than standard)" -ForegroundColor Gray | |
| $results.Tests["PPPoE_Direct"] = $true | |
| } | |
| # Check WAN connection type via interface metrics | |
| $wanAdapter = Get-NetAdapter | Where-Object { $_.Status -eq "Up" -and $_.InterfaceDescription -notlike "*Virtual*" -and $_.InterfaceDescription -notlike "*PANGP*" } | | |
| Sort-Object -Property @{Expression={$_.LinkSpeed}; Descending=$true} | Select-Object -First 1 | |
| if ($wanAdapter) { | |
| Write-Status "Primary Network Adapter" $wanAdapter.Name "White" | |
| Write-Status " Type" $wanAdapter.InterfaceDescription "Gray" | |
| # Check if WiFi | |
| if ($wanAdapter.InterfaceDescription -match "Wi-?Fi|Wireless|802\.11") { | |
| Write-Status " Connection" "Wireless (check upstream ISP for MTU constraints)" "Gray" | |
| $results.Tests["Connection_Type"] = "WiFi" | |
| } | |
| } | |
| # Get all active adapters and their MTUs | |
| $activeAdapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" } | |
| foreach ($adapter in $activeAdapters) { | |
| $mtu = (Get-NetIPInterface -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).NlMtu | |
| $mtuColor = if ($mtu -lt 1400) { $colors.Warning } elseif ($mtu -eq 1500) { $colors.Success } else { "White" } | |
| Write-Status "$($adapter.Name)" "MTU: $mtu" $mtuColor | |
| } | |
| # DNS Configuration | |
| Write-Host "`nDNS Servers Configured:" -ForegroundColor White | |
| $dnsServers = Get-DnsClientServerAddress -AddressFamily IPv4 | | |
| Where-Object { $_.ServerAddresses } | | |
| Select-Object InterfaceAlias, ServerAddresses | |
| foreach ($dns in $dnsServers) { | |
| Write-Host " $($dns.InterfaceAlias): $($dns.ServerAddresses -join ', ')" -ForegroundColor Gray | |
| } | |
| $results.Tests["DNS_Servers"] = $dnsServers | |
| # ============================================================ | |
| # SECTION 3: DNS Resolution Tests | |
| # ============================================================ | |
| Write-Host "`n[3] DNS RESOLUTION TESTS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| foreach ($target in $TestHosts) { | |
| Write-Host "`nTesting: $target" -ForegroundColor White | |
| # Test with system DNS | |
| Write-Status " System DNS resolver..." -Status "" | |
| $dnsResult = $null | |
| $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() | |
| try { | |
| $dnsResult = Resolve-DnsName -Name $target -Type A -DnsOnly -ErrorAction Stop | |
| $stopwatch.Stop() | |
| $ip = ($dnsResult | Where-Object { $_.Type -eq "A" }).IPAddress | Select-Object -First 1 | |
| Write-Status " Result" "$ip (${$stopwatch.ElapsedMilliseconds}ms)" $colors.Success | |
| $results.Tests["DNS_System_$target"] = @{ IP = $ip; Time = $stopwatch.ElapsedMilliseconds } | |
| } catch { | |
| $stopwatch.Stop() | |
| Write-Status " Result" "FAILED - $($_.Exception.Message)" $colors.Failure | |
| $results.Tests["DNS_System_$target"] = @{ Error = $_.Exception.Message } | |
| } | |
| # Test with specific internal DNS server | |
| Write-Status " Internal DNS ($InternalDNS)..." -Status "" | |
| $stopwatch.Restart() | |
| try { | |
| $dnsResult = Resolve-DnsName -Name $target -Type A -Server $InternalDNS -DnsOnly -ErrorAction Stop | |
| $stopwatch.Stop() | |
| $ip = ($dnsResult | Where-Object { $_.Type -eq "A" }).IPAddress | Select-Object -First 1 | |
| Write-Status " Result" "$ip (${$stopwatch.ElapsedMilliseconds}ms)" $colors.Success | |
| $results.Tests["DNS_Internal_$target"] = @{ IP = $ip; Time = $stopwatch.ElapsedMilliseconds } | |
| } catch { | |
| $stopwatch.Stop() | |
| Write-Status " Result" "FAILED - Timeout or unreachable" $colors.Failure | |
| $results.Tests["DNS_Internal_$target"] = @{ Error = "Failed" } | |
| } | |
| # Direct nslookup for comparison (uses UDP) | |
| Write-Status " nslookup (UDP-based)..." -Status "" | |
| $nslookup = nslookup $target $InternalDNS 2>&1 | |
| if ($nslookup -match "Address.*\d+\.\d+\.\d+\.\d+" -and $nslookup -notmatch "timed out") { | |
| Write-Status " Result" "SUCCESS" $colors.Success | |
| } else { | |
| Write-Status " Result" "TIMEOUT/FAILED" $colors.Failure | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 4: Connectivity Tests | |
| # ============================================================ | |
| Write-Host "`n[4] CONNECTIVITY TESTS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| foreach ($target in $TestHosts) { | |
| Write-Host "`nTesting: $target" -ForegroundColor White | |
| # ICMP Ping (may be blocked on some hosts) | |
| $ping = Test-NetConnection -ComputerName $target -WarningAction SilentlyContinue | |
| $pingStatus = if ($ping.PingSucceeded) { "Success"; $colors.Success } else { "No response (may be filtered)"; $colors.Warning } | |
| Write-Status " ICMP Ping" $pingStatus[0] $pingStatus[1] | |
| if ($ping.PingSucceeded) { | |
| Write-Status " RTT" "$($ping.PingReplyDetails.RoundtripTime)ms" "White" | |
| } | |
| $results.Tests["Ping_$target"] = $ping.PingSucceeded | |
| # TCP 443 | |
| $tcp443 = Test-NetConnection -ComputerName $target -Port 443 -WarningAction SilentlyContinue | |
| $tcpStatus = if ($tcp443.TcpTestSucceeded) { "Open"; $colors.Success } else { "Closed/Filtered"; $colors.Failure } | |
| Write-Status " TCP/443 (HTTPS)" $tcpStatus[0] $tcpStatus[1] | |
| $results.Tests["TCP443_$target"] = $tcp443.TcpTestSucceeded | |
| } | |
| # Banner/Apps28 specific test on port 8445 | |
| if ($BannerHost) { | |
| Write-Host "`nTesting Banner: $BannerHost`:$BannerPort" -ForegroundColor White | |
| # DNS resolution first | |
| try { | |
| $bannerIP = (Resolve-DnsName -Name $BannerHost -Type A -DnsOnly -ErrorAction Stop | | |
| Where-Object { $_.Type -eq "A" }).IPAddress | Select-Object -First 1 | |
| Write-Status " DNS Resolution" $bannerIP $colors.Success | |
| $results.Tests["Banner_DNS"] = $bannerIP | |
| } catch { | |
| Write-Status " DNS Resolution" "FAILED" $colors.Failure | |
| $results.Tests["Banner_DNS"] = $null | |
| } | |
| # TCP port test | |
| $bannerConn = Test-NetConnection -ComputerName $BannerHost -Port $BannerPort -WarningAction SilentlyContinue | |
| $bannerStatus = if ($bannerConn.TcpTestSucceeded) { "Open"; $colors.Success } else { "Closed/Filtered"; $colors.Failure } | |
| Write-Status " TCP/$BannerPort" $bannerStatus[0] $bannerStatus[1] | |
| $results.Tests["Banner_TCP_$BannerPort"] = $bannerConn.TcpTestSucceeded | |
| if (-not $bannerConn.TcpTestSucceeded -and $results.Tests["Banner_DNS"]) { | |
| Write-Host " [!] DNS resolves but port unreachable - likely MTU/tunnel issue" -ForegroundColor $colors.Warning | |
| $results.Tests["Banner_MTU_Suspect"] = $true | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 5: MTU Discovery | |
| # ============================================================ | |
| Write-Host "`n[5] MTU PATH DISCOVERY" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| Write-Host "Testing maximum packet size without fragmentation..." -ForegroundColor Gray | |
| # Use login.du.edu for MTU test (responds to ICMP) | |
| $mtuTarget = "login.du.edu" | |
| $mtuSizes = @(1500, 1472, 1450, 1400, 1350, 1300, 1250, 1200) | |
| $maxWorking = 0 | |
| Write-Host "`nTarget: $mtuTarget" -ForegroundColor White | |
| Write-Host "Size`t`tResult" -ForegroundColor Gray | |
| Write-Host "-"*30 | |
| foreach ($size in $mtuSizes) { | |
| $pingTest = ping -n 1 -f -l $size -w 2000 $mtuTarget 2>&1 | Out-String | |
| if ($pingTest -match "Reply from" -and $pingTest -notmatch "fragmented") { | |
| Write-Host "$size bytes`t`tOK" -ForegroundColor $colors.Success | |
| if ($size -gt $maxWorking) { $maxWorking = $size } | |
| if ($size -eq 1500) { break } # No MTU issues | |
| } else { | |
| Write-Host "$size bytes`t`tFAILED" -ForegroundColor $colors.Failure | |
| } | |
| } | |
| $results.Tests["Max_MTU"] = $maxWorking | |
| # Calculate recommended tunnel MTU | |
| $recommendedMTU = $maxWorking - 28 # Account for IP (20) + ICMP (8) headers | |
| Write-Host "`nMaximum working payload: $maxWorking bytes" -ForegroundColor White | |
| Write-Host "Recommended tunnel MTU: $recommendedMTU bytes" -ForegroundColor $colors.Info | |
| # Infer upstream connection type from MTU results | |
| if ($maxWorking -ge 1464 -and $maxWorking -le 1472) { | |
| Write-Host "`n[DETECTED] Path MTU suggests upstream PPPoE (CenturyLink/DSL)" -ForegroundColor $colors.Warning | |
| Write-Host " PPPoE MTU (1492) - IP header (20) - ICMP (8) = 1464 max payload" -ForegroundColor Gray | |
| $results.Tests["Upstream_PPPoE_Inferred"] = $true | |
| } elseif ($maxWorking -lt 1350) { | |
| Write-Host "`n[DETECTED] Very low path MTU - likely mobile hotspot or carrier CGNAT" -ForegroundColor $colors.Warning | |
| Write-Host " Mobile carriers add significant tunneling overhead" -ForegroundColor Gray | |
| $results.Tests["Mobile_Carrier_Inferred"] = $true | |
| } elseif ($maxWorking -lt 1464) { | |
| Write-Host "`n[DETECTED] Path MTU lower than typical PPPoE - possible double-NAT or tunnel" -ForegroundColor $colors.Warning | |
| } | |
| if ($maxWorking -lt 1400) { | |
| Write-Host "`n*** MTU ISSUE DETECTED ***" -ForegroundColor $colors.Warning | |
| Write-Host "This client needs the Low-MTU GP policy" -ForegroundColor $colors.Warning | |
| Write-Host "IPSec adds ~50-70 bytes overhead, leaving insufficient room for DNS/SMB" -ForegroundColor Gray | |
| } | |
| # Store recommended MTU for later use | |
| $results.Tests["Recommended_MTU"] = $recommendedMTU | |
| # ============================================================ | |
| # MTU ADJUSTMENT (if requested) | |
| # ============================================================ | |
| if ($SetMTU -gt 0 -or $AutoMTU -or $RestoreMTU) { | |
| Write-Host "`n[MTU ADJUSTMENT]" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Find GP adapter | |
| $gpAdapterForMTU = Get-NetAdapter | Where-Object { | |
| $_.InterfaceDescription -like "*PANGP*" -or | |
| $_.InterfaceDescription -like "*GlobalProtect*" | |
| } | Select-Object -First 1 | |
| if (-not $gpAdapterForMTU) { | |
| Write-Host "ERROR: GlobalProtect adapter not found. Is VPN connected?" -ForegroundColor $colors.Failure | |
| } else { | |
| $gpIfIndex = $gpAdapterForMTU.ifIndex | |
| $gpAdapterName = $gpAdapterForMTU.Name | |
| $currentGPMTU = (Get-NetIPInterface -InterfaceIndex $gpIfIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).NlMtu | |
| if ($RestoreMTU) { | |
| # Restore to GP default (1400) | |
| $targetMTU = 1400 | |
| Write-Host "Restoring GP adapter to default MTU (1400)..." -ForegroundColor $colors.Info | |
| } elseif ($AutoMTU) { | |
| # Use calculated recommended MTU, but apply safety margins | |
| # Take the recommended MTU and round down to nearest 10 for safety | |
| $targetMTU = [Math]::Floor($recommendedMTU / 10) * 10 | |
| # Apply minimum/maximum bounds | |
| if ($targetMTU -lt 1000) { $targetMTU = 1000 } | |
| if ($targetMTU -gt 1400) { $targetMTU = 1400 } | |
| Write-Host "Auto-calculated MTU based on path discovery:" -ForegroundColor $colors.Info | |
| Write-Host " Path max payload: $maxWorking bytes" -ForegroundColor Gray | |
| Write-Host " Recommended MTU: $recommendedMTU bytes" -ForegroundColor Gray | |
| Write-Host " Safe target MTU: $targetMTU bytes (rounded down)" -ForegroundColor Gray | |
| } else { | |
| # Manual MTU setting | |
| $targetMTU = $SetMTU | |
| Write-Host "Manual MTU setting requested: $targetMTU bytes" -ForegroundColor $colors.Info | |
| } | |
| # Apply the change | |
| $mtuChanged = Set-GPAdapterMTU -NewMTU $targetMTU -CurrentMTU $currentGPMTU ` | |
| -AdapterName $gpAdapterName -InterfaceIndex $gpIfIndex -ForceChange:$Force | |
| if ($mtuChanged) { | |
| $results.Tests["MTU_Changed"] = $true | |
| $results.Tests["MTU_New_Value"] = $targetMTU | |
| $results.Tests["MTU_Previous_Value"] = $currentGPMTU | |
| } | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 6: Route Analysis | |
| # ============================================================ | |
| Write-Host "`n[6] ROUTING ANALYSIS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Show routes through VPN | |
| $vpnRoutes = Get-NetRoute | Where-Object { | |
| $_.InterfaceAlias -like "*GlobalProtect*" -or | |
| $_.InterfaceAlias -like "*PANGP*" | |
| } | Select-Object DestinationPrefix, NextHop, InterfaceAlias, RouteMetric | Sort-Object DestinationPrefix | |
| if ($vpnRoutes) { | |
| Write-Host "Routes via GlobalProtect:" -ForegroundColor White | |
| $vpnRoutes | Format-Table -AutoSize | |
| } else { | |
| Write-Status "No routes through GP adapter" "Check tunnel" $colors.Warning | |
| } | |
| # Trace route to first test host | |
| Write-Host "Route to $($TestHosts[0]):" -ForegroundColor White | |
| $traceResult = Test-NetConnection -ComputerName $TestHosts[0] -TraceRoute -WarningAction SilentlyContinue | |
| if ($traceResult.TraceRoute) { | |
| $hopNum = 1 | |
| foreach ($hop in $traceResult.TraceRoute) { | |
| Write-Host " $hopNum. $hop" -ForegroundColor Gray | |
| $hopNum++ | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 7: Summary & Recommendations | |
| # ============================================================ | |
| Write-Host "`n" + "="*60 -ForegroundColor $colors.Info | |
| Write-Host " SUMMARY & RECOMMENDATIONS" -ForegroundColor $colors.Info | |
| Write-Host "="*60 -ForegroundColor $colors.Info | |
| $issues = @() | |
| # Analyze results | |
| if ($results.Tests["Max_MTU"] -lt 1400) { | |
| # Check if already on low-MTU policy | |
| $alreadyLowMtu = $results.Tests["GP_Adapter_MTU"] -and $results.Tests["GP_Adapter_MTU"] -le 1300 | |
| $severity = if ($alreadyLowMtu) { "INFO" } else { "HIGH" } | |
| $fix = if ($alreadyLowMtu) { | |
| "Already on Low-MTU policy (GP MTU: $($results.Tests['GP_Adapter_MTU'])). Current network constrains to ~$($results.Tests['Max_MTU']) bytes." | |
| } else { | |
| "Move user to Low-MTU GlobalProtect policy group (CenturyLink/PPPoE typically needs MTU ~1420)" | |
| } | |
| $issues += @{ | |
| Severity = $severity | |
| Issue = "MTU path is restricted to $($results.Tests['Max_MTU']) bytes" | |
| Fix = $fix | |
| } | |
| } | |
| if ($results.Tests["Upstream_PPPoE_Inferred"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Upstream PPPoE detected (likely CenturyLink behind router)" | |
| Fix = "ISP modem handles PPPoE (MTU 1492) - IPSec tunnel needs MTU ~1400 or lower" | |
| } | |
| } | |
| if ($results.Tests["Mobile_Carrier_Inferred"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Very low MTU detected (~$($results.Tests['Max_MTU']) bytes) - mobile carrier or heavy tunneling" | |
| Fix = "Use Low-MTU GP policy (1280 or lower). SSL tunnel fallback is expected and acceptable." | |
| } | |
| } | |
| if ($results.Tests["Tunnel_Type"] -eq "IPSec") { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Connection using IPSec tunnel (50-70 byte overhead)" | |
| Fix = "If MTU issues persist, force SSL tunnel via GP gateway config" | |
| } | |
| } | |
| $dnsFailures = $results.Tests.Keys | Where-Object { | |
| $_ -like "DNS_System_*" -or $_ -like "DNS_Internal_*" | |
| } | Where-Object { | |
| $results.Tests[$_] -is [hashtable] -and $results.Tests[$_].Error | |
| } | |
| if ($dnsFailures) { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "DNS resolution failing over VPN tunnel" | |
| Fix = "Likely MTU issue - DNS responses being fragmented/dropped" | |
| } | |
| } | |
| if ($results.Tests["IPSec_Fallback_Detected"]) { | |
| $alreadyLowMtu = $results.Tests["GP_Adapter_MTU"] -and $results.Tests["GP_Adapter_MTU"] -le 1300 | |
| $severity = if ($alreadyLowMtu) { "INFO" } else { "HIGH" } | |
| $issues += @{ | |
| Severity = $severity | |
| Issue = "IPSec tunnel failed, client fell back to SSL" | |
| Fix = if ($alreadyLowMtu) { "Expected behavior on low-MTU/mobile networks. SSL provides reliable connectivity." } | |
| else { "Check UDP 4501 path, MTU issues, or move to low-MTU GP policy" } | |
| } | |
| } | |
| if ($results.Tests["Fragmentation_In_Logs"] -and $results.Tests["Fragmentation_In_Logs"] -gt 5) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Multiple fragmentation events detected in GP logs ($($results.Tests['Fragmentation_In_Logs']) occurrences)" | |
| Fix = "Lower MTU in GP portal config to avoid fragmentation" | |
| } | |
| } | |
| if ($results.Tests["Gateway_TCP_443"] -and -not $results.Tests["UDP_4501_Send"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "TCP 443 works but UDP 4501 may be blocked" | |
| Fix = "IPSec requires UDP 4501 - if blocked, SSL fallback is expected" | |
| } | |
| } | |
| if ($results.Tests["Banner_MTU_Suspect"]) { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "Banner (apps28.du.edu:8445) DNS resolves but port unreachable" | |
| Fix = "Classic MTU issue - move user to low-MTU GP policy" | |
| } | |
| } | |
| if ($results.Tests["Network_Service_Issues"] -gt 0) { | |
| # Check if connectivity is actually working despite stopped services | |
| $connectivityWorking = $results.Tests["Default_Gateway_Reachable"] -or | |
| ($results.Tests.Keys | Where-Object { $_ -like "TCP443_*" -and $results.Tests[$_] -eq $true }) | |
| if ($connectivityWorking) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Network services stopped ($($results.Tests['Network_Service_Issues'])) but connectivity working" | |
| Fix = "Consider restarting NlaSvc/Netman services, but may not be causing current issues" | |
| } | |
| } else { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "Critical network services not running ($($results.Tests['Network_Service_Issues']) stopped)" | |
| Fix = "Restart stopped services or run -ResetNetworkStack" | |
| } | |
| } | |
| } | |
| if ($results.Tests["ThirdParty_LSPs"]) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Third-party Winsock LSPs detected: $($results.Tests['ThirdParty_LSPs'] -join ', ')" | |
| Fix = "AV/security software LSPs can interfere with VPN - try disabling temporarily or run netsh winsock reset" | |
| } | |
| } | |
| if ($results.Tests["Proxy_Enabled"]) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "System proxy enabled: $($results.Tests['Proxy_Server'])" | |
| Fix = "Proxy settings can interfere with VPN split tunneling - verify proxy config" | |
| } | |
| } | |
| if ($results.Tests["ThirdParty_Drivers"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Third-party network filter drivers active" | |
| Fix = "Other VPN/security software may conflict with GlobalProtect" | |
| } | |
| } | |
| # Check for high adapter errors | |
| $adapterErrors = $results.Tests.Keys | Where-Object { $_ -like "Adapter_*_Errors" -and $results.Tests[$_] -gt 100 } | |
| if ($adapterErrors) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Network adapter showing high error count" | |
| Fix = "Update network drivers or check physical connection/cabling" | |
| } | |
| } | |
| if ($results.Tests["Default_Gateway_Reachable"] -eq $false) { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "Cannot reach default gateway - basic connectivity broken" | |
| Fix = "Check WiFi/Ethernet connection, router, or run -ResetNetworkStack" | |
| } | |
| } | |
| if ($results.Tests["Mobile_Hotspot"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Connected via mobile hotspot (detected gateway: $($results.Tests['Default_Gateway']))" | |
| Fix = "Mobile hotspots have severe MTU constraints (~1250-1300). Low-MTU GP policy required. SSL fallback expected." | |
| } | |
| } | |
| # Local internet health issues | |
| if ($results.Tests["Local_Internet_Health"] -eq "Unhealthy") { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "Local internet connection is unhealthy - problem is NOT just the VPN" | |
| Fix = "Check ISP connection, router, or local network before troubleshooting VPN" | |
| } | |
| } elseif ($results.Tests["Local_Internet_Health"] -eq "Degraded") { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Local internet has latency or packet loss issues" | |
| Fix = "VPN issues may be caused by underlying internet instability" | |
| } | |
| } | |
| if ($results.Tests["Packet_Loss_Percent"] -and $results.Tests["Packet_Loss_Percent"] -gt 10) { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "High packet loss detected ($($results.Tests['Packet_Loss_Percent'])%)" | |
| Fix = "Packet loss will severely impact VPN performance - check WiFi signal, ISP, or local network" | |
| } | |
| } | |
| if ($results.Tests["Latency_Average"] -and $results.Tests["Latency_Average"] -gt 150) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "High internet latency ($($results.Tests['Latency_Average']) ms average)" | |
| Fix = "High latency may cause VPN timeouts - check ISP or consider wired connection" | |
| } | |
| } | |
| # Compare external vs internal MTU to identify where the constraint is | |
| if ($results.Tests["External_Max_MTU"] -and $results.Tests["Max_MTU"]) { | |
| $extMTU = $results.Tests["External_Max_MTU"] | |
| $intMTU = $results.Tests["Max_MTU"] | |
| if ($extMTU -gt 0 -and $intMTU -gt 0) { | |
| if ([Math]::Abs($extMTU - $intMTU) -le 50) { | |
| # MTU is similar for both - ISP is the constraint | |
| $results.Tests["MTU_Constraint_Source"] = "ISP" | |
| } elseif ($intMTU -lt $extMTU) { | |
| # Internal MTU is lower - VPN tunnel is adding overhead | |
| $results.Tests["MTU_Constraint_Source"] = "VPN_Tunnel" | |
| } | |
| } | |
| } | |
| if ($issues.Count -eq 0) { | |
| Write-Host "`n[OK] No obvious issues detected" -ForegroundColor $colors.Success | |
| } else { | |
| # Check if things are actually working despite detected "issues" | |
| $bannerWorking = $results.Tests["Banner_TCP_$BannerPort"] | |
| $gpConnected = $results.Tests["GP_Tunnel_IP"] | |
| $lowMtuPolicy = $results.Tests["GP_Adapter_MTU"] -and $results.Tests["GP_Adapter_MTU"] -le 1300 | |
| if ($bannerWorking -and $gpConnected -and $lowMtuPolicy) { | |
| Write-Host "`n[OK] GlobalProtect connected and working with Low-MTU policy (MTU: $($results.Tests['GP_Adapter_MTU']))" -ForegroundColor $colors.Success | |
| Write-Host " Current network has constrained MTU but GP is configured correctly." -ForegroundColor Gray | |
| Write-Host "`nAdditional observations:" -ForegroundColor White | |
| } | |
| foreach ($issue in $issues) { | |
| $sevColor = switch ($issue.Severity) { | |
| "HIGH" { $colors.Failure } | |
| "MEDIUM" { $colors.Warning } | |
| default { $colors.Info } | |
| } | |
| Write-Host "`n[$($issue.Severity)] $($issue.Issue)" -ForegroundColor $sevColor | |
| Write-Host " Fix: $($issue.Fix)" -ForegroundColor Gray | |
| } | |
| } | |
| # ============================================================ | |
| # MTU Recommendation | |
| # ============================================================ | |
| if ($results.Tests["Recommended_MTU"] -and $results.Tests["Max_MTU"] -lt 1400) { | |
| $recMTU = $results.Tests["Recommended_MTU"] | |
| $currentMTU = $results.Tests["GP_Adapter_MTU"] | |
| $safeMTU = [Math]::Floor($recMTU / 10) * 10 | |
| if ($safeMTU -lt 1000) { $safeMTU = 1000 } | |
| if ($safeMTU -gt 1400) { $safeMTU = 1400 } | |
| Write-Host "`n" + "-"*40 -ForegroundColor Cyan | |
| Write-Host "RECOMMENDED MTU ADJUSTMENT:" -ForegroundColor Cyan | |
| Write-Host "-"*40 -ForegroundColor Cyan | |
| Write-Host " Path max payload: $($results.Tests['Max_MTU']) bytes" -ForegroundColor White | |
| Write-Host " Current GP MTU: $currentMTU bytes" -ForegroundColor White | |
| Write-Host " Calculated optimal: $recMTU bytes" -ForegroundColor White | |
| Write-Host " Recommended (safe): $safeMTU bytes" -ForegroundColor Green | |
| if ($currentMTU -and $currentMTU -le $safeMTU) { | |
| Write-Host "`n [OK] Current MTU ($currentMTU) is already at or below recommended." -ForegroundColor Green | |
| } else { | |
| Write-Host "`n To apply this MTU, run:" -ForegroundColor Yellow | |
| Write-Host " .\GP-Troubleshoot.ps1 -SetMTU $safeMTU" -ForegroundColor White | |
| Write-Host "`n Or auto-calculate and apply:" -ForegroundColor Yellow | |
| Write-Host " .\GP-Troubleshoot.ps1 -AutoMTU" -ForegroundColor White | |
| # Store for easy reference | |
| $results.Tests["Recommended_Safe_MTU"] = $safeMTU | |
| } | |
| Write-Host "-"*40 -ForegroundColor Cyan | |
| } | |
| # Show available reset options | |
| Write-Host "`n" + "-"*40 -ForegroundColor DarkGray | |
| Write-Host "AVAILABLE OPTIONS:" -ForegroundColor Gray | |
| Write-Host " -CollectLogs Bundle GP logs to desktop for analysis" -ForegroundColor DarkGray | |
| Write-Host " -GPGateway <ip> Test UDP 4501 connectivity to specific gateway" -ForegroundColor DarkGray | |
| Write-Host " -BannerHost <host> Banner server to test (default: apps28.du.edu)" -ForegroundColor DarkGray | |
| Write-Host " -BannerPort <port> Banner port to test (default: 8445)" -ForegroundColor DarkGray | |
| Write-Host " -AutoMTU Auto-set optimal MTU based on path discovery" -ForegroundColor DarkGray | |
| Write-Host " -SetMTU <value> Manually set GP adapter MTU (576-1500)" -ForegroundColor DarkGray | |
| Write-Host " -RestoreMTU Restore GP adapter MTU to default (1400)" -ForegroundColor DarkGray | |
| Write-Host " -Force Skip confirmation prompts for MTU changes" -ForegroundColor DarkGray | |
| Write-Host " -ResetDNSOnly Flush DNS cache (no restart)" -ForegroundColor DarkGray | |
| Write-Host " -ResetWinsock Reset Winsock catalog (restart required)" -ForegroundColor DarkGray | |
| Write-Host " -ResetNetworkStack Full stack reset: Winsock + TCP/IP (restart required)" -ForegroundColor DarkGray | |
| Write-Host " -ExportResults Export results to JSON on desktop" -ForegroundColor DarkGray | |
| Write-Host " -UploadResults Upload results to GitHub Gist for sharing" -ForegroundColor DarkGray | |
| # ============================================================ | |
| # Export Results | |
| # ============================================================ | |
| if ($ExportResults -or $UploadResults) { | |
| $exportPath = "$env:USERPROFILE\Desktop\GP-Troubleshoot-$(Get-Date -Format 'yyyyMMdd-HHmmss').json" | |
| $results | ConvertTo-Json -Depth 5 | Out-File $exportPath | |
| Write-Host "`nResults exported to: $exportPath" -ForegroundColor $colors.Info | |
| } | |
| # ============================================================ | |
| # Upload to GitHub Gist | |
| # ============================================================ | |
| if ($UploadResults) { | |
| Write-Host "`n[UPLOADING TO GITHUB GIST]" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| if (-not $GitHubToken) { | |
| Write-Host "GitHub token required. Provide via -GitHubToken or set GITHUB_TOKEN env var." -ForegroundColor $colors.Failure | |
| Write-Host "Create a token at: https://github.com/settings/tokens (needs 'gist' scope)" -ForegroundColor Gray | |
| } else { | |
| try { | |
| # Prepare the results content | |
| $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" | |
| $jsonContent = $results | ConvertTo-Json -Depth 5 | |
| # Create a summary text file | |
| $summaryContent = @" | |
| GP Troubleshooting Results - $timestamp | |
| Hostname: $($results.Hostname) | |
| Username: $($results.Username) | |
| Local Internet Health: | |
| - Status: $($results.Tests["Local_Internet_Health"]) | |
| - External MTU: $($results.Tests["External_Max_MTU"]) bytes | |
| - Latency: $($results.Tests["Latency_Average"]) ms avg | |
| - Packet Loss: $($results.Tests["Packet_Loss_Percent"])% | |
| - ISP Type (inferred): $($results.Tests["ISP_Type_Inferred"]) | |
| VPN Findings: | |
| - GP Adapter MTU: $($results.Tests["GP_Adapter_MTU"]) | |
| - Tunnel Type: $($results.Tests["Tunnel_Type"]) | |
| - Path MTU (to DU): $($results.Tests["Max_MTU"]) bytes | |
| - Recommended MTU: $($results.Tests["Recommended_Safe_MTU"]) | |
| - Default Gateway: $($results.Tests["Default_Gateway"]) | |
| - Mobile Hotspot: $($results.Tests["Mobile_Hotspot"]) | |
| - Banner TCP/$BannerPort`: $($results.Tests["Banner_TCP_$BannerPort"]) | |
| See JSON file for full details. | |
| "@ | |
| # Build gist payload | |
| $gistFiles = @{ | |
| "GP-Results-Summary.txt" = @{ content = $summaryContent } | |
| "GP-Results-Full.json" = @{ content = $jsonContent } | |
| } | |
| # Add PanGPS.log snippet if available | |
| $gpLogPath = "C:\Program Files\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| if (Test-Path $gpLogPath) { | |
| $logTail = Get-Content $gpLogPath -Tail 200 -ErrorAction SilentlyContinue | Out-String | |
| $gistFiles["PanGPS-tail.log"] = @{ content = $logTail } | |
| } | |
| $gistBody = @{ | |
| description = "GP Troubleshooting - $($results.Hostname) - $timestamp" | |
| public = $false | |
| files = $gistFiles | |
| } | ConvertTo-Json -Depth 5 | |
| $response = Invoke-RestMethod -Uri "https://api.github.com/gists" ` | |
| -Method Post ` | |
| -Headers @{ | |
| Authorization = "token $GitHubToken" | |
| Accept = "application/vnd.github.v3+json" | |
| } ` | |
| -Body $gistBody ` | |
| -ContentType "application/json" | |
| Write-Host "Gist created successfully!" -ForegroundColor $colors.Success | |
| Write-Host "URL: $($response.html_url)" -ForegroundColor $colors.Info | |
| Write-Host "`nShare this URL to provide diagnostic results." -ForegroundColor Gray | |
| # Copy URL to clipboard | |
| $response.html_url | Set-Clipboard | |
| Write-Host "(URL copied to clipboard)" -ForegroundColor DarkGray | |
| $results.Tests["Gist_URL"] = $response.html_url | |
| } catch { | |
| Write-Host "Failed to create gist: $($_.Exception.Message)" -ForegroundColor $colors.Failure | |
| if ($_.Exception.Response) { | |
| $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream()) | |
| Write-Host $reader.ReadToEnd() -ForegroundColor Gray | |
| } | |
| } | |
| } | |
| } | |
| Write-Host "`n" + "="*60 -ForegroundColor $colors.Info | |
| Write-Host " Troubleshooting complete - $(Get-Date -Format 'HH:mm:ss')" -ForegroundColor DarkGray | |
| Write-Host "="*60 + "`n" -ForegroundColor $colors.Info | |
| # Return results object for pipeline use | |
| return $results |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| GlobalProtect IPSec/MTU Troubleshooting Script | |
| .DESCRIPTION | |
| Diagnoses connectivity issues with GlobalProtect VPN, focusing on | |
| MTU/fragmentation problems common with IPSec tunnels. | |
| Common culprits: CenturyLink/PPPoE (MTU 1492), T-Mobile, hotel networks, | |
| or any ISP with underlying tunneling that reduces effective MTU. | |
| .PARAMETER ResetWinsock | |
| Resets Winsock catalog to clean state. Requires restart. | |
| .PARAMETER ResetNetworkStack | |
| Full network stack reset (Winsock + IP + DNS cache). Requires restart. | |
| .PARAMETER ResetDNSOnly | |
| Flushes DNS cache only. No restart required. | |
| .PARAMETER CollectLogs | |
| Copies all GP logs to desktop for analysis. | |
| .PARAMETER GPGateway | |
| GlobalProtect gateway IP/hostname for UDP 4501 IPSec test. | |
| .PARAMETER BannerHost | |
| Banner ERP server hostname (default: apps28.du.edu) | |
| .PARAMETER BannerPort | |
| Banner ERP port (default: 8445) | |
| .NOTES | |
| University of Denver - Information Security | |
| Run as Administrator for full functionality and reset options. | |
| #> | |
| [CmdletBinding()] | |
| param( | |
| [string[]]$TestHosts = @("login.du.edu", "du.edu"), | |
| [string]$InternalDNS = "130.253.2.14", | |
| [string]$GPGateway = "", | |
| [string]$BannerHost = "apps28.du.edu", | |
| [int]$BannerPort = 8445, | |
| [switch]$ExportResults, | |
| [switch]$ResetWinsock, | |
| [switch]$ResetNetworkStack, | |
| [switch]$ResetDNSOnly, | |
| [switch]$CollectLogs | |
| ) | |
| # Colors for output | |
| $colors = @{ | |
| Success = "Green" | |
| Failure = "Red" | |
| Warning = "Yellow" | |
| Info = "Cyan" | |
| } | |
| # ============================================================ | |
| # ADMIN CHECK & NETWORK RESETS | |
| # ============================================================ | |
| $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) | |
| # Handle reset operations first (if requested) | |
| if ($ResetDNSOnly) { | |
| Write-Host "`n[DNS CACHE FLUSH]" -ForegroundColor Cyan | |
| Write-Host "-"*40 | |
| ipconfig /flushdns | |
| Clear-DnsClientCache | |
| Write-Host "`nDNS cache flushed. No restart required." -ForegroundColor Green | |
| Write-Host "Continuing with diagnostics...`n" -ForegroundColor Gray | |
| } | |
| if ($ResetWinsock -or $ResetNetworkStack) { | |
| if (-not $isAdmin) { | |
| Write-Host "`n[ERROR] Network reset operations require Administrator privileges." -ForegroundColor Red | |
| Write-Host "Please re-run PowerShell as Administrator and try again." -ForegroundColor Yellow | |
| Write-Host "`nTo run as admin: Right-click PowerShell > 'Run as administrator'`n" -ForegroundColor Gray | |
| exit 1 | |
| } | |
| Write-Host "`n" + "="*60 -ForegroundColor Yellow | |
| Write-Host " NETWORK STACK RESET OPERATIONS" -ForegroundColor Yellow | |
| Write-Host "="*60 -ForegroundColor Yellow | |
| Write-Host "`nWARNING: These operations will require a system restart." -ForegroundColor Yellow | |
| Write-Host "Press Ctrl+C within 10 seconds to cancel...`n" -ForegroundColor Yellow | |
| Start-Sleep -Seconds 10 | |
| Write-Host "[1/6] Flushing DNS cache..." -ForegroundColor Cyan | |
| ipconfig /flushdns | Out-Null | |
| Clear-DnsClientCache | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[2/6] Releasing IP address..." -ForegroundColor Cyan | |
| ipconfig /release | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[3/6] Resetting Winsock catalog..." -ForegroundColor Cyan | |
| netsh winsock reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| if ($ResetNetworkStack) { | |
| Write-Host "[4/6] Resetting TCP/IP stack..." -ForegroundColor Cyan | |
| netsh int ip reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[5/6] Resetting IPv6 stack..." -ForegroundColor Cyan | |
| netsh int ipv6 reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| Write-Host "[6/6] Resetting Winsock2 registry keys..." -ForegroundColor Cyan | |
| netsh int tcp reset | Out-Null | |
| Write-Host " Done." -ForegroundColor Green | |
| } else { | |
| Write-Host "[4-6] Skipped (use -ResetNetworkStack for full reset)" -ForegroundColor DarkGray | |
| } | |
| Write-Host "`n" + "="*60 -ForegroundColor Green | |
| Write-Host " RESET COMPLETE" -ForegroundColor Green | |
| Write-Host "="*60 -ForegroundColor Green | |
| $restart = Read-Host "`nRestart now? (Y/N)" | |
| if ($restart -eq 'Y' -or $restart -eq 'y') { | |
| Write-Host "Restarting in 5 seconds..." -ForegroundColor Yellow | |
| Start-Sleep -Seconds 5 | |
| Restart-Computer -Force | |
| } else { | |
| Write-Host "`nPlease restart the computer manually before testing connectivity." -ForegroundColor Yellow | |
| Write-Host "Run this script again after restart (without reset flags) to diagnose.`n" -ForegroundColor Gray | |
| exit 0 | |
| } | |
| } | |
| # Check admin status for informational purposes | |
| if (-not $isAdmin) { | |
| Write-Host "[Note] Running without admin rights - some tests may be limited" -ForegroundColor DarkYellow | |
| Write-Host " Reset options require admin: -ResetWinsock, -ResetNetworkStack`n" -ForegroundColor DarkGray | |
| } | |
| # ============================================================ | |
| # LOG COLLECTION (if requested) | |
| # ============================================================ | |
| if ($CollectLogs) { | |
| Write-Host "`n[LOG COLLECTION]" -ForegroundColor Cyan | |
| Write-Host "-"*40 | |
| $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" | |
| $logDest = "$env:USERPROFILE\Desktop\GP-Logs-$timestamp" | |
| New-Item -ItemType Directory -Path $logDest -Force | Out-Null | |
| # GP Installation directory logs | |
| $gpInstallPath = "C:\Program Files\Palo Alto Networks\GlobalProtect" | |
| if (Test-Path $gpInstallPath) { | |
| Write-Host "Copying logs from installation directory..." -ForegroundColor Gray | |
| Copy-Item "$gpInstallPath\*.log" $logDest -ErrorAction SilentlyContinue | |
| } | |
| # GP User AppData logs | |
| $gpUserPath = "$env:LOCALAPPDATA\Palo Alto Networks\GlobalProtect" | |
| if (Test-Path $gpUserPath) { | |
| Write-Host "Copying logs from user AppData..." -ForegroundColor Gray | |
| Copy-Item "$gpUserPath\*.log" $logDest -ErrorAction SilentlyContinue | |
| Copy-Item "$gpUserPath\*.dat" $logDest -ErrorAction SilentlyContinue | |
| } | |
| # Network diagnostics | |
| Write-Host "Capturing network diagnostics..." -ForegroundColor Gray | |
| ipconfig /all > "$logDest\ipconfig.txt" | |
| route print > "$logDest\routes.txt" | |
| netsh interface ipv4 show subinterfaces > "$logDest\mtu-interfaces.txt" | |
| netsh interface ipv4 show destinationcache > "$logDest\mtu-destinationcache.txt" | |
| netsh winsock show catalog > "$logDest\winsock-catalog.txt" | |
| netsh interface tcp show global > "$logDest\tcp-global.txt" | |
| Get-NetAdapter | Format-List * > "$logDest\adapters.txt" | |
| Get-NetAdapterStatistics | Format-List * > "$logDest\adapter-stats.txt" 2>$null | |
| Get-Service | Where-Object { $_.Name -match "Dnscache|Dhcp|NlaSvc|netprofm|Netman|PanGPS" } | Format-List * > "$logDest\services.txt" | |
| Write-Host "`nLogs collected to: $logDest" -ForegroundColor Green | |
| Write-Host "Continuing with diagnostics...`n" -ForegroundColor Gray | |
| } | |
| function Write-Status { | |
| param([string]$Message, [string]$Status, [string]$Color = "White") | |
| $timestamp = Get-Date -Format "HH:mm:ss" | |
| Write-Host "[$timestamp] " -NoNewline -ForegroundColor DarkGray | |
| Write-Host "$Message " -NoNewline | |
| if ($Status) { | |
| Write-Host "[$Status]" -ForegroundColor $Color | |
| } else { | |
| Write-Host "" | |
| } | |
| } | |
| function Get-MTUSize { | |
| param([string]$Target, [int]$StartSize = 1500, [int]$MinSize = 1200) | |
| $size = $StartSize | |
| $lastGood = $MinSize | |
| while ($size -ge $MinSize) { | |
| $result = ping -n 1 -f -l $size $Target 2>&1 | |
| if ($result -match "Reply from" -and $result -notmatch "fragmented") { | |
| $lastGood = $size | |
| break | |
| } | |
| $size -= 10 | |
| } | |
| return $lastGood | |
| } | |
| # Banner | |
| Write-Host "`n" + "="*60 -ForegroundColor $colors.Info | |
| Write-Host " GlobalProtect VPN Troubleshooting Script" -ForegroundColor $colors.Info | |
| Write-Host " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor DarkGray | |
| Write-Host "="*60 + "`n" -ForegroundColor $colors.Info | |
| $results = [ordered]@{ | |
| Timestamp = Get-Date -Format "o" | |
| Hostname = $env:COMPUTERNAME | |
| Username = $env:USERNAME | |
| Tests = @{} | |
| } | |
| # ============================================================ | |
| # SECTION 1: GlobalProtect Status | |
| # ============================================================ | |
| Write-Host "`n[1] GLOBALPROTECT STATUS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Check GP Service | |
| $gpService = Get-Service -Name "PanGPS" -ErrorAction SilentlyContinue | |
| if ($gpService) { | |
| $status = if ($gpService.Status -eq "Running") { "Running"; "Success" } else { "Stopped"; "Failure" } | |
| Write-Status "GlobalProtect Service (PanGPS)" $gpService.Status $colors[$status[1]] | |
| $results.Tests["GP_Service"] = $gpService.Status | |
| } else { | |
| Write-Status "GlobalProtect Service" "NOT FOUND" $colors.Failure | |
| } | |
| # Check GP Network Adapter | |
| $gpAdapter = Get-NetAdapter | Where-Object { $_.InterfaceDescription -like "*PANGP*" -or $_.Name -like "*GlobalProtect*" } | |
| if ($gpAdapter) { | |
| Write-Status "GP Virtual Adapter" $gpAdapter.Status $colors.Success | |
| Write-Status " Adapter Name" $gpAdapter.Name "White" | |
| Write-Status " Link Speed" $gpAdapter.LinkSpeed "White" | |
| # Get adapter MTU | |
| $adapterMTU = (Get-NetIPInterface -InterfaceIndex $gpAdapter.ifIndex -ErrorAction SilentlyContinue).NlMtu | Select-Object -First 1 | |
| Write-Status " Adapter MTU" $adapterMTU $(if ($adapterMTU -lt 1400) { $colors.Warning } else { "White" }) | |
| $results.Tests["GP_Adapter_MTU"] = $adapterMTU | |
| # Get adapter IP | |
| $gpIP = (Get-NetIPAddress -InterfaceIndex $gpAdapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).IPAddress | |
| if ($gpIP) { | |
| Write-Status " Tunnel IP" $gpIP "White" | |
| $results.Tests["GP_Tunnel_IP"] = $gpIP | |
| } | |
| } else { | |
| Write-Status "GP Virtual Adapter" "NOT FOUND (not connected?)" $colors.Warning | |
| } | |
| # Check for SSL-Only preference | |
| $gpUserPrefs = "$env:LOCALAPPDATA\Palo Alto Networks\GlobalProtect" | |
| $sslOnlyPref = $false | |
| if (Test-Path "$gpUserPrefs\PanPortalCfg_*.dat") { | |
| # Check if SSL-only might be configured (heuristic) | |
| $prefFiles = Get-ChildItem "$gpUserPrefs\PanPortalCfg_*.dat" -ErrorAction SilentlyContinue | |
| if ($prefFiles) { | |
| Write-Status "Portal config cached" "Yes" "White" | |
| } | |
| } | |
| # Check registry for GP settings | |
| $gpRegPath = "HKLM:\SOFTWARE\Palo Alto Networks\GlobalProtect\Settings" | |
| if (Test-Path $gpRegPath) { | |
| $gpSettings = Get-ItemProperty -Path $gpRegPath -ErrorAction SilentlyContinue | |
| if ($gpSettings.Portal) { | |
| Write-Status "Configured Portal" $gpSettings.Portal "White" | |
| $results.Tests["GP_Portal"] = $gpSettings.Portal | |
| } | |
| } | |
| # Check connection type from GP logs if accessible | |
| $gpLogPath = "C:\Program Files\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| if (-not (Test-Path $gpLogPath)) { | |
| $gpLogPath = "$env:ProgramData\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| } | |
| if (Test-Path $gpLogPath) { | |
| $recentLog = Get-Content $gpLogPath -Tail 100 -ErrorAction SilentlyContinue | |
| $connType = if ($recentLog -match "IPSec") { "IPSec" } | |
| elseif ($recentLog -match "SSL") { "SSL" } | |
| else { "Unknown" } | |
| # SSL is fine if on low MTU network/policy, IPSec is preferred otherwise | |
| $gpMtu = (Get-NetIPInterface -InterfaceIndex $gpAdapter.ifIndex -ErrorAction SilentlyContinue).NlMtu | Select-Object -First 1 | |
| $typeColor = if ($connType -eq "SSL" -and $gpMtu -le 1300) { $colors.Success } | |
| elseif ($connType -eq "SSL") { $colors.Warning } | |
| elseif ($connType -eq "IPSec") { $colors.Success } | |
| else { "White" } | |
| Write-Status "Detected Tunnel Type" $connType $typeColor | |
| $results.Tests["Tunnel_Type"] = $connType | |
| } | |
| # ============================================================ | |
| # SECTION 1.5: GLOBALPROTECT LOG ANALYSIS | |
| # ============================================================ | |
| Write-Host "`n[1.5] GLOBALPROTECT LOG ANALYSIS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| $gpLogPaths = @( | |
| "C:\Program Files\Palo Alto Networks\GlobalProtect\PanGPS.log", | |
| "$env:ProgramData\Palo Alto Networks\GlobalProtect\PanGPS.log" | |
| ) | |
| $gpLog = $null | |
| foreach ($path in $gpLogPaths) { | |
| if (Test-Path $path) { | |
| $gpLog = Get-Content $path -Tail 500 -ErrorAction SilentlyContinue | |
| Write-Status "Found PanGPS.log" $path "White" | |
| break | |
| } | |
| } | |
| if ($gpLog) { | |
| # Check for IPSec vs SSL indicators | |
| $ipsecAttempts = ($gpLog | Select-String -Pattern "keep alive to ipsec socket" -AllMatches).Matches.Count | |
| $sslFallback = ($gpLog | Select-String -Pattern "SSL tunnel|SSL connection" -AllMatches).Matches.Count | |
| $ipsecSuccess = ($gpLog | Select-String -Pattern "IPSec tunnel established|ESP" -AllMatches).Matches.Count | |
| Write-Status "IPSec keep-alive attempts (recent)" $ipsecAttempts "White" | |
| Write-Status "SSL tunnel indicators" $sslFallback $(if ($sslFallback -gt 0) { $colors.Warning } else { "White" }) | |
| if ($ipsecAttempts -gt 0 -and $sslFallback -gt 0 -and $ipsecSuccess -eq 0) { | |
| Write-Host " [!] Pattern suggests IPSec failed, fell back to SSL" -ForegroundColor $colors.Warning | |
| $results.Tests["IPSec_Fallback_Detected"] = $true | |
| } | |
| # Check for fragmentation in logs | |
| $fragCount = ($gpLog | Select-String -Pattern "fragment 0x" -AllMatches).Matches.Count | |
| if ($fragCount -gt 0) { | |
| Write-Status "Fragmentation events in log" $fragCount $colors.Warning | |
| $results.Tests["Fragmentation_In_Logs"] = $fragCount | |
| } else { | |
| Write-Status "Fragmentation events in log" "None detected" $colors.Success | |
| } | |
| # Check MTU settings applied | |
| $mtuSettings = $gpLog | Select-String -Pattern "set VA MTU|set MTU:" | Select-Object -Last 3 | |
| if ($mtuSettings) { | |
| Write-Host "`n Recent MTU settings applied:" -ForegroundColor White | |
| foreach ($line in $mtuSettings) { | |
| Write-Host " $($line.Line.Trim())" -ForegroundColor Gray | |
| } | |
| } | |
| # Check for connection errors | |
| $connErrors = $gpLog | Select-String -Pattern "error|failed|timeout" -AllMatches | Select-Object -Last 5 | |
| if ($connErrors) { | |
| Write-Host "`n Recent errors/warnings:" -ForegroundColor $colors.Warning | |
| foreach ($err in $connErrors) { | |
| $shortErr = $err.Line.Trim() | |
| if ($shortErr.Length -gt 80) { $shortErr = $shortErr.Substring(0, 77) + "..." } | |
| Write-Host " $shortErr" -ForegroundColor Gray | |
| } | |
| } | |
| # Extract gateway IP if present | |
| $gwMatch = $gpLog | Select-String -Pattern "Connected to: (\d+\.\d+\.\d+\.\d+)" | Select-Object -Last 1 | |
| if ($gwMatch -and $gwMatch.Matches.Groups[1]) { | |
| $detectedGW = $gwMatch.Matches.Groups[1].Value | |
| Write-Status "`n Detected Gateway IP" $detectedGW "White" | |
| $results.Tests["Detected_Gateway"] = $detectedGW | |
| # Use detected gateway for UDP test if not specified | |
| if (-not $GPGateway) { | |
| $GPGateway = $detectedGW | |
| } | |
| } | |
| } else { | |
| Write-Status "PanGPS.log" "NOT FOUND - GP may not be installed" $colors.Warning | |
| } | |
| # ============================================================ | |
| # SECTION 1.7: NETWORK STACK HEALTH CHECK | |
| # ============================================================ | |
| Write-Host "`n[1.7] NETWORK STACK HEALTH CHECK" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Check critical network services | |
| $criticalServices = @( | |
| @{ Name = "Dnscache"; Display = "DNS Client" }, | |
| @{ Name = "Dhcp"; Display = "DHCP Client" }, | |
| @{ Name = "NlaSvc"; Display = "Network Location Awareness" }, | |
| @{ Name = "netprofm"; Display = "Network List Service" }, | |
| @{ Name = "Netman"; Display = "Network Connections" } | |
| ) | |
| Write-Host "Network Services:" -ForegroundColor White | |
| $serviceIssues = 0 | |
| foreach ($svc in $criticalServices) { | |
| $service = Get-Service -Name $svc.Name -ErrorAction SilentlyContinue | |
| if ($service) { | |
| $status = if ($service.Status -eq "Running") { "Running"; $colors.Success } else { "STOPPED"; $colors.Failure; $serviceIssues++ } | |
| Write-Status " $($svc.Display)" $status[0] $status[1] | |
| } else { | |
| Write-Status " $($svc.Display)" "NOT FOUND" $colors.Warning | |
| } | |
| } | |
| $results.Tests["Network_Service_Issues"] = $serviceIssues | |
| # Check Winsock LSP catalog for corruption/third-party hooks | |
| Write-Host "`nWinsock Catalog:" -ForegroundColor White | |
| $winsockCatalog = netsh winsock show catalog 2>&1 | |
| $lspCount = ($winsockCatalog | Select-String -Pattern "Layered Chain Entry" -AllMatches).Matches.Count | |
| $nonMsLsp = ($winsockCatalog | Select-String -Pattern "Provider Path.*(?<!Microsoft|Windows)" -AllMatches).Matches.Count | |
| Write-Status " Layered Service Providers" $lspCount $(if ($lspCount -gt 10) { $colors.Warning } else { "White" }) | |
| # Check for known problematic LSPs | |
| $problematicLSPs = @("avgnt", "NOD32", "Kaspersky", "Avast", "Norton", "McAfee", "WebRoot", "Symantec") | |
| $foundProblematic = @() | |
| foreach ($lsp in $problematicLSPs) { | |
| if ($winsockCatalog -match $lsp) { | |
| $foundProblematic += $lsp | |
| } | |
| } | |
| if ($foundProblematic.Count -gt 0) { | |
| Write-Status " Third-party LSPs detected" ($foundProblematic -join ", ") $colors.Warning | |
| $results.Tests["ThirdParty_LSPs"] = $foundProblematic | |
| } else { | |
| Write-Status " Third-party LSPs" "None detected" $colors.Success | |
| } | |
| # Check network adapter statistics for errors | |
| Write-Host "`nAdapter Health:" -ForegroundColor White | |
| $adapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" } | |
| foreach ($adapter in $adapters) { | |
| $stats = Get-NetAdapterStatistics -Name $adapter.Name -ErrorAction SilentlyContinue | |
| if ($stats) { | |
| $errors = $stats.InErrors + $stats.OutErrors | |
| $discards = $stats.InDiscards + $stats.OutDiscards | |
| $adapterHealth = if ($errors -gt 100 -or $discards -gt 100) { "Issues"; $colors.Warning } | |
| elseif ($errors -gt 0 -or $discards -gt 0) { "Minor errors"; $colors.Info } | |
| else { "Healthy"; $colors.Success } | |
| Write-Status " $($adapter.Name)" $adapterHealth[0] $adapterHealth[1] | |
| if ($errors -gt 0 -or $discards -gt 0) { | |
| Write-Host " Errors: $errors | Discards: $discards" -ForegroundColor Gray | |
| } | |
| $results.Tests["Adapter_$($adapter.Name)_Errors"] = $errors | |
| $results.Tests["Adapter_$($adapter.Name)_Discards"] = $discards | |
| } | |
| } | |
| # Check TCP/IP parameters | |
| Write-Host "`nTCP/IP Parameters:" -ForegroundColor White | |
| # Check for TCP auto-tuning (can cause issues with some VPNs) | |
| $autoTuning = netsh interface tcp show global 2>&1 | Out-String | |
| $autoTuningLevel = "Unknown" | |
| if ($autoTuning -match "Receive Window Auto-Tuning Level\s*:\s*(\w+)") { | |
| $autoTuningLevel = $Matches[1] | |
| } | |
| Write-Status " TCP Auto-Tuning" $autoTuningLevel $(if ($autoTuningLevel -eq "disabled") { $colors.Warning } else { "White" }) | |
| # Check ECN capability | |
| $ecn = "Unknown" | |
| if ($autoTuning -match "ECN Capability\s*:\s*(\w+)") { | |
| $ecn = $Matches[1] | |
| } | |
| Write-Status " ECN Capability" $ecn "White" | |
| # Check for RSS (Receive Side Scaling) | |
| $rss = "Unknown" | |
| if ($autoTuning -match "Receive-Side Scaling State\s*:\s*(\w+)") { | |
| $rss = $Matches[1] | |
| } | |
| Write-Status " Receive-Side Scaling" $rss "White" | |
| # Check proxy settings (can interfere with VPN) | |
| Write-Host "`nProxy Settings:" -ForegroundColor White | |
| $proxyReg = Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings" -ErrorAction SilentlyContinue | |
| if ($proxyReg.ProxyEnable -eq 1) { | |
| Write-Status " System Proxy" "ENABLED - $($proxyReg.ProxyServer)" $colors.Warning | |
| $results.Tests["Proxy_Enabled"] = $true | |
| $results.Tests["Proxy_Server"] = $proxyReg.ProxyServer | |
| } else { | |
| Write-Status " System Proxy" "Disabled" $colors.Success | |
| $results.Tests["Proxy_Enabled"] = $false | |
| } | |
| # Check for VPN-related WFP filters that might cause issues | |
| Write-Host "`nNetwork Filters:" -ForegroundColor White | |
| $wfpFilters = Get-NetFirewallProfile -ErrorAction SilentlyContinue | |
| foreach ($profile in $wfpFilters) { | |
| $status = if ($profile.Enabled) { "Enabled"; "White" } else { "Disabled"; $colors.Warning } | |
| Write-Status " Windows Firewall ($($profile.Name))" $status[0] $status[1] | |
| } | |
| # Check for third-party network filter drivers | |
| $filterDrivers = Get-CimInstance -ClassName Win32_SystemDriver -ErrorAction SilentlyContinue | | |
| Where-Object { $_.DisplayName -match "filter|firewall|vpn|tunnel" -and $_.State -eq "Running" -and $_.DisplayName -notmatch "Microsoft|Windows|PANGP|GlobalProtect" } | | |
| Select-Object -First 5 | |
| if ($filterDrivers) { | |
| Write-Host " Third-party network drivers:" -ForegroundColor $colors.Warning | |
| foreach ($drv in $filterDrivers) { | |
| Write-Host " - $($drv.DisplayName)" -ForegroundColor Gray | |
| } | |
| $results.Tests["ThirdParty_Drivers"] = $filterDrivers.DisplayName | |
| } | |
| # Check for IP forwarding (unusual for workstations) | |
| $ipForward = (Get-NetIPInterface | Where-Object { $_.Forwarding -eq "Enabled" }).InterfaceAlias | |
| if ($ipForward) { | |
| Write-Status " IP Forwarding" "Enabled on: $($ipForward -join ', ')" $colors.Warning | |
| $results.Tests["IP_Forwarding"] = $ipForward | |
| } | |
| # Check hosts file for suspicious entries | |
| $hostsFile = Get-Content "$env:SystemRoot\System32\drivers\etc\hosts" -ErrorAction SilentlyContinue | | |
| Where-Object { $_ -notmatch "^\s*#" -and $_ -match "\S" } | |
| $hostsEntries = ($hostsFile | Measure-Object).Count | |
| if ($hostsEntries -gt 5) { | |
| Write-Status " Custom hosts entries" "$hostsEntries entries" $colors.Warning | |
| } else { | |
| Write-Status " Custom hosts entries" "$hostsEntries entries" "White" | |
| } | |
| $results.Tests["Hosts_Entries"] = $hostsEntries | |
| # Check IPv6 status (can sometimes cause VPN issues) | |
| $ipv6Adapters = Get-NetAdapterBinding -ComponentID ms_tcpip6 -ErrorAction SilentlyContinue | | |
| Where-Object { $_.Enabled -eq $true } | |
| $ipv6Count = ($ipv6Adapters | Measure-Object).Count | |
| Write-Status " IPv6 enabled adapters" $ipv6Count "White" | |
| # Quick connectivity sanity check - can we reach the gateway? | |
| $defaultGateway = (Get-NetRoute -DestinationPrefix "0.0.0.0/0" -ErrorAction SilentlyContinue | | |
| Sort-Object RouteMetric | Select-Object -First 1).NextHop | |
| if ($defaultGateway) { | |
| $gwPing = Test-Connection -ComputerName $defaultGateway -Count 1 -Quiet -ErrorAction SilentlyContinue | |
| $gwStatus = if ($gwPing) { "Reachable"; $colors.Success } else { "Unreachable"; $colors.Failure } | |
| Write-Status " Default Gateway ($defaultGateway)" $gwStatus[0] $gwStatus[1] | |
| $results.Tests["Default_Gateway_Reachable"] = $gwPing | |
| $results.Tests["Default_Gateway"] = $defaultGateway | |
| # Detect mobile hotspot based on common gateway ranges | |
| $hotspotRanges = @( | |
| "172.20.10.", # iPhone hotspot | |
| "192.168.43.", # Android hotspot | |
| "192.168.137.", # Windows Mobile hotspot | |
| "10.0.0." # Some carrier hotspots | |
| ) | |
| $isHotspot = $false | |
| foreach ($range in $hotspotRanges) { | |
| if ($defaultGateway.StartsWith($range)) { | |
| $isHotspot = $true | |
| break | |
| } | |
| } | |
| if ($isHotspot) { | |
| Write-Status " [!] Mobile Hotspot Detected" "MTU will be severely constrained" $colors.Warning | |
| $results.Tests["Mobile_Hotspot"] = $true | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 1.6: UDP 4501 (IPSec) CONNECTIVITY TEST | |
| # ============================================================ | |
| if ($GPGateway) { | |
| Write-Host "`n[1.6] UDP 4501 (IPSec) CONNECTIVITY TEST" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| Write-Host "Testing IPSec port to gateway: $GPGateway" -ForegroundColor White | |
| # UDP 4501 test | |
| try { | |
| $udpClient = New-Object System.Net.Sockets.UdpClient | |
| $udpClient.Client.ReceiveTimeout = 3000 | |
| $udpClient.Connect($GPGateway, 4501) | |
| # Send a small packet | |
| $bytes = [System.Text.Encoding]::ASCII.GetBytes("test") | |
| $udpClient.Send($bytes, $bytes.Length) | Out-Null | |
| Write-Status "UDP 4501 send" "Packet sent (no guarantee of delivery)" $colors.Info | |
| $udpClient.Close() | |
| $results.Tests["UDP_4501_Send"] = $true | |
| } catch { | |
| Write-Status "UDP 4501 test" "Failed - $($_.Exception.Message)" $colors.Failure | |
| $results.Tests["UDP_4501_Send"] = $false | |
| } | |
| # Check if any firewall rules might block UDP 4501 | |
| $fwRules = Get-NetFirewallRule -Direction Outbound -Action Block -Enabled True -ErrorAction SilentlyContinue | | |
| Where-Object { $_.DisplayName -match "GlobalProtect|UDP|4501" } | |
| if ($fwRules) { | |
| Write-Status "Potential blocking FW rules" $fwRules.Count $colors.Warning | |
| } | |
| # Test TCP 443 to same gateway for comparison | |
| $tcp443 = Test-NetConnection -ComputerName $GPGateway -Port 443 -WarningAction SilentlyContinue | |
| $tcpStatus = if ($tcp443.TcpTestSucceeded) { "Open"; $colors.Success } else { "Closed"; $colors.Failure } | |
| Write-Status "TCP 443 (SSL fallback port)" $tcpStatus[0] $tcpStatus[1] | |
| $results.Tests["Gateway_TCP_443"] = $tcp443.TcpTestSucceeded | |
| } | |
| # ============================================================ | |
| # SECTION 2: Network Configuration | |
| # ============================================================ | |
| Write-Host "`n[2] NETWORK CONFIGURATION" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Check for direct PPPoE connections (rare - usually on modem/router) | |
| $pppoeAdapter = Get-NetAdapter | Where-Object { $_.InterfaceDescription -like "*PPP*" -or $_.Name -like "*PPP*" } | |
| if ($pppoeAdapter) { | |
| Write-Status "Direct PPPoE Connection" "Detected on this machine" $colors.Warning | |
| Write-Host " PPPoE has MTU of 1492 (8 bytes less than standard)" -ForegroundColor Gray | |
| $results.Tests["PPPoE_Direct"] = $true | |
| } | |
| # Check WAN connection type via interface metrics | |
| $wanAdapter = Get-NetAdapter | Where-Object { $_.Status -eq "Up" -and $_.InterfaceDescription -notlike "*Virtual*" -and $_.InterfaceDescription -notlike "*PANGP*" } | | |
| Sort-Object -Property @{Expression={$_.LinkSpeed}; Descending=$true} | Select-Object -First 1 | |
| if ($wanAdapter) { | |
| Write-Status "Primary Network Adapter" $wanAdapter.Name "White" | |
| Write-Status " Type" $wanAdapter.InterfaceDescription "Gray" | |
| # Check if WiFi | |
| if ($wanAdapter.InterfaceDescription -match "Wi-?Fi|Wireless|802\.11") { | |
| Write-Status " Connection" "Wireless (check upstream ISP for MTU constraints)" "Gray" | |
| $results.Tests["Connection_Type"] = "WiFi" | |
| } | |
| } | |
| # Get all active adapters and their MTUs | |
| $activeAdapters = Get-NetAdapter | Where-Object { $_.Status -eq "Up" } | |
| foreach ($adapter in $activeAdapters) { | |
| $mtu = (Get-NetIPInterface -InterfaceIndex $adapter.ifIndex -AddressFamily IPv4 -ErrorAction SilentlyContinue).NlMtu | |
| $mtuColor = if ($mtu -lt 1400) { $colors.Warning } elseif ($mtu -eq 1500) { $colors.Success } else { "White" } | |
| Write-Status "$($adapter.Name)" "MTU: $mtu" $mtuColor | |
| } | |
| # DNS Configuration | |
| Write-Host "`nDNS Servers Configured:" -ForegroundColor White | |
| $dnsServers = Get-DnsClientServerAddress -AddressFamily IPv4 | | |
| Where-Object { $_.ServerAddresses } | | |
| Select-Object InterfaceAlias, ServerAddresses | |
| foreach ($dns in $dnsServers) { | |
| Write-Host " $($dns.InterfaceAlias): $($dns.ServerAddresses -join ', ')" -ForegroundColor Gray | |
| } | |
| $results.Tests["DNS_Servers"] = $dnsServers | |
| # ============================================================ | |
| # SECTION 3: DNS Resolution Tests | |
| # ============================================================ | |
| Write-Host "`n[3] DNS RESOLUTION TESTS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| foreach ($target in $TestHosts) { | |
| Write-Host "`nTesting: $target" -ForegroundColor White | |
| # Test with system DNS | |
| Write-Status " System DNS resolver..." -Status "" | |
| $dnsResult = $null | |
| $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() | |
| try { | |
| $dnsResult = Resolve-DnsName -Name $target -Type A -DnsOnly -ErrorAction Stop | |
| $stopwatch.Stop() | |
| $ip = ($dnsResult | Where-Object { $_.Type -eq "A" }).IPAddress | Select-Object -First 1 | |
| Write-Status " Result" "$ip (${$stopwatch.ElapsedMilliseconds}ms)" $colors.Success | |
| $results.Tests["DNS_System_$target"] = @{ IP = $ip; Time = $stopwatch.ElapsedMilliseconds } | |
| } catch { | |
| $stopwatch.Stop() | |
| Write-Status " Result" "FAILED - $($_.Exception.Message)" $colors.Failure | |
| $results.Tests["DNS_System_$target"] = @{ Error = $_.Exception.Message } | |
| } | |
| # Test with specific internal DNS server | |
| Write-Status " Internal DNS ($InternalDNS)..." -Status "" | |
| $stopwatch.Restart() | |
| try { | |
| $dnsResult = Resolve-DnsName -Name $target -Type A -Server $InternalDNS -DnsOnly -ErrorAction Stop | |
| $stopwatch.Stop() | |
| $ip = ($dnsResult | Where-Object { $_.Type -eq "A" }).IPAddress | Select-Object -First 1 | |
| Write-Status " Result" "$ip (${$stopwatch.ElapsedMilliseconds}ms)" $colors.Success | |
| $results.Tests["DNS_Internal_$target"] = @{ IP = $ip; Time = $stopwatch.ElapsedMilliseconds } | |
| } catch { | |
| $stopwatch.Stop() | |
| Write-Status " Result" "FAILED - Timeout or unreachable" $colors.Failure | |
| $results.Tests["DNS_Internal_$target"] = @{ Error = "Failed" } | |
| } | |
| # Direct nslookup for comparison (uses UDP) | |
| Write-Status " nslookup (UDP-based)..." -Status "" | |
| $nslookup = nslookup $target $InternalDNS 2>&1 | |
| if ($nslookup -match "Address.*\d+\.\d+\.\d+\.\d+" -and $nslookup -notmatch "timed out") { | |
| Write-Status " Result" "SUCCESS" $colors.Success | |
| } else { | |
| Write-Status " Result" "TIMEOUT/FAILED" $colors.Failure | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 4: Connectivity Tests | |
| # ============================================================ | |
| Write-Host "`n[4] CONNECTIVITY TESTS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| foreach ($target in $TestHosts) { | |
| Write-Host "`nTesting: $target" -ForegroundColor White | |
| # ICMP Ping (may be blocked on some hosts) | |
| $ping = Test-NetConnection -ComputerName $target -WarningAction SilentlyContinue | |
| $pingStatus = if ($ping.PingSucceeded) { "Success"; $colors.Success } else { "No response (may be filtered)"; $colors.Warning } | |
| Write-Status " ICMP Ping" $pingStatus[0] $pingStatus[1] | |
| if ($ping.PingSucceeded) { | |
| Write-Status " RTT" "$($ping.PingReplyDetails.RoundtripTime)ms" "White" | |
| } | |
| $results.Tests["Ping_$target"] = $ping.PingSucceeded | |
| # TCP 443 | |
| $tcp443 = Test-NetConnection -ComputerName $target -Port 443 -WarningAction SilentlyContinue | |
| $tcpStatus = if ($tcp443.TcpTestSucceeded) { "Open"; $colors.Success } else { "Closed/Filtered"; $colors.Failure } | |
| Write-Status " TCP/443 (HTTPS)" $tcpStatus[0] $tcpStatus[1] | |
| $results.Tests["TCP443_$target"] = $tcp443.TcpTestSucceeded | |
| } | |
| # Banner/Apps28 specific test on port 8445 | |
| if ($BannerHost) { | |
| Write-Host "`nTesting Banner: $BannerHost`:$BannerPort" -ForegroundColor White | |
| # DNS resolution first | |
| try { | |
| $bannerIP = (Resolve-DnsName -Name $BannerHost -Type A -DnsOnly -ErrorAction Stop | | |
| Where-Object { $_.Type -eq "A" }).IPAddress | Select-Object -First 1 | |
| Write-Status " DNS Resolution" $bannerIP $colors.Success | |
| $results.Tests["Banner_DNS"] = $bannerIP | |
| } catch { | |
| Write-Status " DNS Resolution" "FAILED" $colors.Failure | |
| $results.Tests["Banner_DNS"] = $null | |
| } | |
| # TCP port test | |
| $bannerConn = Test-NetConnection -ComputerName $BannerHost -Port $BannerPort -WarningAction SilentlyContinue | |
| $bannerStatus = if ($bannerConn.TcpTestSucceeded) { "Open"; $colors.Success } else { "Closed/Filtered"; $colors.Failure } | |
| Write-Status " TCP/$BannerPort" $bannerStatus[0] $bannerStatus[1] | |
| $results.Tests["Banner_TCP_$BannerPort"] = $bannerConn.TcpTestSucceeded | |
| if (-not $bannerConn.TcpTestSucceeded -and $results.Tests["Banner_DNS"]) { | |
| Write-Host " [!] DNS resolves but port unreachable - likely MTU/tunnel issue" -ForegroundColor $colors.Warning | |
| $results.Tests["Banner_MTU_Suspect"] = $true | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 5: MTU Discovery | |
| # ============================================================ | |
| Write-Host "`n[5] MTU PATH DISCOVERY" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| Write-Host "Testing maximum packet size without fragmentation..." -ForegroundColor Gray | |
| # Use login.du.edu for MTU test (responds to ICMP) | |
| $mtuTarget = "login.du.edu" | |
| $mtuSizes = @(1500, 1472, 1450, 1400, 1350, 1300, 1250, 1200) | |
| $maxWorking = 0 | |
| Write-Host "`nTarget: $mtuTarget" -ForegroundColor White | |
| Write-Host "Size`t`tResult" -ForegroundColor Gray | |
| Write-Host "-"*30 | |
| foreach ($size in $mtuSizes) { | |
| $pingTest = ping -n 1 -f -l $size -w 2000 $mtuTarget 2>&1 | Out-String | |
| if ($pingTest -match "Reply from" -and $pingTest -notmatch "fragmented") { | |
| Write-Host "$size bytes`t`tOK" -ForegroundColor $colors.Success | |
| if ($size -gt $maxWorking) { $maxWorking = $size } | |
| if ($size -eq 1500) { break } # No MTU issues | |
| } else { | |
| Write-Host "$size bytes`t`tFAILED" -ForegroundColor $colors.Failure | |
| } | |
| } | |
| $results.Tests["Max_MTU"] = $maxWorking | |
| # Calculate recommended tunnel MTU | |
| $recommendedMTU = $maxWorking - 28 # Account for IP (20) + ICMP (8) headers | |
| Write-Host "`nMaximum working payload: $maxWorking bytes" -ForegroundColor White | |
| Write-Host "Recommended tunnel MTU: $recommendedMTU bytes" -ForegroundColor $colors.Info | |
| # Infer upstream connection type from MTU results | |
| if ($maxWorking -ge 1464 -and $maxWorking -le 1472) { | |
| Write-Host "`n[DETECTED] Path MTU suggests upstream PPPoE (CenturyLink/DSL)" -ForegroundColor $colors.Warning | |
| Write-Host " PPPoE MTU (1492) - IP header (20) - ICMP (8) = 1464 max payload" -ForegroundColor Gray | |
| $results.Tests["Upstream_PPPoE_Inferred"] = $true | |
| } elseif ($maxWorking -lt 1350) { | |
| Write-Host "`n[DETECTED] Very low path MTU - likely mobile hotspot or carrier CGNAT" -ForegroundColor $colors.Warning | |
| Write-Host " Mobile carriers add significant tunneling overhead" -ForegroundColor Gray | |
| $results.Tests["Mobile_Carrier_Inferred"] = $true | |
| } elseif ($maxWorking -lt 1464) { | |
| Write-Host "`n[DETECTED] Path MTU lower than typical PPPoE - possible double-NAT or tunnel" -ForegroundColor $colors.Warning | |
| } | |
| if ($maxWorking -lt 1400) { | |
| Write-Host "`n*** MTU ISSUE DETECTED ***" -ForegroundColor $colors.Warning | |
| Write-Host "This client needs the Low-MTU GP policy" -ForegroundColor $colors.Warning | |
| Write-Host "IPSec adds ~50-70 bytes overhead, leaving insufficient room for DNS/SMB" -ForegroundColor Gray | |
| } | |
| # ============================================================ | |
| # SECTION 6: Route Analysis | |
| # ============================================================ | |
| Write-Host "`n[6] ROUTING ANALYSIS" -ForegroundColor $colors.Info | |
| Write-Host "-"*40 | |
| # Show routes through VPN | |
| $vpnRoutes = Get-NetRoute | Where-Object { | |
| $_.InterfaceAlias -like "*GlobalProtect*" -or | |
| $_.InterfaceAlias -like "*PANGP*" | |
| } | Select-Object DestinationPrefix, NextHop, InterfaceAlias, RouteMetric | Sort-Object DestinationPrefix | |
| if ($vpnRoutes) { | |
| Write-Host "Routes via GlobalProtect:" -ForegroundColor White | |
| $vpnRoutes | Format-Table -AutoSize | |
| } else { | |
| Write-Status "No routes through GP adapter" "Check tunnel" $colors.Warning | |
| } | |
| # Trace route to first test host | |
| Write-Host "Route to $($TestHosts[0]):" -ForegroundColor White | |
| $traceResult = Test-NetConnection -ComputerName $TestHosts[0] -TraceRoute -WarningAction SilentlyContinue | |
| if ($traceResult.TraceRoute) { | |
| $hopNum = 1 | |
| foreach ($hop in $traceResult.TraceRoute) { | |
| Write-Host " $hopNum. $hop" -ForegroundColor Gray | |
| $hopNum++ | |
| } | |
| } | |
| # ============================================================ | |
| # SECTION 7: Summary & Recommendations | |
| # ============================================================ | |
| Write-Host "`n" + "="*60 -ForegroundColor $colors.Info | |
| Write-Host " SUMMARY & RECOMMENDATIONS" -ForegroundColor $colors.Info | |
| Write-Host "="*60 -ForegroundColor $colors.Info | |
| $issues = @() | |
| # Analyze results | |
| if ($results.Tests["Max_MTU"] -lt 1400) { | |
| # Check if already on low-MTU policy | |
| $alreadyLowMtu = $results.Tests["GP_Adapter_MTU"] -and $results.Tests["GP_Adapter_MTU"] -le 1300 | |
| $severity = if ($alreadyLowMtu) { "INFO" } else { "HIGH" } | |
| $fix = if ($alreadyLowMtu) { | |
| "Already on Low-MTU policy (GP MTU: $($results.Tests['GP_Adapter_MTU'])). Current network constrains to ~$($results.Tests['Max_MTU']) bytes." | |
| } else { | |
| "Move user to Low-MTU GlobalProtect policy group (CenturyLink/PPPoE typically needs MTU ~1420)" | |
| } | |
| $issues += @{ | |
| Severity = $severity | |
| Issue = "MTU path is restricted to $($results.Tests['Max_MTU']) bytes" | |
| Fix = $fix | |
| } | |
| } | |
| if ($results.Tests["Upstream_PPPoE_Inferred"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Upstream PPPoE detected (likely CenturyLink behind router)" | |
| Fix = "ISP modem handles PPPoE (MTU 1492) - IPSec tunnel needs MTU ~1400 or lower" | |
| } | |
| } | |
| if ($results.Tests["Mobile_Carrier_Inferred"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Very low MTU detected (~$($results.Tests['Max_MTU']) bytes) - mobile carrier or heavy tunneling" | |
| Fix = "Use Low-MTU GP policy (1280 or lower). SSL tunnel fallback is expected and acceptable." | |
| } | |
| } | |
| if ($results.Tests["Tunnel_Type"] -eq "IPSec") { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Connection using IPSec tunnel (50-70 byte overhead)" | |
| Fix = "If MTU issues persist, force SSL tunnel via GP gateway config" | |
| } | |
| } | |
| $dnsFailures = $results.Tests.Keys | Where-Object { | |
| $_ -like "DNS_System_*" -or $_ -like "DNS_Internal_*" | |
| } | Where-Object { | |
| $results.Tests[$_] -is [hashtable] -and $results.Tests[$_].Error | |
| } | |
| if ($dnsFailures) { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "DNS resolution failing over VPN tunnel" | |
| Fix = "Likely MTU issue - DNS responses being fragmented/dropped" | |
| } | |
| } | |
| if ($results.Tests["IPSec_Fallback_Detected"]) { | |
| $alreadyLowMtu = $results.Tests["GP_Adapter_MTU"] -and $results.Tests["GP_Adapter_MTU"] -le 1300 | |
| $severity = if ($alreadyLowMtu) { "INFO" } else { "HIGH" } | |
| $issues += @{ | |
| Severity = $severity | |
| Issue = "IPSec tunnel failed, client fell back to SSL" | |
| Fix = if ($alreadyLowMtu) { "Expected behavior on low-MTU/mobile networks. SSL provides reliable connectivity." } | |
| else { "Check UDP 4501 path, MTU issues, or move to low-MTU GP policy" } | |
| } | |
| } | |
| if ($results.Tests["Fragmentation_In_Logs"] -and $results.Tests["Fragmentation_In_Logs"] -gt 5) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Multiple fragmentation events detected in GP logs ($($results.Tests['Fragmentation_In_Logs']) occurrences)" | |
| Fix = "Lower MTU in GP portal config to avoid fragmentation" | |
| } | |
| } | |
| if ($results.Tests["Gateway_TCP_443"] -and -not $results.Tests["UDP_4501_Send"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "TCP 443 works but UDP 4501 may be blocked" | |
| Fix = "IPSec requires UDP 4501 - if blocked, SSL fallback is expected" | |
| } | |
| } | |
| if ($results.Tests["Banner_MTU_Suspect"]) { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "Banner (apps28.du.edu:8445) DNS resolves but port unreachable" | |
| Fix = "Classic MTU issue - move user to low-MTU GP policy" | |
| } | |
| } | |
| if ($results.Tests["Network_Service_Issues"] -gt 0) { | |
| # Check if connectivity is actually working despite stopped services | |
| $connectivityWorking = $results.Tests["Default_Gateway_Reachable"] -or | |
| ($results.Tests.Keys | Where-Object { $_ -like "TCP443_*" -and $results.Tests[$_] -eq $true }) | |
| if ($connectivityWorking) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Network services stopped ($($results.Tests['Network_Service_Issues'])) but connectivity working" | |
| Fix = "Consider restarting NlaSvc/Netman services, but may not be causing current issues" | |
| } | |
| } else { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "Critical network services not running ($($results.Tests['Network_Service_Issues']) stopped)" | |
| Fix = "Restart stopped services or run -ResetNetworkStack" | |
| } | |
| } | |
| } | |
| if ($results.Tests["ThirdParty_LSPs"]) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Third-party Winsock LSPs detected: $($results.Tests['ThirdParty_LSPs'] -join ', ')" | |
| Fix = "AV/security software LSPs can interfere with VPN - try disabling temporarily or run netsh winsock reset" | |
| } | |
| } | |
| if ($results.Tests["Proxy_Enabled"]) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "System proxy enabled: $($results.Tests['Proxy_Server'])" | |
| Fix = "Proxy settings can interfere with VPN split tunneling - verify proxy config" | |
| } | |
| } | |
| if ($results.Tests["ThirdParty_Drivers"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Third-party network filter drivers active" | |
| Fix = "Other VPN/security software may conflict with GlobalProtect" | |
| } | |
| } | |
| # Check for high adapter errors | |
| $adapterErrors = $results.Tests.Keys | Where-Object { $_ -like "Adapter_*_Errors" -and $results.Tests[$_] -gt 100 } | |
| if ($adapterErrors) { | |
| $issues += @{ | |
| Severity = "MEDIUM" | |
| Issue = "Network adapter showing high error count" | |
| Fix = "Update network drivers or check physical connection/cabling" | |
| } | |
| } | |
| if ($results.Tests["Default_Gateway_Reachable"] -eq $false) { | |
| $issues += @{ | |
| Severity = "HIGH" | |
| Issue = "Cannot reach default gateway - basic connectivity broken" | |
| Fix = "Check WiFi/Ethernet connection, router, or run -ResetNetworkStack" | |
| } | |
| } | |
| if ($results.Tests["Mobile_Hotspot"]) { | |
| $issues += @{ | |
| Severity = "INFO" | |
| Issue = "Connected via mobile hotspot (detected gateway: $($results.Tests['Default_Gateway']))" | |
| Fix = "Mobile hotspots have severe MTU constraints (~1250-1300). Low-MTU GP policy required. SSL fallback expected." | |
| } | |
| } | |
| if ($issues.Count -eq 0) { | |
| Write-Host "`n[OK] No obvious issues detected" -ForegroundColor $colors.Success | |
| } else { | |
| # Check if things are actually working despite detected "issues" | |
| $bannerWorking = $results.Tests["Banner_TCP_$BannerPort"] | |
| $gpConnected = $results.Tests["GP_Tunnel_IP"] | |
| $lowMtuPolicy = $results.Tests["GP_Adapter_MTU"] -and $results.Tests["GP_Adapter_MTU"] -le 1300 | |
| if ($bannerWorking -and $gpConnected -and $lowMtuPolicy) { | |
| Write-Host "`n[OK] GlobalProtect connected and working with Low-MTU policy (MTU: $($results.Tests['GP_Adapter_MTU']))" -ForegroundColor $colors.Success | |
| Write-Host " Current network has constrained MTU but GP is configured correctly." -ForegroundColor Gray | |
| Write-Host "`nAdditional observations:" -ForegroundColor White | |
| } | |
| foreach ($issue in $issues) { | |
| $sevColor = switch ($issue.Severity) { | |
| "HIGH" { $colors.Failure } | |
| "MEDIUM" { $colors.Warning } | |
| default { $colors.Info } | |
| } | |
| Write-Host "`n[$($issue.Severity)] $($issue.Issue)" -ForegroundColor $sevColor | |
| Write-Host " Fix: $($issue.Fix)" -ForegroundColor Gray | |
| } | |
| } | |
| # Show available reset options | |
| Write-Host "`n" + "-"*40 -ForegroundColor DarkGray | |
| Write-Host "AVAILABLE OPTIONS:" -ForegroundColor Gray | |
| Write-Host " -CollectLogs Bundle GP logs to desktop for analysis" -ForegroundColor DarkGray | |
| Write-Host " -GPGateway <ip> Test UDP 4501 connectivity to specific gateway" -ForegroundColor DarkGray | |
| Write-Host " -BannerHost <host> Banner server to test (default: apps28.du.edu)" -ForegroundColor DarkGray | |
| Write-Host " -BannerPort <port> Banner port to test (default: 8445)" -ForegroundColor DarkGray | |
| Write-Host " -ResetDNSOnly Flush DNS cache (no restart)" -ForegroundColor DarkGray | |
| Write-Host " -ResetWinsock Reset Winsock catalog (restart required)" -ForegroundColor DarkGray | |
| Write-Host " -ResetNetworkStack Full stack reset: Winsock + TCP/IP (restart required)" -ForegroundColor DarkGray | |
| Write-Host " -ExportResults Export results to JSON on desktop" -ForegroundColor DarkGray | |
| # ============================================================ | |
| # Export Results | |
| # ============================================================ | |
| if ($ExportResults) { | |
| $exportPath = "$env:USERPROFILE\Desktop\GP-Troubleshoot-$(Get-Date -Format 'yyyyMMdd-HHmmss').json" | |
| $results | ConvertTo-Json -Depth 5 | Out-File $exportPath | |
| Write-Host "`nResults exported to: $exportPath" -ForegroundColor $colors.Info | |
| } | |
| Write-Host "`n" + "="*60 -ForegroundColor $colors.Info | |
| Write-Host " Troubleshooting complete - $(Get-Date -Format 'HH:mm:ss')" -ForegroundColor DarkGray | |
| Write-Host "="*60 + "`n" -ForegroundColor $colors.Info | |
| # Return results object for pipeline use | |
| return $results |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment