Фрагментация IP-дейтаграмм
В главе 5 мы увидим, что у различных протоколов канального уровня может быть разный, максимальный размер переносимых пакетов. Некоторые протоколы могут переносить «большие» пакеты, тогда как другие допускают перенос только «маленьких» пакетов. Например, Ethernet-пакеты могут содержать не более 1500 байт данных, тогда как многие протоколы глобальных линий способны переносить пакеты размером не более 576 байт. Максимальное количество данных, которое может переносить пакет канального уровня, называют максимальной единицей передачи (Maximum Transfer Unit, MTU). Поскольку каждая IP-дейтаграмма для передачи от одного маршрутизатора к другому инкапсулируется в пакет канального уровня, максимальный размер поля данных протокола канального уровня накладывает жесткое ограничение на длину IP-дейтаграммы. Само по себе жесткое ограничение на размер IP-дейтаграммы не представляет проблемы. Проблема заключается в том, что в каждой линии связи на пути от отправителя до получателя могут использоваться разные протоколы канального уровня, и у каждого из этих протоколов может быть свой, отличный от других, максимальный размер поля данных.
Чтобы лучше разобраться в этой проблеме, представьте себе маршрутизатор, соединяющий несколько линий, в каждой из которых применяется свой, отличный от других протокол канального уровня со своим максимальным размером поля данных. Предположим, маршрутизатор получает IP-дейтаграмму по одной линии и заглядывает в свою таблицу продвижения данных, чтобы определить исходящую линию для этой дейтаграммы. Предположим также, что максимальный размер поля данных в исходящей линии меньше длины IP-дейтаграммы. Впору запаниковать, поскольку нужно сжимать слишком большой IP-пакет так, чтобы он поместился в поле полезной нагрузки пакета канального уровня. Решение этой проблемы состоит в разбиении содержащихся в IP-дейтаграмме данных на несколько IP-дейтаграмм меньшего размера. Каждую из этих IP-дейтаграмм меньшего размера называют фрагментом. Прежде чем фрагменты достигнут транспортного уровня адресата, из них необходимо снова собрать исходную дейтаграмму. Действительно, протоколы TCP и UDP ожидают получить от сетевого уровня полный, не фрагментированный пакет. Разработчики протокола IPv4 понимали, что повторная сборка (и, возможно, повторная фрагментация) дейтаграмм на маршрутизаторах значительно усложнит протокол и снизит производительность маршрутизаторов. (Если бы вы были маршрутизатором, захотели бы вы сверх всех ваших обязанностей заниматься еще и повторной сборкой фрагментированных дейтаграмм?) Придерживаясь принципа сохранения простоты сетевого уровня, разработчики IPv4 решили оставить задачу повторной сборки фрагментированных дейтаграмм оконечным системам.
Когда хост-адресат получает серию дейтаграмм, он должен определить, являются ли данные дейтаграммы фрагментами некой исходной дейтаграммы большего размера. Если он выясняет, что некие дейтаграммы представляют собой фрагменты, ему нужно также идентифицировать последний фрагмент дейтаграммы, чтобы можно было собрать эти фрагменты вместе в оригинальную дейтаграмму и выяснить, как это делается. Чтобы хост-получатель мог осуществлять повторную сборку дейтаграмм, разработчики IPv4 поместили в дейтаграмму поля идентификации, флага и фрагментации. Когда дейтаграмма создается, хост-отправитель маркирует ее номером-идентификатором, а также помещает в нее адреса отправителя и получателя. Хост-отправитель увеличивает на единицу идентификационный номер для каждой следующей посылаемой дейтаграммы. Когда маршрутизатору необходимо фрагментировать дейтаграмму, каждый получающийся фрагмент помечается адресом отправителя, адресом получателя и идентификационным номером оригинальной дейтаграммы. Когда хост-адресат получает серию дейтаграмм от одного и того же передающего хоста, он изучает идентификационные номера дейтаграмм, чтобы определить, являются ли данные дейтаграммы фрагментами дейтаграммы большего размера. Поскольку протокол IP предоставляет ненадежную службу, один или несколько фрагментов могут не достичь адресата. Чтобы хост-адресат мог быть абсолютно уверен в том, что получил последний фрагмент оригинальной дейтаграммы, бит флага в последнем фрагменте устанавливается в 0, тогда как во всех остальных фрагментах он устанавливается в 1. Кроме того, чтобы хост-адресат мог определить, не был ли потерян какой-либо из фрагментов (а также иметь возможность собрать фрагменты оригинальной дейтаграммы в правильном порядке), в каждом фрагменте имеется поле смещения.
На рис. 4.24 изображен пример. Дейтаграмма из 4000 байт (20 байт IP-заголовка и 3980 байт полезной нагрузки) прибывает на маршрутизатор и должна быть переправлена далее по линии с максимальным размером поля данных в 1500 байт.
Это означает, что 3980 байт оригинальной дейтаграммы должны быть распределены между тремя отдельными фрагментами (каждый из которых также представляет собой IP-дейтаграмму). Предположим, что оригинальная дейтаграмма маркирована идентификационным номером 777. Характеристики трех фрагментов показаны в табл. 4.3.
Полезная нагрузка дейтаграммы передается транспортному уровню получателя только после того, как IP-уровень полностью восстановит оригинальную дейтаграмму. Если один или несколько фрагментов не сумеют достичь адресата, вся дейтаграмма отбрасывается и не передается транспортному уровню. Но, как было показано в предыдущей главе, если в качестве транспортного уровня используется протокол TCP, тогда восстановлением после потери фрагмента занимается протокол TCP, повторяя передачу всех данных оригинальной дейтаграммы.
Фрагментация и повторная сборка накладывают дополнительное бремя на Интернет-маршрутизаторы (фрагментация) и на хосты-адресаты (повторная сборка). Поэтому желательно свести фрагментацию к минимуму. Для этого часто ограничиваются размеры TCP- и UDP-сегментов, что снижает вероятность фрагментации. Поскольку все протоколы передачи данных, поддерживаемые протоколом IP, должны обеспечивать транспортировку пакетов данных размером, по меньшей мере, 576 байт, фрагментации можно полностью избежать, если использовать максимальный размер сегмента (MSS), равный 536 байт, что вместе с двумя 20-разрядными IP- и TCP-заголовками составит 576 байт. По этой причине размер большинства TCP-сегментов для передачи данных больших объемов (например, HTTP-данных) находится в пределах от 512 до 536 байт (возможно, путешествуя в web, вы замечали, что данные поступают блоками примерно по 500 байт).