Learning Platform
Глоссарий Troubleshooting
Урок 04.03 · 18 мин
Начальный
EncapsulationHeadersProtocol stackTCPIPEthernet

Encapsulation — путь пакета сверху вниз и обратно

В прошлом уроке мы упомянули, что HTTP-запрос «спускается» через слои TCP, IP, Ethernet и улетает в кабель. На каждом слое к данным добавляется заголовок. Это процесс называется encapsulation (инкапсуляция). На приёмной стороне — обратный процесс decapsulation: заголовки считываются и снимаются, пока не останутся исходные данные.

Encapsulation — одна из самых элегантных идей в дизайне сетей. Она позволяет каждому слою работать с теми данными, которые относятся к нему, и не вникать в смысл данных верхних слоёв. Это и есть конкретное воплощение принципа «уровни абстракции». В этом уроке разберём encapsulation детально, с реальными цифрами и hex-данными.


Encapsulation — основная идея

Представьте письмо в почтовых конвертах. Вы написали текст. Положили в один конверт с пометкой «личное». Этот конверт положили в больший конверт с адресом получателя. Большой конверт положили в почтовый мешок с городом. Мешок — в самолёт.

Каждая обёртка добавляет свою адресную информацию для своего этапа доставки. Самолёт знает только город. Городская почта только адрес дома. Жилец только пометку «личное». Никто не должен видеть текст письма, кроме отправителя и получателя.

Encapsulation -- слои-обёртки
Данные приложенияHTTP-запрос: GET /index.html HTTP/1.1\nHost: example.com. Просто текст для нас, но это application payload
+ TCP header20 байт TCP-заголовка перед данными. Содержит src port, dst port, sequence number, ACK, flags, window. Теперь это TCP-segment
+ IP header20 байт IPv4-заголовка перед TCP-сегментом. Содержит src IP, dst IP, TTL, protocol=6 (TCP). Теперь это IP-packet
+ Ethernet header14 байт Ethernet-заголовка + 4 байта CRC. Содержит src MAC, dst MAC, EtherType=0x0800 (IPv4). Теперь это Ethernet-frame, готов для отправки

Каждый слой видит payload верхнего слоя как «непрозрачные байты» — ему не нужно понимать структуру. TCP не знает, что внутри HTTP. IP не знает, что внутри TCP. Ethernet не знает, что внутри IP. Это и есть принцип абстракции.

С точки зрения размеров:

  • HTTP-запрос «GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n» — около 40 байт.
    • TCP header (20 байт) = TCP segment 60 байт.
    • IP header (20 байт) = IP packet 80 байт.
    • Ethernet header (14 байт) + CRC (4 байта) = Ethernet frame 98 байт.

Эти 98 байт улетают в кабель.


Заголовки в деталях

Посмотрим, что конкретно лежит в каждом заголовке.

Ethernet frame header (14 байт + 4 байта CRC)

Ethernet frame header
dst MAC (6 байт)MAC-адрес получателя на этом link. Если это пакет, идущий за пределы LAN -- dst MAC = MAC default gateway. Если внутри LAN -- MAC конечного хоста
src MAC (6 байт)MAC-адрес отправителя на этом link. Всегда MAC вашего интерфейса -- если NAT, маршрутизатор сам подменит
EtherType (2 байта)Тип того, что внутри. 0x0800 = IPv4, 0x86DD = IPv6, 0x0806 = ARP, 0x8100 = VLAN tag. Подсказывает receiving stack'у, как парсить дальше
Payload (46-1500 байт)Данные верхнего слоя -- обычно IP-пакет. Min 46 байт (если меньше -- padding), max 1500 байт (standard MTU)
CRC (4 байта)Frame check sequence. Контрольная сумма всего frame'а. Receiving NIC проверяет -- если не совпало, frame отбрасывается

IPv4 packet header (20 байт без options)

