Skip to content

Instantly share code, notes, and snippets.

@WinSe7en
Last active January 14, 2026 18:19
Show Gist options
  • Select an option

  • Save WinSe7en/3f80309200ae227603e484367c6ea67c to your computer and use it in GitHub Desktop.

Select an option

Save WinSe7en/3f80309200ae227603e484367c6ea67c to your computer and use it in GitHub Desktop.
<#
.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
<#
.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
<#
.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