Полоса канала редко остаётся свободной: обновления, резервные копии и загрузки быстро выедают её, а видеозвонок начинает рассыпаться. В Linux ограничение трафика выполняет подсистема tc из пакета iproute2. Она умеет резать скорость, делить канал между сервисами и эмулировать задержку для тестов. Ниже — рабочая схема с qdisc, классами и фильтрами, команды для обоих направлений, диагностика и защита от потери SSH-доступа.
Как устроен tc: qdisc, классы и фильтры
Дисциплина очереди, или qdisc, определяет, как ядро ставит пакеты в очередь и с какой скоростью отправляет их в интерфейс. Простая дисциплина вроде tbf задаёт общий лимит, а htb создаёт дерево классов с гарантированной полосой и потолком. Фильтры привязывают трафик к классам по портам, адресам или меткам. По умолчанию настройки действуют на исходящий трафик; для входящего нужен отдельный приём с интерфейсом IFB.
ip -brief link
tc qdisc show dev eth0
tc -s qdisc show dev eth0
tc class show dev eth0
Имя интерфейса подставьте своё: на проводных системах это часто enp3s0, на ноутбуках — wlp2s0. Команды без sudo показывают состояние, но изменять правила нужно с правами root. Счётчики -s помогают понять, сколько пакетов реально прошло через очередь и сколько было отброшено.
Быстрое ограничение: tbf
tbf — token bucket filter: он выдаёт токены с заданной скоростью и пропускает пакет, только если токенов хватает. Параметр rate задаёт среднюю скорость, burst — объём, который можно отправить залпом, latency — допустимое время ожидания пакета в очереди.
sudo tc qdisc add dev eth0 root handle 1: tbf rate 10mbit burst 32kbit latency 400ms
tc -s qdisc show dev eth0
sudo tc qdisc change dev eth0 root handle 1: tbf rate 5mbit burst 32kbit latency 400ms
sudo tc qdisc del dev eth0 root
Схема ограничивает только исходящий трафик: если провайдер даёт 100 Мбит, сервис получит 10. Для входящего потока она не сработает: пакеты уже приняты интерфейсом, и отбросить их можно лишь после перенаправления в IFB. Скорость сверяйте с iperf3: единицы и накладные расходы расходятся.
HTB: несколько классов на одном интерфейсе
HTB выделяет гарантированные полосы и разрешает занимать свободное место до потолка ceil. В примере ниже корневой класс получает 50 Мбит, SSH — 10 Мбит с потолком 20, HTTPS — 25 Мбит, остальное уходит в класс по умолчанию. Фильтры u32 смотрят на порт назначения у пакетов, покидающих хост.
sudo tc qdisc add dev eth0 root handle 1: htb default 30
sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 50mbit ceil 50mbit
sudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10mbit ceil 20mbit
sudo tc class add dev eth0 parent 1:1 classid 1:20 htb rate 25mbit ceil 40mbit
sudo tc class add dev eth0 parent 1:1 classid 1:30 htb rate 5mbit ceil 10mbit
sudo tc filter add dev eth0 protocol ip parent 1: prio 1 u32 match ip dport 22 0xffff flowid 1:10
sudo tc filter add dev eth0 protocol ip parent 1: prio 2 u32 match ip dport 443 0xffff flowid 1:20
tc -s class show dev eth0
Класс 1:30 указан в default, поэтому неразмеченный трафик не остаётся без ограничения. Гарантия rate действует при перегрузке, а ceil позволяет забрать простаивающую полосу у соседей. Для нестандартного порта добавьте фильтр или пометьте пакеты через nftables и fwmark.
Входящий трафик через IFB
Чтобы ограничить скачивание, входящие пакеты перенаправляют с физического интерфейса на виртуальный IFB и уже там ставят в очередь. Модуль ifb должен быть загружен, а интерфейс — поднят. Правило mirred перенаправляет весь входящий поток, поэтому не включайте его без необходимости на нагруженном маршрутизаторе.
sudo modprobe ifb
sudo ip link add ifb0 type ifb
sudo ip link set ifb0 up
sudo tc qdisc add dev eth0 handle ffff: ingress
sudo tc filter add dev eth0 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifb0
sudo tc qdisc add dev ifb0 root handle 2: tbf rate 20mbit burst 64kbit latency 400ms
tc -s qdisc show dev ifb0
После включения ingress проверьте ifb0: вывод tc -s qdisc show dev ifb0 покажет, доходят ли пакеты до виртуального интерфейса и растёт ли счётчик отбросов.
После перезагрузки правила исчезнут. Для постоянной схемы создайте unit systemd или скрипт, который ждёт появления сетевого интерфейса и применяет настройки. Ingress подключается в точке ffff: и удаляется отдельно: tc qdisc del dev eth0 ingress.
Диагностика и типичные ошибки
Проверьте модули ядра sch_htb, sch_tbf и act_mirred и сравните счётчики до и после нагрузки. Если скорость ниже ожидаемой, ищите узкое место не только в tc: драйвер, offload и Wi-Fi дают тот же эффект.
sudo modprobe sch_htb sch_tbf act_mirred
tc -s -d qdisc show dev eth0
iperf3 -s
iperf3 -c 192.168.1.10 -t 20 -R
Проверьте offload: агрегация пакетов на некоторых драйверах искажает картину. Счётчики интерфейса и настройки ethtool помогают отличить ограничение tc от проблемы драйвера.
ethtool -k eth0 | grep -E 'tso|gso|tx-checksumming'
ip -s link show eth0
Сохраните вывод tc -s qdisc и tc -s class до и после настройки — это база для сравнения. iperf3 -R меняет направление: клиент принимает данные, сервер отправляет. Так удобно проверять входящий лимит. На удалённом сервере запускайте скрипт отката через systemd-run --on-active=5m и держите вторую сессию: ошибка в фильтре может отрезать SSH.
Чек-лист
- Определите, что именно ограничиваете: исходящий канал, входящий или отдельный сервис.
- Проверьте имя интерфейса и наличие модулей ядра.
- Начните с
tbf, затем переходите к дереву HTB. - Для ingress используйте IFB и не забывайте удалять правило
ffff:. - Сделайте правила постоянными через systemd или NetworkManager dispatcher.
Итог
tc даёт точный контроль над очередями, но требует дисциплины: сначала измерение, потом правило, затем проверка и откат. Для простой задачи хватает tbf, для разделения сервисов — HTB, для входящего потока — IFB, а netem нужен только для тестов. Настройки не сохраняются после перезагрузки, поэтому оформляйте их как управляемый сервис и документируйте: иначе причину странной скорости придётся искать наугад.