Как найти самый большой пакет UDP, который я могу отправить без фрагментации?

Мне нужно знать, какой самый большой UDP-пакет, который я могу отправить на другой компьютер, не имеет фрагментации.

Этот размер обычно известен как MTU (Maximum Transmission Unit). Предположительно, между двумя компьютерами будет много маршрутизаторов и модемов, которые могут иметь разные MTU.

Я прочитал, что реализация TCP в Windows автоматически находит максимальный MTU в пути.

Я также экспериментировал, и я узнал, что максимальный MTU с моего компьютера на сервер составлял 57712 байтов + заголовок. Все, что выше этого, было отброшено. Мой компьютер находится в локальной сети, а не MTU должен быть около 1500 байт?

Ответ 1

Следующее не отвечает на ваш вопрос напрямую, но вам может показаться интересным; в нем говорится, что IP-пакеты могут быть разобраны/собраны и, следовательно, больше, чем предел на нижнем носителе (например, 1500-байтовый Ethernet): Разрешить фрагментацию IP, MTU, MSS, и вопросы PMTUD с GRE и IPSEC


Подробнее об этой теме:

  • Re: UDP-фрагментация говорит, что вы должны использовать ICMP вместо UDP для обнаружения MTU
  • Path MTU Discovery говорит, что TCP-соединение может включать неявное согласование MTU через ICMP

Я не знаю о создании ICMP через API в Windows: когда-то был предложен такой API, и он был спорным, потому что люди утверждали, что упростит работу с программным обеспечением, которое реализует функции отказа в обслуживании, поток сообщений ICMP.

Нет, похоже, что он реализован: см., например, Winsock Programmer FAQ Примеры: Ping: Raw Sockets Method.

Итак, чтобы обнаружить MTU, создайте ping-пакеты с флагом "не фрагментировать".

Может быть, есть более простой API, чем я, я не знаю; но я надеюсь, что я дал вам понять базовый протокол [s].

Ответ 2

В дополнение ко всем предыдущим ответам, цитируя classic:

IPv4 и IPv6 определяют минимальный размер буфера повторной сборки , минимальный размер дейтаграммы, который нам гарантирован, должен поддерживать. Для IPv4 это 576 байт. IPv6 повышает это до 1280 байт.


Это в значительной степени означает, что вы хотите ограничить размер вашей дейтаграммы до 576, если вы работаете в общедоступном Интернете, и вы контролируете только одну сторону обмена - что делает большинство стандартных протоколов UDP.

Также обратите внимание, что PMTU является динамическим свойством пути. Это одна из вещей, с которой работает TCP. Если вы не готовы повторить много логики последовательности, синхронизации и повторной передачи, используйте TCP для любой критической сети. Тест, тест, профиль, т.е. доказать, что TCP является вашим узким местом, только тогда рассмотрим UDP.

Ответ 3

Это интересная тема для меня. Возможно, некоторые практические результаты могут представлять интерес при доставке коротких данных UDP в реальном мире через Интернет через UDP и со скоростью передачи 1 пакет в секунду, данные продолжают появляться с минимальной потерей пакетов до примерно 2K. По этому и вы начинаете сталкиваться с проблемами, но регулярно мы поставляем пакеты с 1600 байтами без бедствия - это больше, чем мобильные сети GPRS, а также WAN по всему миру. При ~ 1K, если сигнал стабилен (его нет!), Вы получаете небольшую потерю пакетов.

Интересно, что это не нечетный пакет, а часто шквал пакетов в течение нескольких секунд, что, по-видимому, объясняется тем, что VoIP-вызовы иногда рушится.

Ответ 4

Ваш собственный MTU доступен в registry, но MTU на практике подходит к самому маленькому MTU на пути между вашим машины и места назначения. Его обе переменные и могут быть определены только эмпирически. Существует несколько RFC, показывающих, как их определить.

Локальная сеть может иметь очень большие значения MTU, поскольку сетевое оборудование обычно однородно или, по меньшей мере, централизованно администрируется.

Ответ 5

Для приложений UDP вы должны обрабатывать сквозной MTU самостоятельно, если хотите избежать фрагментации IP-адресов или потери пакетов. Рекомендуемый подход для любого приложения - стараться использовать PMTU для выбора максимальной датаграммы или отправлять дейтаграммы & lt; минимальный PMTU

https://tools.ietf.org/html/rfc5405#section-3.2

Рекомендации по использованию одноадресного UDP для разработчиков приложений "НЕ СЛЕДУЕТ отправлять дейтаграммы, превышающие PMTU, ДОЛЖНЫ обнаруживать PMTU или отправлять дейтаграммы & lt; минимум PMTU

Появится окно настроек и доступа к информации PMTU через интерфейс основных опций сокета:

Вы можете убедиться, что обнаружение PMTU включено через IP_MTU_DISCOVER, и вы можете прочитать MTU через IP_MTU.

https://docs.microsoft.com/en-us/windows/desktop/winsock/ipproto-ip-socket-options

Ответ 6

