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

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

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

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

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


person Unknown    schedule 23.05.2009    source источник
comment
См. Здесь образец кода C / C ++: stackoverflow.com/questions/13073797/   -  person Alexis Wilke    schedule 12.10.2020


Ответы (6)


Следующее не дает прямого ответа на ваш вопрос, но может показаться вам интересным; в нем говорится, что 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 : Ping: Метод сырых сокетов.

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

Может быть, есть более простой API, чем этот, я не знаю; но я надеюсь, что я дал вам понять лежащий в основе протокол (ы).

person ChrisW    schedule 23.05.2009
comment
Спасибо, это было немного полезно. Мне просто интересно, все ли, кто использует UDP, просто идут с минимальным значением по умолчанию 576, что кажется ужасной идеей. - person Unknown; 23.05.2009
comment
Когда я реализовал протокол с использованием UDP, он использовал порядковые номера и повторные передачи для обнаружения потерянных пакетов. Установив «соединение» с использованием небольших пакетов, я попытался «договориться», используя более крупный пакет, посылая более крупный пакет из конца в конец и проверяя, получил ли я ответ (или был ли он потерян в пути). YMMV. Маленькие пакеты (например, 100-байтовые) часто используются в реальном мире для таких приложений, как VoIP; Я не знаю, какие характеристики производительности вам требуются, почему вы вообще используете UDP: если вы собираетесь работать с большими пакетами, возможно, вы пытаетесь оптимизировать пропускную способность, а не ... - person ChrisW; 23.05.2009
comment
... джиттер; но я не знаю, в любом случае использование максимально больших пакетов сильно влияет на пропускную способность. - person ChrisW; 23.05.2009
comment
@ChrisW, это так. TCP имеет накладные расходы в 160 байт на пакет (даже не включая квитирование и подтверждение). UDP имеет только 64 байта. - person Unknown; 23.05.2009
comment
В пакете размером 1500 байт накладные расходы составляют от 5% до 10%. ‹Shrug› - person ChrisW; 23.05.2009
comment
Как вы пришли к 160 и 64 здесь? AFAIK IP-заголовок = 20, UDP-заголовок = 8, TCP-заголовок = 20. - person Nikolai Fetissov; 24.05.2009
comment
Можно использовать флаг не фрагментировать с пакетами UDP, и если ICMP будет получать ошибку фрагментации, уменьшите размер. - person David Dolson; 25.08.2009
comment
@Nikolai: Он, должно быть, думал битами, а не байтами. Таким образом, разница в накладных расходах менее 1% для пакета размером 1500 байт. - person BlueRaja - Danny Pflughoeft; 23.06.2011
comment
@ChrisW: О порядковых номерах позаботились в вашем собственном приложении? Я знаю, что TCP имеет порядковые номера, подтверждения и дефрагментацию, но UDP? благодаря. - person wmac; 04.07.2014
comment
@wmac Да, я помещаю порядковые номера в полезную нагрузку UDP-пакетов. Поскольку я был приложением, использующим стек протоколов сокетов (Winsock), у меня не было доступа (не мог читать) заголовки пакетов IP / UDP / TCP (которые использовались / потреблялись / скрывались стеком протоколов). - person ChrisW; 05.07.2014

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

IPv4 and IPv6 define minimum reassembly buffer size, the minimum datagram size that we are guaranteed any implementation must support. For IPv4, this is 576 bytes. IPv6 raises this to 1,280 bytes.


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

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

person Nikolai Fetissov    schedule 24.05.2009
comment
Если вы не готовы повторно реализовать множество логики последовательности, синхронизации и повторной передачи ... Да, я уже знаю об этом и готов реализовать свою собственную. Кроме того, PMTU не является частью TCP. Я не вижу причин, по которым вы не можете использовать его для своего собственного протокола. - person Unknown; 24.05.2009
comment
Я просто говорю, что TCP уже реализует RFC 1191/1981 и многое другое, в том числе в ядре. Если у вас есть причина работать в UDP - хорошо - продолжайте, просто убедитесь, что причина действительна. - person Nikolai Fetissov; 24.05.2009
comment
А что, если я просто использую фиктивное tcp-соединение вначале? Можно ли извлечь из этого PMTU? - person Unknown; 31.05.2009
comment
Вы можете извлечь текущий MSS из TCP-сокета с опцией TCP_MAXSEG. Это имеет смысл сделать только после того, как соединение будет установлено - вместо этого возвращается MSS по умолчанию. Теоретически PMTU может измениться, поскольку IP-маршрутизация является динамической, поэтому YMMV. Просто для полноты - IPv6 имеет явную опцию сокета IPV6_PATHMTU, которую можно запросить. - person Nikolai Fetissov; 31.05.2009
comment
докажите, что TCP является вашим узким местом, только тогда рассмотрите, что UDP не кажется жизнеспособным способом выявления проблем. UDP имеет множество применений, где TCP не подходит - person scape; 26.10.2015
comment
Конечно, да! Предлагалось изучить оба протокола и подумать о применимости. Вы вырываете слова из контекста. - person Nikolai Fetissov; 27.10.2015

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

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

person Alexp    schedule 14.01.2014

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

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

person Jherico    schedule 23.05.2009
comment
Почему люди часто говорят, что MTU для Ethernet составляет 1500? - person Unknown; 23.05.2009
comment
Потому что это определено в RFC для Ethernet V2. Я не достаточно сообразителен, чтобы знать, происходит ли обычно обратная фрагментация пакетов Ethernet в маршрутизаторе Ethernet на IP-адрес, поэтому это может иметь или не иметь отношения к рассматриваемому вопросу. - person Jherico; 23.05.2009

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

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

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

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

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

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

person bockmabe    schedule 31.10.2018

Вот фрагмент Windows PowerShell, который я написал, чтобы проверить наличие проблем с MTU пути. (Общий метод нетрудно реализовать на других языках программирования.) Многие брандмауэры и маршрутизаторы настроены на отключение всех ICMP людьми, которые ничего не знают. Обнаружение MTU пути зависит от возможности получить сообщение ICMP о недоступности пункта назначения с установленным параметром «Требуется фрагментация» в ответ на отправку пакета с установленным параметром «Не фрагментировать». Устранение проблем с фрагментацией 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's 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()
        }
    }
}
person JamieSee    schedule 06.09.2019