108. Маршрутизация, VLAN, Wi-Fi и VPN
Сеть из раздела 105 кончается на шлюзе. Дальше пакет ведут маршрутизаторы: каждый смотрит в свою таблицу и отдаёт пакет следующему, уменьшая его время жизни. VLAN делит один провод на несколько сетей, Wi-Fi заменяет провод радио, а VPN прокладывает свою сеть поверх чужой. Код раздела выбирает маршрут, разбирает ответы маршрутизатора, ставит метку VLAN, считает каналы, затухание и цену туннеля. Опыт — маршрутизатор Linux между двумя сетями Docker: эхо-запросы с TTL 1 и 2, как у traceroute.
Кадры, ARP и ping — раздел 105, TCP и MSS — раздел 106.
Как это называется в документации
Оригинал Перевод routing table таблица маршрутов longest prefix match выбор самого длинного совпавшего префикса default route маршрут по умолчанию next hop следующий узел на пути time exceeded ICMP «время жизни истекло» destination unreachable ICMP «адресат недостижим» VLAN tag, 802.1Q метка виртуальной сети VLAN ID (VID), priority (PCP) номер виртуальной сети, приоритет trunk / access port порт с метками / без меток channel, channel width канал, ширина канала free-space path loss (FSPL) затухание в свободном пространстве RSSI, dBm уровень принятого сигнала, децибел к милливатту tunnel, encapsulation туннель, упаковка пакета в пакет MTU, MSS clamping наибольший пакет, урезание MSS под туннель
108.1 Таблица маршрутов
Маршрутизатор ищет в таблице запись, в чью сеть попадает адрес назначения; если подходят несколько — выигрывает самый длинный префикс:
int route_lookup(const route_t *t, unsigned n, uint32_t dst)
{
int best = -1;
for (unsigned i = 0; i < n; i++) {
uint32_t m = t[i].len ? 0xFFFFFFFFu << (32u - t[i].len) : 0u;
if ((dst & m) == t[i].net && (best < 0 || t[i].len > t[best].len))
best = (int)i;
}
return best;
}
| Сеть | Шлюз | Что это |
|---|---|---|
| 0.0.0.0/0 | 192.168.1.1 | всё остальное — маршрут по умолчанию |
| 192.168.1.0/24 | — | своя сеть, напрямую |
| 10.8.0.0/16 | 10.8.0.1 | туннель VPN |
| 10.8.5.0/24 | 192.168.1.50 | через соседний маршрутизатор |
| 10.8.5.7/32 | 192.168.1.60 | один узел — отдельно |
8.8.8.8 уходит по маршруту по умолчанию, 192.168.1.77 — напрямую, 10.8.1.2 — в туннель, 10.8.5.9 — через .50, а 10.8.5.7 — через .60: у /32 префикс длиннее всех. Без маршрута по умолчанию для 8.8.8.8 записи нет, и маршрутизатор отвечает «адресат недостижим».
108.2 Время жизни и traceroute
Каждый маршрутизатор уменьшает TTL на единицу. Дошло до нуля — пакет выбрасывают, а отправителю шлют ICMP «время жизни истекло» и вкладывают начало его пакета: заголовок IP и 8 байт за ним, где у эха стоит номер. По нему отправитель узнаёт, на какой запрос пришёл ответ:
} else if (r->type == ICMP_TIME_EXCEEDED || r->type == ICMP_UNREACHABLE) {
const uint8_t *in = m + 8; /* заголовок нашего пакета и 8 байт за ним */
if (n < ihl + 8 + 20)
return 0;
unsigned ihl2 = (in[0] & 15u) * 4u;
if (n < ihl + 8 + ihl2 + 8)
return 0;
r->orig_dst = g32(in + 16);
r->id = g16(in + ihl2 + 4);
r->seq = g16(in + ihl2 + 6);
}
Опыт: сети 10.108.1.0/24 и 10.108.2.0/24, между ними контейнер с включённой пересылкой, а у сервера за ним — маршрут обратно. Код раздела собирает эхо функциями раздела 105 и отдаёт кадр MAC-адресу маршрутизатора. Эхо с TTL 1 маршрутизатор дальше не пустил и ответил сам — тип 11, код 0, от 10.108.1.2, а внутри — начало нашего пакета к 10.108.2.20 с нашим номером эха. Эхо с TTL 2 прошло, и ответ сервера пришёл с TTL 63: он начал с 64 и миновал один маршрутизатор. traceroute делает то же: TTL 1, 2, 3 …, и каждый ответ «время истекло» называет очередной узел пути.
108.3 VLAN: одна сеть — несколько сетей
Метка 802.1Q — 4 байта между адресом отправителя и типом: 0x8100, затем 3 бита приоритета, бит DEI и 12 бит номера сети:
unsigned vlan_tag(uint8_t *f, unsigned n, unsigned pcp, unsigned vid)
{
memmove(f + 16, f + 12, n - 12);
w16(f + 12, VLAN_TPID);
w16(f + 14, (pcp & 7u) << 13 | (vid & 0xFFFu)); /* приоритет 3 бита, DEI, номер сети 12 бит */
return n + 4;
}
Приоритет 5, сеть 10 — это 81 00 a0 0a, а за меткой остаётся настоящий тип, 0x0806. Номеров 4096, но 0 и 4095 заняты — сетей 4094. Кадр с меткой — до 1522 байт: старое оборудование, ждущее не больше 1518, такой кадр режет. Порт коммутатора «с метками» (trunk) несёт много сетей по одному проводу, порт «без меток» (access) — одну, и метку на нём снимают.
108.4 Wi-Fi: каналы и затухание
unsigned wifi_mhz(unsigned ch)
{
if (ch == 14)
return 2484; /* особый канал, только в Японии */
if (ch >= 1 && ch <= 13)
return 2407 + 5 * ch;
return 5000 + 5 * ch; /* 5 ГГц: канал 36 — 5180 МГц */
}
| Канал | МГц |
|---|---|
| 1 | 2412 |
| 6 | 2437 |
| 11 | 2462 |
| 13 | 2472 |
| 14 | 2484 |
| 36 | 5180 |
Каналы отстоят на 5 МГц, а занимают 20 МГц: соседние перекрываются. Не перекрываются 1, 6 и 11 — между ними 25 МГц. Где разрешены каналы до 13-го, для 802.11g/n берут и четыре — 1, 5, 9, 13: они отстоят ровно на 20 МГц.
Затухание в свободном пространстве растёт с расстоянием и частотой:
float fspl_db(float d_m, float f_mhz)
{
return 20.0f * log10f(d_m / 1000.0f) + 20.0f * log10f(f_mhz) + 32.44f;
}
На канале 6 на 1 м — 40,2 дБ, на 10 м — 60,2 дБ, на 100 м — 80,2 дБ: вдвое дальше — плюс 6,02 дБ, вдесятеро — плюс 20. На 5180 МГц те же 10 м — 66,7 дБ, на 6,55 дБ больше: 5 ГГц быстрее, но короче. Передатчик на 20 дБм в 100 м в чистом поле даст −60,2 дБм, а каждая стена отнимет ещё.
108.5 VPN: сеть поверх сети
Туннель упаковывает пакет в пакет: внутренний шифруют и отправляют внутри UDP. WireGuard добавляет 16 байт своего заголовка и 16 байт метки подлинности, а данные дополняет до кратного 16:
unsigned wg_outer(unsigned n, int ipv6, unsigned mtu)
{
unsigned padded = (n + 15u) & ~15u;
if (padded > mtu)
padded = mtu;
return (ipv6 ? 40u : 20u) + 8u + WG_HDR + padded + WG_TAG;
}
Пакет датчика в 60 байт выходит из туннеля в 124 байта — больше чем вдвое; ping из 84 байт — 156. Внутренний пакет в 1420 байт с IPv6 снаружи даёт ровно 1500, поэтому MTU туннеля — 1420; с IPv4 снаружи выходит 1480. TCP внутри туннеля должен договориться о MSS 1380, иначе полноразмерные сегменты не пролезут.
108.6 На приборе
У MAC F407 есть регистр сравнения метки VLAN — MACVLANTR, смещение 0x1C.
Совпавшую метку он только отмечает битом VLAN в состоянии дескриптора приёма
и разрешает кадр длиной до 1522 байт; кадры чужих сетей всё равно ложатся
в буферы, и отбрасывать их приходится программе по этому признаку.
Аппаратный фильтр по метке есть у MAC новых семейств — у H7 это бит VTFE
в ETH_MACPFR. Wi-Fi к STM32
обычно подключают готовым модулем на UART или SPI, и затухание из 108.4
считают для него. VPN на приборе — редкость; чаще туннель держит
маршрутизатор, а прибор видит обычную сеть и живёт с MTU 1420, если пакеты
идут через туннель.
_Static_assert(offsetof(ETH_TypeDef, MACVLANTR) == 0x1C, "ETH_MACVLANTR — смещение 0x1C");
108.7 Грабли
Ответ из другой сети не приходит, хотя запрос маршрутизатор пропустил. У получателя нет обратного маршрута, и ответ уходит его шлюзу по умолчанию. В опыте серверу пришлось добавить маршрут в 10.108.1.0/24 через .2.
Через VPN ходят мелкие пакеты, а большие пропадают. MTU туннеля 1420: 1500-байтный пакет в него не влезает. MTU 1420, MSS 1380.
Wi-Fi соседа на канале 3 мешает сильнее, чем на канале 1. Соседние каналы перекрываются: брать 1, 6 или 11.
Кадр с меткой VLAN не проходит. Порт без меток, или оборудование режет кадры длиннее 1518 байт.
traceroute показывает звёздочки. Маршрутизатор не обязан отвечать «время истекло» и ограничивает частоту таких ответов.
108.8 Итоги
- Маршрут — самый длинный совпавший префикс; маршрут по умолчанию — /0.
- Каждый маршрутизатор уменьшает TTL; на нуле он отвечает «время жизни истекло» — на этом стоит traceroute. В опыте: TTL 1 — ответ маршрутизатора, TTL 2 — ответ сервера с TTL 63.
- VLAN — 4 байта метки: 4094 сети на одном проводе.
- Wi-Fi: каналы через 5 МГц шириной 20, без перекрытия — 1, 6, 11; затухание +6,02 дБ на каждое удвоение расстояния.
- Туннель стоит байтов: 60 байт → 124 у WireGuard, MTU 1420.
Дальше: раздел 109 «Git для инженера: ветки, история, версии прошивки» — как не терять прошивки и помнить, из какого кода собрана каждая.