IPv4 packet header
Version + IHL4 бита версии (для IPv4 = 4) + 4 бита Internet Header Length (длина заголовка в 32-битных словах, обычно 5 = 20 байт)
Type of ServiceDSCP (6 бит) + ECN (2 бита). DSCP для QoS-маркировки. ECN для notification congestion без потери пакетов
Total LengthДлина всего IP-пакета (заголовок + payload), макс 65535 байт
IdentificationУникальный ID пакета, используется при фрагментации для сборки на receiving stack
Flags + Fragment Offset3 бита флагов (DF -- Don't Fragment, MF -- More Fragments) + 13 бит offset фрагмента в исходном пакете
TTLTime to Live. Декрементируется каждым router'ом. При 0 -- пакет отбрасывается, шлётся ICMP Time Exceeded. Изначально 64 (Linux) или 128 (Windows)
ProtocolЧто внутри payload IP. 6 = TCP, 17 = UDP, 1 = ICMP, 47 = GRE, 50 = ESP (IPsec). Подсказывает receiving IP-stack'у, кому отдать
Header ChecksumКонтрольная сумма IP-заголовка (не payload!). Каждый router пересчитывает, потому что TTL меняется
Src IP (4 байта)IP-адрес отправителя. NAT-router может подменить, чтобы скрыть внутренний IP
Dst IP (4 байта)IP-адрес получателя. По нему router'ы решают, куда передавать

TCP segment header (20 байт без options)

TCP segment header
Src Port (2 байта)Порт отправителя. У клиента обычно random ephemeral port (32000-65535). У сервера -- well-known (80, 443, 25)
Dst Port (2 байта)Порт получателя. К чему конкретно подключаемся: 443 -- HTTPS, 22 -- SSH, 5432 -- PostgreSQL
Sequence Number (4 байта)Sequence number -- порядковый номер первого байта данных в этом segment'е. Используется для упорядоченной доставки и retransmission
ACK Number (4 байта)Следующий sequence number, который получатель ожидает. Подтверждает receipt всех предыдущих байтов
Data Offset + Flags4 бита Data Offset (длина TCP header'а), 9 бит флагов: NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN. SYN/ACK/FIN/RST -- ключевые для handshake и состояния
Window Size (2 байта)Receive window. Сколько байт receiver готов принять без подтверждения. Используется для flow control
ChecksumКонтрольная сумма TCP-сегмента (включая pseudo-IP-header). Receiver проверяет целостность
Urgent PointerУказатель на urgent data. Используется редко -- большинство TCP-stack игнорирует

Итого: 14 (Ethernet) + 20 (IP) + 20 (TCP) = 54 байта overhead на каждый TCP-segment. Если ваш HTTP-запрос — 100 байт, на проводе летит 154 байт. Если файл 1 МБ — ~1024 пакетов по ~1460 байт payload + 54 байт header’а = ~55 КБ overhead. Это ~5% издержек на заголовки, что приемлемо.


Decapsulation на приёмной стороне

Когда пакет приходит на receiving host, происходит обратный процесс. Каждый слой считывает свой заголовок, делает свою работу, передаёт payload вверх.

Decapsulation -- слой за слоем
Bits arriveNIC принимает электрические сигналы (или световые / радио), декодирует в биты, собирает frame
Ethernet driverПроверяет CRC -- если плох, отбрасывает. Сверяет dst MAC: моё или broadcast/multicast? Если моё -- передаёт payload (IP-пакет) дальше
IP stackПроверяет IP-заголовок. Сверяет dst IP -- моё? Декрементирует TTL (если forwarding). Проверяет protocol -- TCP/UDP/ICMP. Передаёт payload дальше
TCP stackПроверяет TCP-заголовок. Дёргает sequence number -- куда поставить в receive buffer. Отвечает ACK если нужно. Передаёт payload приложению
App readsПриложение делает read() на socket. Читает байты HTTP-запроса. Парсит -- понимает, что это GET /index.html. Обрабатывает

Скорость прохождения через стэк — микросекунды на каждый слой. Большая часть времени тратится в очередях OS (interrupt handling, scheduling) и на копирование данных между kernel space и user space. Для performance-критичных приложений есть kernel bypass (DPDK, AF_XDP), но это уже глубокий уровень.


Encapsulation в действии — реальный hex

Соберём всё вместе. Вот реальный hex-dump TCP SYN-пакета, который я только что захватил.

0000  ff ff ff ff ff ff aa bb cc dd ee 00 08 00              <- Ethernet header
0014  45 00 00 28 12 34 40 00 40 06 a1 b2 c0 a8 01 02   c0   <- IPv4 header
0024  a8 01 01 d6 d8 01 bb f0 12 34 56 00 00 00 00 50 02     <- TCP header
0034  00 ff 1c b3 00 00                                        <- TCP header (end)

Разбор побайтно:

Разбор реального пакета
ff ff ff ff ff ffDst MAC = broadcast (все f). Это типичный пример. В реальной упаковке здесь был бы MAC default gateway
aa bb cc dd ee 00Src MAC -- например, ваш интерфейс с MAC aa:bb:cc:dd:ee:00
08 00EtherType 0x0800 = IPv4 inside
45 00Version 4, IHL 5 (20 байт заголовок). ToS = 0x00 (default)
00 28Total Length = 40 байт. 20 IP header + 20 TCP header = 40, payload = 0 (это SYN)
12 34 40 00Identification = 0x1234, Flags = 0x4 (DF set), Fragment Offset = 0
40 06TTL = 64, Protocol = 6 (TCP)
a1 b2Checksum -- считается при создании пакета
c0 a8 01 02Src IP = 192.168.1.2 (ваш ноут в локальной сети)
c0 a8 01 01Dst IP = 192.168.1.1 (типичный default gateway)
d6 d8Src Port = 0xd6d8 = 54,872 (ephemeral)
01 bbDst Port = 0x01bb = 443 (HTTPS)
f0 12 34 56Sequence Number -- random initial value (ISN)
00 00 00 00ACK Number = 0 -- мы пока ничего не подтверждаем (это SYN, первый пакет)
50 02Data Offset = 5 (20 байт), Flags = 0x02 = SYN bit set

Видите — весь TCP SYN-пакет умещается в 54 байта. Из них 14 Ethernet, 20 IPv4, 20 TCP. Полезной нагрузки в этом конкретном пакете нет — SYN это просто запрос «давай установим соединение».


Encapsulation на routers и switches

Когда пакет идёт через сеть, разные узлы по-разному его обрабатывают.

Что видит каждый node на пути
Switch (L2)Видит Ethernet frame. Смотрит dst MAC, ищет в MAC table -- порт назначения. Forward без изменения. НЕ смотрит IP или выше
Router (L3)Сверяет dst IP в routing table, выбирает next hop. Декрементирует TTL, пересчитывает IP checksum. Меняет dst MAC на MAC next hop'а, src MAC на свой. НЕ смотрит TCP и выше
NAT routerКроме L3 routing, ещё подменяет src IP (private -> public) и иногда src port. Запоминает mapping для обратного направления. Смотрит L4 (порты), но не выше
Stateful firewallЗапоминает состояние TCP-соединений (SYN -> ESTABLISHED -> FIN). Решает по правилам -- блокирует / пропускает. Смотрит L4 (флаги TCP), не выше
L7 LB / WAFПарсит HTTP. Смотрит URL, headers, body. Принимает решения о маршрутизации, блокировке, кэшировании. Видит ВСЕ слои
Destination hostКонечная точка. Декапсулирует все слои, передаёт data приложению. Здесь HTTP-запрос реально обрабатывается

Это иллюстрирует, почему слои так важны: каждое промежуточное устройство смотрит только нужный ему уровень. Switch’у не нужно понимать HTTP. Router’у не нужно понимать TCP. Это позволяет switches быть простыми и быстрыми (миллиарды пакетов в секунду).


Encapsulation в туннелях — inception

В реальных сетях бывают сценарии, когда внутри одного слоя «вложен» другой слой того же уровня. Это туннели.

Примеры:

  1. IPsec. Зашифрованный IP-пакет лежит внутри другого IP-пакета. На внешнем уровне — адреса VPN-серверов. На внутреннем — настоящие адреса пользователей. ESP-заголовок между ними.

  2. WireGuard. UDP-пакет лежит внутри другого UDP-пакета. На внешнем — VPN-сервер. На внутреннем — зашифрованный пользовательский трафик.

  3. GRE. Generic Routing Encapsulation. Любой L3-пакет (IP, ICMP) внутри IP. Используется для туннелей между сайтами.

  4. VXLAN. Ethernet-frame внутри UDP-пакета. Используется в overlay-сетях дата-центров (Kubernetes, OpenStack).

  5. HTTP/3 (QUIC). Сложнее — TLS внутри QUIC внутри UDP внутри IP. Структура: IP > UDP > QUIC (с встроенным TLS) > HTTP/3 headers > application data.

В Wireshark такие случаи видны как «слой внутри слоя». Например, IPsec-туннель в Wireshark показывает два IP-заголовка подряд.


Попробуй сам

Посмотрите encapsulation на реальном трафике.

# 1. Захватите 10 пакетов на ваш интерфейс
sudo tcpdump -i en0 -c 10 -nn -e -X
# -e показывает Ethernet-заголовки
# -nn не резолвит имена
# -X показывает payload hex

# Вы увидите что-то вроде:
# 18:32:15.123456 aa:bb:cc:dd:ee:ff > ff:ff:ff:...
#   192.168.1.5.54321 > 142.250.185.78.443: TCP ...
#   0x0000:  4500 0028 ...    <- IP header в hex
#   0x0010:  ...               <- payload

# 2. Захватите в файл и откройте в Wireshark
sudo tcpdump -i en0 -c 20 -w /tmp/encap.pcap
# Откройте /tmp/encap.pcap в Wireshark
# Кликните на любой пакет -- увидите дерево слоёв в нижней панели

# 3. Только Ethernet-заголовок
sudo tcpdump -i en0 -c 5 -nn -e | head
# Каждая строка начинается с MAC-адресов

# 4. Размер пакета
sudo tcpdump -i en0 -c 5 -nn -e -l | awk '{print $NF}'
# Покажет размеры пакетов

# 5. Подсчитать заголовки HTTP-запроса
echo -e 'GET /index.html HTTP/1.1\r\nHost: example.com\r\n\r\n' | wc -c
# Около 40 байт application data
# + 20 TCP + 20 IP + 14 Ethernet = 94 байта на проводе

Откройте Wireshark на любой пакет и развернувайте дерево слоёв. Это и есть encapsulation глазами. Каждый слой — отдельная ветка с полями.


Системные вызовы и kernel buffers: что происходит после decapsulation
Проверка знанийKnowledge check
Junior захватывает в Wireshark TCP SYN-пакет. В дереве он видит: Ethernet II, IPv4, TCP. Затем он видит размер пакета -- 74 байта на проводе. Объясните, что внутри этих 74 байт, почему именно столько, и где здесь данные приложения.
ОтветAnswer
Разбор 74 байт: (1) Ethernet header -- 14 байт. Состоит из: dst MAC (6 байт), src MAC (6 байт), EtherType (2 байта = 0x0800 для IPv4). На отрезке frame'а ещё есть 4 байта CRC, но Wireshark обычно их не показывает (NIC уже проверил и отбросил). (2) IPv4 header -- 20 байт (без options). Содержит version, length, ToS, total length, ID, flags, TTL, protocol, checksum, src IP, dst IP. (3) TCP header -- 20 байт без options или 40 байт с TCP options. SYN-пакеты ОБЫЧНО имеют TCP options (MSS, Window Scale, SACK Permitted, Timestamp), поэтому 40 байт. С options: 14 (Ethernet) + 20 (IP) + 40 (TCP с options) = 74 байта. Без options было бы 54 байта. (4) Application data -- 0 байт. SYN -- это сигнальный пакет, начинающий TCP-handshake. Никаких данных приложения в нём нет. Только заголовки и TCP-options. Реальный HTTP-запрос (или любые данные) пошлются в следующих пакетах ПОСЛЕ завершения handshake'а (SYN -> SYN-ACK -> ACK). Junior должен заметить: 74 байта = только overhead заголовков (с TCP options). Это feature, не bug -- TCP-handshake нужен для установления состояния, прежде чем данные потекут. На performance это даёт overhead 3 round-trips для коротких HTTP-запросов (отсюда популярность HTTP keep-alive, multiplexing в HTTP/2, 0-RTT в QUIC). Если бы Junior увидел SYN без TCP options (54 байта) -- это бы означало, что TCP реализация очень минимальная (что редко). Бонус: можно проверить размер пакета на проводе через tcpdump -e -nn -- покажет 'length 74'.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 6. Что такое encapsulation в сетевом контексте и какой главный принцип она реализует?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4