Здесь немного Windows PowerShell, который я написал, чтобы проверить наличие проблем с MTU. (Общая техника не слишком сложна для реализации на других языках программирования.) Многие брандмауэры и маршрутизаторы настроены так, чтобы отбрасывать все ICMP людьми, которые не знают ничего лучше. Определение MTU пути зависит от возможности получения сообщения ICMP Destination Unreachable с параметром Fragementation Needed в ответ на отправку пакета с параметром Don't Fragment. Устранение проблем фрагментации IPv4, MTU, MSS и PMTUD с помощью GRE и IPsec действительно хорошо объясняет, как работает обнаружение.

function Test-IPAddressOrName($ipAddressOrName)
{
    $ipaddress = $null
    $isValidIPAddressOrName = [ipaddress]::TryParse($ipAddressOrName, [ref] $ipaddress)

    if ($isValidIPAddressOrName -eq $false)
    {
        $hasResolveDnsCommand = $null -ne (Get-Command Resolve-DnsName -ErrorAction SilentlyContinue)
        if ($hasResolveDnsCommand -eq $true)
        {
            $dnsResult = Resolve-DnsName -DnsOnly -Name $ipAddressOrName -ErrorAction SilentlyContinue
            $isValidIPAddressOrName = $null -ne $dnsResult
        }
    }

    return $isValidIPAddressOrName
}

function Get-NameAndIPAddress($ipAddressOrName)
{
    $hasResolveDnsCommand = $null -ne (Get-Command Resolve-DnsName -ErrorAction SilentlyContinue)

    $ipAddress = $null
    $validIPAddress = [ipaddress]::TryParse($ipAddressOrName, [ref] $ipAddress)
    $nameAndIp = [PSCustomObject] @{ 'Name' = $null; 'IPAddress' = $null }

    if ($validIPAddress -eq $false)
    {
        if ($hasResolveDnsCommand -eq $true)
        {
            $dnsResult = Resolve-DnsName -DnsOnly $ipAddressOrName -Type A -ErrorAction SilentlyContinue

            if ($null -ne $dnsResult -and $dnsResult.QueryType -eq 'A')
            {
                $nameAndIp.Name = $dnsResult.Name
                $nameAndIp.IPAddress = $dnsResult.IPAddress
            }
            else
            {
                Write-Error "The name $($ipAddressOrName) could not be resolved."
                $nameAndIp = $null
            }
        }
        else
        {
            Write-Warning "Resolve-DnsName not present. DNS resolution check skipped."
        }
    }
    else
    {
        $nameAndIp.IPAddress = $ipAddress

        if ($hasResolveDnsCommand -eq $true)
        {
            $dnsResult = Resolve-DnsName -DnsOnly $ipAddress -Type PTR -ErrorAction SilentlyContinue

            if ($null -ne $dnsResult -and $dnsResult.QueryType -eq 'PTR')
            {
                $nameAndIp.Name = $dnsResult.NameHost
            }
        }
    }

    return $nameAndIp
}

<#
    .Synopsis
    Performs a series of pings (ICMP echo requests) with Don't Fragment specified to discover the path MTU (Maximum Transmission Unit).

    .Description
    Performs a series of pings with Don't Fragment specified to discover the path MTU (Maximum Transmission Unit). An ICMP echo request 
    is sent with a random payload with a payload length specified by the PayloadBytesMinimun. ICMP echo requests of increasing size are 
    sent until a ping response status other than Success is received. If the response status is PackeTooBig, the last successful packet 
    length is returned as a reliable MTU; otherwise, if the respone status is TimedOut, the same size packet is retried up to the number 
    of retries specified. If all of the retries have been exhausted with a response status of TimedOut, the last successful packet 
    length is returned as the assumed MTU.

    .Parameter UseDefaultGateway
    If UseDefaultGateway is specified the default gateway reported by the network interface is used as the destination host.

    .Parameter DestinationHost
    The IP Address or valid fully qualified DNS name of the destination host.

    .Parameter InitialTimeout
    The number of milliseconds to wait for an ICMP echo reply. Internally, this is doubled each time a retry occurs.

    .Parameter Retries
    The number of times to try the ping in the event that no reply is recieved before the timeout.

    .Parameter PayloadBytesMinimum
    The minimum number of bytes in the payload to use. The minimum MTU for IPv4 is 68 bytes; however, in practice, it extremely rare 
    to see an MTU size less than 576 bytes so the default value is 548 bytes (576 bytes total packet size minus an ICMP header of 28 
    bytes).

    .Parameter PayloadBytesMaximum
    The maximum number of bytes in the payload to use. An IPv4 MTU for jumbo frames is 9000 bytes. The default value is 8973 bytes (9001 
    bytes total packet size, which is 1 byte larger than the maximum IPv4 MTU for a jumbo frame, minus an ICMP header of 28 bytes).

    .Example
    Discover-PathMTU -UseDefaultGateway

    .Example
    Discover-PathMTU -DestinationHost '192.168.1.1'

    .Example
    Discover-PathMTU -DestinationHost 'www.google.com'
