Транспортный уровень: трансляция адресов и формирование трафика
Ещё про VirtualBox
VBoxManage:
modifyvm
clonevm
startvm
controlvm … acpipowerbutton
unregistervm … --delete
Всё это умеют сценарии из лекций
- Соответствующая команда выводится на экран
Проброс портов из хост-системы в VirtualBox.
- Что такое и зачем нужно: если в профиль входит интерфейс типа «NAT», подключение к порту 2201 на хост-машине транслируется в подключение к порту 22 на этом интерфейсе гостевой машины
VBoxManage modifyvm srv --natpf1 guestssh,tcp,,2201,,22 (см. в документации)
- в GUI (пример)
NAT
Network address translation — что такое и зачем нужно:
- Переменные / множественные / локальные адреса внутри → «белые IP» снаружи
- Экономия IPv4 адресов
- (косвенно) безопасность
NAT без транспортного уровня
Идея замены конкретного IP другим конкретным IP (и обратно) — например, если «внутренний» IP из интранет-диапазона.
- Работает только 1:1
(
actually, не работает)
Проблемы:
Любые контрольные суммы некорректны, если в них входит IP (надо пересчитывать)
- Нет информации для подмены «много → один»
- Давно выбросили ☺
NAT с идентификацией потока (NAPT)
Динамическая подмена IP на основании «состояния»
- Таблица: пара IP + идентификатор «состояния»
Кстати, учебник по сетевым sysctl (
довольно старый)
- Может работать N:1, M:N
Destination NAT (например, для отдельного веб-сервера внутри локальной сети)
- Идентификаторы — всё, что можно извлечь из пакетов
- TCP: 2 порта + 2 IP (+ initial SeqN?)
- DNS (UDP): DNS Query ID (поле прикладного уровня!) + 2 порта + 2 IP
- NTP (UDP): Originator Timestamp (поле прикладного уровня!) + 2 порта + 2 IP
- ping: ICMP-serial (поле сетевого уровня) + IP
- …
- Проблема коллизий SEQN / портов / идентификаторов в вариантах N/1 и M/N:
- Два хоста (A и B идут на один и тот же порт хоста C через NAT):
У них могут совпасть порты отправителя:
- A:1234 → C:80; B:1234 → C:80
=> коллизия
- Оок, введём в идентификатор ещё seqn:
- A:1234, seqn 4321 → C:80; B:1234, seqn 39014 → C:80
=> вероятность коллизии?
Примеры SNAT и DNAT
SNAT — при отсылке «наружу» (например, из intranet-диапазона)
- Кстати, MASQUERADE — это на случай динамического IP, при формировании conntrack-а всякий IP раз запрашивается у интерфейса
DNAT — при приёме (например, межсетевой экран на одной машине, а веб-сервер — внутри сети, на другой)
- Т. н. «проброс портов» — подключение к машине по по определённому порту «пробрасывается» на другую машину (можно поменять номер порта тоже):
# iptables -t nat -A PREROUTING -i интерфейс -p tcp --dport порт -j DNAT --to-destination внутренний_адрес[:внутренний_порт]
- Т. н. «проброс портов» — подключение к машине по по определённому порту «пробрасывается» на другую машину (можно поменять номер порта тоже):
- Работа с iptables (только введение, изучать будем NFTables)
/proc/net/nf_conntrack и утилита conntrack
Пример: client ← deepnet → firewall ← localnet → srv:
+ везде autonet
на srv default route через firewall
на firewall DNAT: интерфейс eth2 (от client), порт 2202, на адрес_srv:22
на client — ssh адрес_srv -p 2202
на firewall — conntrack -L
Обход NAT с помощью STUN (на примере UDP)
Проблема: клиент - nat1[NAT] - … - [NAT]nat2 - сервер. Как переслать пакет серверу от клиента, если на участке «- … -» неизвестен IP ни того, ни другого?
«Проброс портов»: подключение на nat2:порт2 транслируется межсетевым экраном в подключение к сервер:порт
А если нет административного доступа к NAT-серверам? Очевидно, нужен ещё один сервер, к которому оба клиента могут обратиться (как минимум, для того, чтобы узнать, что до них кто-то хочет достучаться):
клиент - nat1[NAT] - … - координатор - … - [NAT]nat2 - сервер
Полная ретрансляция через координатор.
ПО (например, socat), которое слушает на координатор:порт2 и координатор:порт3 и перекладывает трафик между ними
socat TCP-listen:2223,reuseaddr TCP-listen:2222,reuseaddr
ПО (например, socat) на сервере, которое постоянно подключено к server - координатор:порт2 и перекладывает трафик на server:порт
socat TCP:10.12.0.27:2223 TCP:localhost:22
Подключение к координатор:порт3 перенаправится на server:порт
(Делается с помощью ssh, например)
STUN (Session Traversal Utilities for NAT).
Включают в себя несколько протоколов (в том числе полную ретрансляцию, Traversal_Using_Relay_NAT); остальные протоколы работают не всегда
- Базируется на двух (необоснованных) предположениях:
Если с клиента послать UDP-пакет какому-то серверу, в conntrack в течение некоторого времени находится запись, позволяющая получить UDP-«ответ» на этот пакет со стороны сервера, с порта получателя на порт отправителя
Так работают прикладные протоколы (например, DNS), но датаграмма не подразумевает ответ
Предположим, в UDP-пакете, предназначенном координатору, указан порт отправителя A, а при выходе из NAT на сервере1 порт отправителя превратился в B, о чём была сделана запись в conntrack. Тогда если послать ещё один UDP-пакет, на этот раз серверу2, указав тот же самый порт отправителя A, то на сервере1 порт отправителя тоже не изменится и будет B.
(если успеем) Простой пример для linux
Площадка: client - nat2 - stun - nat1 - srv
EPort по умолчанию совпадает с IPort, если нет конфликтов (и известен заранее)
«Стартовый» пакет (5) и (6) создаёт conntrack-запись не должен дойти до абонента (иначе отправителю вернётся либо «ответ», либо icmp-reject, и правило удалится), для этого выcтавляем ttl=2
Никакого обмена с координатором (stun) не происходит; он нужен только для того, чтобы пакеты с ttl=2 не доходили до nat1
Настройка:
- Виртуалки:
vbsnap 1 localnet:srv,nat1 anet:nat1,stun deepnet:client,nat2 bnet:nat2,stun vbintnets vms
Везде autonet
nat1:
# ip route add default via 10.1.0.28 — stun anet # iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE
nat2:
# ip route add default via 10.2.0.28 — stun bnet # iptables -t nat -A POSTROUTING -o eth2 -j MASQUERADE
srv:
# ip route add default via 10.12.0.27 — nat1 localnet
client:
# ip route add default via 10.4.0.30 — nat2 deepnet
Проверка:
cient
# ping 10.1.0.27 — пакет доходит до nat1, потому что в нём адрес поменян на nat2
Собственно STUN:
srv:
# echo QQ | socat - UDP:10.2.0.30:1234,sourceport=4321,ttl=2; socat -d -d -v PIPE UDP-RECVFROM:4321,fork
client: Внезапно™ начинает работать PIPE — на клиент приходят echo-ответы
# socat - UDP:10.1.0.27:4321,sourceport=1234
nat1
# conntrack -L если сделать conntrack -F — связь пропадёт!
TCP: а что делать с seqn? А ничего, не работает TCP ☹
Syn flood атака и SYNPROXY
- Суть: на каждый SYN в ядре выделяется приличная структурка. Можно заDOS-ить большим потоком безответственных SYN-ов.
Решение: разделить МЭ и сервер, и до сервера доносить только «хорошие» SYN-ы, точнее — принимать SYN-SYNACK-ACK-и всей пачкой, а затем ретранслировать (частичное проксирование)
- Требует более сложной логики
- Требует подмены SEQN (потому что мы всё ещё не знаем, какую SEQN выберет сервер, но уже «проиграли» для него 3WH)
Введение в обработку трафика
Эта тема на целый семестр! Поэтому про главную задачу — traffic shaping — только упомянем.
Задача: если сетевое устройство пересылает несколько потоков данных, надо обеспечить:
- «Справедливое» разделение трафика между абонентами (в т. ч. сообразно приоритетам)
- Работоспособность при перегрузках
Базовая статья: Traffic Control HOWTO
Как всегда Арчевики
Общая идея: маршрутизируемые пакеты нельзя просто перекладывать по одному
∃ серьёзная вероятность, что прямо сейчас принятый пакет отсылать нельзя (занято etc.)
- ⇒ буферизация (очередь)
- Приоритизация трафика
- ⇒ Выделение потоков (TCP)
- Например, SSH с быстрыми командами vs HTTP с большими объёмами
- Кстати: SSH / SFTP и ToS 0x48 / 0x00
Различные дисциплины (по длине очереди, по времени ожидания и т. п.)
Деревья очередей и фильтры для ветвления
Проблема: Bufferbloat
Дисциплина по умолчанию: tc-fq_codel:
CoDel (анти-буферблоат: 5ms в очереди + выбрасывание пакетов из «плохих» очередей) + Fair_queuing (равномерное распределение пропускной способности по потокам данных)
Умеет Explicit_Congestion_Notification вместо Random_early_detection
Пример: дисциплина Network Emulator
Статья (не всё работает так, как написано)
- Удобно использовать на тест-стендах в курсовах по сетям!
Потестируем её с помощью «flood ping» (безудержной отправки ping-ов с максимальной скоростью)
Исходное состояние (используем stun):
# tc -col qdisc show # tc -s qdisc show # ping -f -c1000 10.1.0.27
Дисциплина по умолчанию CoDel
Медленная сеть:
# tc qdisc add dev eth1 root netem delay 400ms # tc qdisc show # ping -f -c100 10.1.0.27
- Должна выстроиться стопка из оправленных но ещё не отвеченных пакетов
Плохая сеть, в которой пакеты пропадают:
# tc qdisc del root dev eth1 # tc qdisc add dev eth1 root netem loss 10% # tc qdisc show # ping -f -c500 10.1.0.27
- Не забудем сначала удалить старую дисциплину
- К. О. рапортует: пакеты пропали
Очень плохая сеть, и медленная, и пакеты портятся:
# tc qdisc del root dev eth1 # tc qdisc add dev eth1 root netem delay 400ms corrupt 5% # tc qdisc show # ping -f -c500 10.1.0.27
Немного про shape:
Шейпить имеет смысл только исходящий трафик, потому что входящий уже получен ☺
- (на самом деле не только, но use case всё равно непростой)
Простейшая дисциплина — Token Bucket Filter
- Очередь пакетов → выдача «жетонов» из «ведра» → отсылка пакета с «жетоном»
По объёму: tbf limit объём_очереди burst ёмкость_ведра rate пропускная_способность
По времени ожидания tbf latency сколько_ждать burst ёмкость_ведра rate пропускная_способность
- Классовая очередь (дерево) — на каждый поток своя труба + общий слив
Простая приоритизация по ToS
Hierarchy Token Bucket с произвольной топологией (учебник)
Будем исследовать пропускную способность с помощью IPerf
На втором абоненте запустим iperf3 -4s (серверная часть, только IPv4)
На исследуемой машине будем настраивать дисциплину и запускать iperf3 -4c
Исходная ситуация (эмулятор гигаибтной сетевой карты):
# tc qdisc del root dev eth1 # iperf3 -t5 -4c 10.1.0.27
Понизим пропускную способность до мегабита в секунду:
# tc qdisc add dev eth1 root tbf rate 1mbit burst 32kbit latency 400ms # tc qdisc show # iperf3 -t5 -4c 10.1.0.27
К сожалению, наиболее популярную дисциплину — HTB — рассмотреть не успеем. Тема большая.
Д/З
Образ не изменился
Задание 7
- Суть: воспроизвести примеры из лекции, с выходным NAT-ом, пробросом порта и задержкой на интерфейсе.
- Площадка (сетевые адреса и маршруты настроены заранее, в отчёт не входят)
A — адрес delayed
B — адрес client
R — «внешний» (для доступа из srv) адрес router
- Отчёт:
report 7 router
Настроить исходящий NAT на интерфейсе R, который работал бы для A и B (Например, для всей их сети)
Настроить проброс порта 1234 при подключении к R → на порт 1234 адреса B
Запустить tcpdump на «внешнем» интерфейсе, который отловит все пакеты TCP-соединения от client на srv и несколько пакетов ping-а, а затем сам остановится (ключ -c). Если сложно — просто остановите перед продолжением отчёта.
report 7 srv
Принять одно TCP-соединение на порт 1212 (в tcpdump на router-е должно быть видно, что это соединение от R, потому что SNAT)
report 7 delayed
Настроить (с помощью netem) задержку при отсылке пакетов в 200ms
Немного по ping-ать srv (чтобы запущенный там tcpdump остановился)
report 7 client
Отправить несколько строк на порт 1212 машины srv
Запустить ping адреса A (в отчёте должна быть видна задержка ⩾ 200ms)
- Принять одно TCP-соединение на порт 1234
(продолжение отчёта srv)
Отправить несколько строк на порт 1234 адреса R (его должен принять B, потому что DNAT)
Четыре отчёта (названия сохранить, должно быть: report.07.router, report.07.srv, report.07.client, report.07.delayed) переслать одним письмом в качестве приложений на uneexlectures@cs.msu.ru
В теме письма должно встречаться слово LinuxNetwork2026
