Компьютерные сети: от IPC к C/Winsock
IP-маршрут: как пакет покидает локальную сеть
Переносим получателя «Привет» в другую сеть: принимаем local/remote decision, выбираем самый точный маршрут, меняем Ethernet-кадр на каждом hop и выводим MTU/MSS из реального пути.
IP-маршрут: как пакет покидает локальную сеть
Задача: процесс B больше не сосед
Процесс A всё ещё отправляет двенадцать UTF-8-байтов Привет, но компьютеры
теперь находятся в разных сетях:
Computer A: 192.168.10.42/24
Gateway A: 192.168.10.1
Computer B: 203.0.113.20
Приложение A знает адрес B и порт процесса B. Оно не знает MAC удалённого сервера, список маршрутизаторов и типы каналов по дороге. Первый вопрос задаёт IP-стек: destination доступен напрямую или нужен next hop?
Если по ошибке искать MAC 203.0.113.20 локальным ARP broadcast, ответа не
будет: ARP не пересекает маршрутизатор. Для удалённого destination компьютеру
A нужен MAC локального gateway, а IP destination при этом остаётся
203.0.113.20.
Шаг 1. Префикс определяет область адресов
Запись 192.168.10.42/24 означает, что первые 24 бита IPv4-адреса относятся
к prefix. Побитовое AND адреса с маской 255.255.255.0 даёт сеть:
192.168.10.42 AND 255.255.255.0 = 192.168.10.0
Адрес 192.168.10.80 совпадает с connected prefix 192.168.10.0/24 и может
быть on-link. 203.0.113.20 не совпадает, поэтому нужен другой маршрут.
Префикс не измеряет метры и не гарантирует один switch. Это правило адресации и маршрутизации. Виртуальные машины на одном физическом сервере могут принадлежать разным IP-сетям; растянутая L2-сеть может соединять географически разнесённые узлы.
Откуда у хоста вообще есть адрес: DHCP
Мы записали 192.168.10.42/24 как данность. Но откуда Windows вообще узнала
свой address, prefix, default gateway и DNS servers? Два пути:
- static: администратор вводит настройки вручную; типично для серверов;
- DHCP lease: при подключении хост broadcast-запросом спрашивает настройки, а DHCP server выдаёт lease — IPv4 address, prefix, gateway, DNS servers и срок действия. До истечения срока lease продлевают; потеряв lease, хост обязан прекратить пользоваться адресом и запросить настройки заново.
Это наблюдаемо, а не скрыто в недрах ОС:
Get-NetIPConfiguration -Detailed
В выводе видны Dhcp Enabled, адрес DHCP server, Lease Obtained и
Lease Expires. Если DHCP недоступен, Windows может назначить себе link-local
адрес 169.254.x.x (APIPA). Такая строка в диагностике читается однозначно:
адрес есть, но его хост выдумал сам — реальной настройки от сети не было.
Важно не смешать два вопроса. DHCP отвечает, как хост получил настройки; local/remote decision и longest prefix match отвечают, куда пойдёт пакет. Второй вопрос решается одинаково для static- и DHCP-адреса, поэтому дальше по курсу источник адреса не меняет ни одного вывода.
Шаг 2. Побеждает самый длинный совпавший prefix
ОС рассматривает все маршруты, которые покрывают destination, и сначала выбирает самый конкретный — с наибольшей длиной prefix:
| DestinationPrefix | NextHop | Интерфейс | Metric |
|---|---|---|---|
203.0.113.20/32 | 10.8.0.1 | VPN | 200 |
203.0.113.0/24 | 192.168.10.1 | Ethernet | 50 |
0.0.0.0/0 | 192.168.10.1 | Ethernet | 5 |
Для 203.0.113.20 выигрывает /32, хотя его metric больше. Metric помогает
выбирать между маршрутами сравнимой специфичности, но default route с низким
metric не становится точнее host route.
Выбранная строка определяет как минимум:
- исходящий интерфейс;
- next hop или признак on-link;
- косвенно — подходящий source IP интерфейса.
Если приложение заранее привязало сокет к конкретному local address,
возможности выбора сужаются. bind() не создаёт отсутствующий маршрут.
Шаг 3. Первый кадр адресован gateway
Для remote destination стек запрашивает через ARP MAC 192.168.10.1, а не
MAC B. Получается сочетание адресов разных масштабов:
Ethernet destination: MAC gateway 192.168.10.1
IPv4 destination: 203.0.113.20
TCP destination port: порт процесса B
Application payload: 12 bytes, UTF-8 "Привет"
Switch довозит frame до gateway. Маршрутизатор снимает канальную оболочку, уменьшает TTL IPv4 или Hop Limit IPv6, ищет новый next hop и создаёт новый кадр для следующего link. Поэтому MAC-адреса меняются hop-by-hop, а destination IP обычно остаётся адресом B.
Исключение, которое нужно замечать, — NAT. Устройство трансляции может переписать source или destination IP/port и хранит mapping. NAT не является обязательной частью IP-маршрутизации, не шифрует данные и не подтверждает их доставку.
Шаг 4. TTL останавливает петлю, а не измеряет время
Если из-за ошибочных таблиц два router пересылают пакет друг другу, без ограничителя он мог бы ходить бесконечно. Каждый IPv4 router уменьшает TTL на единицу; в IPv6 поле называется Hop Limit. Когда значение исчерпано, пакет отбрасывается, и устройство обычно сообщает ICMP Time Exceeded.
Windows tracert использует эту механику: отправляет probes с постепенно
растущим TTL и наблюдает ответы промежуточных узлов. Звёздочка не доказывает,
что router выключен. Он может исправно пересылать обычный трафик, но не
отвечать на probe или возвращать ICMP по другому фильтруемому пути.
Шаг 5. Только теперь появляется MTU пути
Каждый link ограничивает размер сетевого пакета, который помещается в его
канальный payload. Для распространённого Ethernet MTU равен 1500: в поле
payload кадра помещается IP-пакет до 1500 байтов. Это не максимальный
размер сообщения приложения.
Для TCP поверх IPv4 без IP/TCP options бюджет одного сегмента выглядит так:
Ethernet MTU 1500
- minimal IPv4 header 20
- minimal TCP header 20
= TCP data (baseline MSS) 1460 bytes
Для IPv6 с базовым 40-байтовым заголовком тот же простой расчёт даёт 1440
байтов TCP data. TCP options, IPv6 extension headers и туннельные оболочки
уменьшают доступный бюджет. Поэтому 1460 — частый результат, а не
универсальная константа.
Двенадцать байтов Привет уверенно помещаются в такой бюджет: размер не
требует разделения payload. Но это не обещание «один send() — один пакет».
TCP может объединять записи, делить поток иначе, а сетевой адаптер — выполнять
segmentation offload. MSS ограничивает TCP data в сегменте на пути, а не
создаёт границы сообщений приложения.
Path MTU определяется самым узким link на выбранном пути. В IPv4 packet может быть фрагментирован, если это разрешено, но современные TCP-пути обычно стараются подобрать размер сегментов через Path MTU Discovery. В IPv6 маршрутизаторы не фрагментируют транзитный packet: источник получает сигнал Packet Too Big и адаптирует размер. Если такие ICMP-сообщения блокируются, маленькие пакеты могут проходить, а большие — зависать: это характерный PMTUD black hole.
C/Winsock checkpoint: узнать выбранный source endpoint
После успешного connect() приложение может спросить у Winsock, какой
локальный адрес и ephemeral port фактически выбрал стек:
struct sockaddr_storage local = {0};
int local_length = sizeof(local);
if (getsockname(
s,
(struct sockaddr *)&local,
&local_length
) == SOCKET_ERROR) {
printf("getsockname failed: %d\n", WSAGetLastError());
return 1;
}
char host[NI_MAXHOST];
char service[NI_MAXSERV];
if (getnameinfo(
(struct sockaddr *)&local, local_length,
host, sizeof(host),
service, sizeof(service),
NI_NUMERICHOST | NI_NUMERICSERV
) != 0) {
printf("getnameinfo failed\n");
return 1;
}
printf("local endpoint: %s:%s\n", host, service);
В connect() были remote IP и port. getsockname() показывает результат
локального выбора, но не раскрывает весь route и gateway. Их нужно наблюдать
инструментами IP Helper/PowerShell. Это ещё один пример границы API: socket
handle даёт процессу нужный контракт, а детали forwarding остаются у ОС.
Предсказание → эксперимент → доказательство
Используйте адрес удалённого стенда преподавателя; 203.0.113.20 ниже —
документационный placeholder.
Предсказание. До команд запишите: какой prefix должен победить, какой интерфейс и source IP выберет Windows, чей MAC появится в первом frame, и изменится ли destination IP после первого router.
Эксперимент. Спросите не свою интуицию, а таблицы ОС:
$Target = '203.0.113.20' # замените адресом удалённого lab-сервера
Get-NetIPConfiguration
Get-NetRoute -AddressFamily IPv4 |
Sort-Object DestinationPrefix |
Format-Table DestinationPrefix, NextHop, InterfaceAlias, RouteMetric
Find-NetRoute -RemoteIPAddress $Target
Get-NetNeighbor -AddressFamily IPv4 |
Format-Table IPAddress, LinkLayerAddress, State, InterfaceAlias
tracert -d $Target
После TCP-подключения сравните local endpoint из C с IPAddress и
InterfaceAlias результата Find-NetRoute.
Доказательство. Заполните route card:
destination:
winning prefix:
outgoing interface:
selected source IP:
next hop:
next-hop MAC in neighbor cache:
first Ethernet destination:
IP destination after first hop:
smallest known MTU / unknown:
Если известен только MTU локального интерфейса, не называйте его Path MTU. Для такого вывода нужны сведения о всём пути или результат корректной PMTUD.
Частые заблуждения
«Удалённый сервер должен быть destination MAC». Router разделяет broadcast domains. Первый frame нужен локальному gateway; удалённый MAC не виден и не требуется.
«Маршрут с меньшим metric всегда выигрывает». Сначала сравнивается длина
совпавшего prefix. Более конкретный /32 сильнее /0.
«MTU 1500 запрещает отправлять сообщения больше 1500 байтов». Socket API может принять большой TCP-буфер; транспорт распределит поток по сегментам. MTU ограничивает IP packet на link, а framing сообщения остаётся обязанностью приложения.
Проверьте модель
Короткий итог
- IP-стек выбирает route по самому длинному совпавшему prefix.
- Для remote destination первый Ethernet frame адресован локальному next hop.
- Router заменяет канальную оболочку и уменьшает TTL/Hop Limit на каждом hop.
- Destination IP обычно сохраняется, но NAT может его переписать.
- MTU ограничивает IP packet на link; MSS выводится после учёта IP/TCP headers.
- Размер сетевого сегмента не является границей сообщения приложения.
Мост к следующей главе
Теперь мы умеем провести пакет до удалённого B и уже знаем, через какой socket
процесс отдаёт байты ОС. Следующая проблема находится выше IP: TCP переносит
не отдельные сообщения, а поток байтов. В следующей главе проверим, почему
один send() не обязан соответствовать одному recv(), и добавим собственный
framing прикладного протокола.