#>
function Discover-PathMtu
{
    [CmdletBinding(SupportsShouldProcess = $false)]
    param
    (
        [Parameter(Mandatory = $true, ParameterSetName = 'DefaultGateway')]
        [switch] $UseDefaultGateway,

        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = 'IPAddressOrName')]
        [ValidateScript({ Test-IPAddressOrName $_ })]
        [string] $DestinationHost,

        [Parameter(ParameterSetName = 'IPAddressOrName')]
        [Parameter(ParameterSetName = 'DefaultGateway')]
        [int] $InitialTimeout = 3000,

        [Parameter(ParameterSetName = 'IPAddressOrName')]
        [Parameter(ParameterSetName = 'DefaultGateway')]
        [int] $Retries = 3,

        [Parameter(ParameterSetName = 'IPAddressOrName')]
        [Parameter(ParameterSetName = 'DefaultGateway')]
        $PayloadBytesMinimum = 548,

        [Parameter(ParameterSetName = 'IPAddressOrName')]
        [Parameter(ParameterSetName = 'DefaultGateway')]
        $PayloadBytesMaximum = 8973
    )

    begin
    {
        $ipConfiguration = Get-NetIPConfiguration -Detailed | ?{ $_.NetProfile.Ipv4Connectivity -eq 'Internet' -and $_.NetAdapter.Status -eq 'Up' } | Sort { $_.IPv4DefaultGateway.InterfaceMetric } | Select -First 1
        $gatewayIPAddress = $ipConfiguration.IPv4DefaultGateway.NextHop

        $pingOptions = New-Object System.Net.NetworkInformation.PingOptions
        $pingOptions.DontFragment = $true
        $pinger = New-Object System.Net.NetworkInformation.Ping

        $rng = New-Object System.Security.Cryptography.RNGCryptoServiceProvider
    }

    process
    {
        $pingIpAddress = $null

        if ($UseDefaultGateway -eq $true)
        {
            $DestinationHost = $gatewayIPAddress
        }

        $nameAndIP = Get-NameAndIPAddress $DestinationHost

        if ($null -ne $nameAndIP)
        {
            Write-Host "Performing Path MTU discovery for $($nameAndIP.Name) $($nameAndIP.IPAddress)..."

            $pingReply = $null
            $payloadLength = $PayloadBytesMinimum
            $workingPingTimeout = $InitialTimeout

            do
            {
                $payloadLength++

                # Use a random payload to prevent compression in the path from potentially causing a false MTU report.
                [byte[]] $payloadBuffer = (,0x00 * $payloadLength)
                $rng.GetBytes($payloadBuffer)

                $pingCount = 1

                do
                {
                    $pingReply = $pinger.Send($nameAndIP.IPAddress, $workingPingTimeout, $payloadBuffer, $pingOptions)

                    if ($pingReply.Status -notin 'Success', 'PacketTooBig', 'TimedOut')
                    {
                        Write-Warning "An unexpected ping reply status, $($pingReply.Status), was received in $($pingReply.RoundtripTime) milliseconds on attempt $($pingCount)."
                    }
                    elseif ($pingReply.Status -eq 'TimedOut')
                    {
                        Write-Warning "The ping request timed out while testing a packet of size $($payloadLength + 28) using a timeout value of $($workingPingTimeout) milliseconds on attempt $($pingCount)."
                        $workingPingTimeout = $workingPingTimeout * 2
                    }
                    else
                    {
                        Write-Verbose "Testing packet of size $($payloadLength + 28). The reply was $($pingReply.Status) and was received in $($pingReply.RoundtripTime) milliseconds on attempt $($pingCount)."
                        $workingPingTimeout = $InitialTimeout
                    }

                    Sleep -Milliseconds 10

                    $pingCount++
                } while ($pingReply.Status -eq 'TimedOut' -and $pingCount -le $Retries)
            } while ($payloadLength -lt $PayloadBytesMaximum -and $pingReply -ne $null -and $pingReply.Status -eq 'Success')

            if ($pingReply.Status -eq 'PacketTooBig')
            {
                Write-Host "Reported IPv4 MTU is $($ipConfiguration.NetIPv4Interface.NlMtu). The discovered IPv4 MTU is $($payloadLength + 27)."
            }
            elseif ($pingReply.Status -eq 'TimedOut')
            {
                Write-Host "Reported IPv4 MTU is $($ipConfiguration.NetIPv4Interface.NlMtu). The discovered IPv4 MTU is $($payloadLength + 27), but may not be reliable because the packet appears to have been discarded."    
            }
            else
            {
                Write-Host "Reported IPv4 MTU is $($ipConfiguration.NetIPv4Interface.NlMtu). The discovered IPv4 MTU is $($payloadLength + 27), but may not be reliable, due to an unexpected ping reply status."    
            }

            return $payloadLength + 27
        }
        else
        {
            Write-Error "The name $($DestinationHost) could not be resolved. No Path MTU discovery will be performed."
        }
    }

    end
    {
        if ($null -ne $pinger)
        {
            $pinger.Dispose()
        }

        if ($null -ne $rng)
        {
            $rng.Dispose()
        }
    }
}