Public/Test-LissWindowsPathMtu.ps1

function Test-LissWindowsPathMtu {
    <#
    .SYNOPSIS
    Estimates an IPv4 path MTU without changing interface settings.
    .DESCRIPTION
    Confirms ordinary IPv4 ICMP response, then uses ping.exe with Don't Fragment and a bounded binary search. Every attempted payload is returned. The estimated MTU adds 28 bytes for IPv4 and ICMP headers to the largest successful payload.
    .PARAMETER Target
    Approved IPv4 destination hostname or address.
    .PARAMETER MinimumPayloadBytes
    Smallest ICMP payload considered by the binary search.
    .PARAMETER MaximumPayloadBytes
    Largest ICMP payload considered by the binary search.
    .PARAMETER TimeoutMilliseconds
    Timeout for each ping.exe attempt.
    .EXAMPLE
    Test-LissWindowsPathMtu -Target 1.1.1.1 -MinimumPayloadBytes 1200 -MaximumPayloadBytes 1472
    #>

    [CmdletBinding()]
    [OutputType('LISSTech.WindowsNetworkDiagnostics.PathMtuResult')]
    param(
        [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Target,
        [ValidateRange(0, 65507)][int]$MinimumPayloadBytes = 1200,
        [ValidateRange(0, 65507)][int]$MaximumPayloadBytes = 1472,
        [ValidateRange(100, 10000)][int]$TimeoutMilliseconds = 1000
    )

    if ($MinimumPayloadBytes -gt $MaximumPayloadBytes) {
        throw 'MinimumPayloadBytes cannot exceed MaximumPayloadBytes.'
    }
    $started = Get-Date
    $ordinary = Invoke-LissPingExecutable -Target $Target -PayloadBytes 32 -TimeoutMilliseconds $TimeoutMilliseconds
    if (-not $ordinary.Success) {
        return [pscustomobject]@{
            PSTypeName              = 'LISSTech.WindowsNetworkDiagnostics.PathMtuResult'
            Target                  = $Target
            StartedAt               = $started
            CompletedAt             = Get-Date
            Status                  = 'IcmpUnavailable'
            OrdinaryIcmpSuccessful  = $false
            MinimumPayloadBytes     = $MinimumPayloadBytes
            MaximumPayloadBytes     = $MaximumPayloadBytes
            LargestSuccessfulPayload = $null
            EstimatedPathMtu        = $null
            Attempts                = @()
            Error                   = 'The target did not answer an ordinary IPv4 ICMP echo request.'
        }
    }

    $attempts = New-Object 'Collections.Generic.List[object]'
    $low = $MinimumPayloadBytes
    $high = $MaximumPayloadBytes
    $largestSuccess = $null
    while ($low -le $high) {
        $payload = [int][Math]::Floor(($low + $high) / 2)
        $probe = Invoke-LissPingExecutable -Target $Target -PayloadBytes $payload -TimeoutMilliseconds $TimeoutMilliseconds -DontFragment
        $attempts.Add([pscustomobject]@{
                PayloadBytes = $payload
                Success      = $probe.Success
                ExitCode     = $probe.ExitCode
                Output       = $probe.Output
            })
        if ($probe.Success) {
            $largestSuccess = $payload
            $low = $payload + 1
        }
        else {
            $high = $payload - 1
        }
    }
    $status = if ($null -eq $largestSuccess) { 'NoSuccessfulProbe' } else { 'Estimated' }
    [pscustomobject]@{
        PSTypeName              = 'LISSTech.WindowsNetworkDiagnostics.PathMtuResult'
        Target                  = $Target
        StartedAt               = $started
        CompletedAt             = Get-Date
        Status                  = $status
        OrdinaryIcmpSuccessful  = $true
        MinimumPayloadBytes     = $MinimumPayloadBytes
        MaximumPayloadBytes     = $MaximumPayloadBytes
        LargestSuccessfulPayload = $largestSuccess
        EstimatedPathMtu        = if ($null -ne $largestSuccess) { $largestSuccess + 28 } else { $null }
        Attempts                = $attempts.ToArray()
        Error                   = if ($status -eq 'NoSuccessfulProbe') { "Ordinary ICMP worked, but no Don't Fragment probe in the requested range succeeded." } else { $null }
    }
}