Шейпинг исходящего трафика для TCP порта в Linux
Содержание
Шейпинг исходящего трафика для TCP порта в Linux
Инструкция описывает, как ограничить (шейпить) исходящую полосу пропускания для конкретного TCP-порта в зависимости от гео-принадлежности IP-адреса клиента (страны), используя nftables и tc/HTB.
Описание
Готовый скрипт geo-shaper.sh делает следующее:
- скачивает базу MaxMind GeoLite2-Country (CSV) и строит из неё списки подсетей (CIDR) по странам;
- через nftables помечает (fwmark) исходящие TCP-пакеты с заданного порта в зависимости от того, к какой группе стран относится IP-адрес назначения;
- через tc (HTB) создаёт по одному классу на каждую группу и ограничивает скорость каждой группы своим значением.
Важно: шейпинг применяется к исходящему (исходящему от сервера) трафику на указанном сетевом интерфейсе, для TCP-пакетов с source port, равным указанному порту.
Схема поддерживает от одной до трёх групп (тарифов) по полосе пропускания - обязательна только первая, остальные две опциональны:
- список стран непустой - для этих стран действует свой лимит, а весь остальной трафик на этом порту, не попавший ни в одну группу, не шейпится вообще (идёт без ограничений).
- список стран пустой ("") - эта группа становится "тарифом по умолчанию" на весь трафик, не попавший в другие группы (если это единственная группа - лимит действует на весь трафик порта целиком).
- пустой список стран допускается максимум в одной группе - если оставить его пустым в двух и более, скрипт откажется запускаться с понятной ошибкой.
Требования
Пример установки приведён для Ubuntu 22.04 LTS / 24.04 LTS (для Debian 11/12 команды идентичны, пакет-менеджер тот же - apt).
Необходимые пакеты:
| Пакет | Назначение |
|---|---|
| nftables | классификация трафика (fwmark) по подсетям стран |
| iproute2 | утилита tc - собственно шейпинг (HTB)
|
| curl | загрузка скрипта и базы GeoIP |
| unzip | распаковка архива с базой MaxMind GeoLite2-Country CSV |
| gawk | разбор и объединение CSV-файлов базы GeoIP |
Установка пакетов
sudo apt update sudo apt install -y nftables iproute2 curl unzip gawk sudo systemctl enable --now nftables
Загрузка скрипта
Скрипт можно скачать по прямой ссылке и сохранить, например, в /opt/geo-shaper.sh:
sudo curl -fsSL -o /opt/geo-shaper.sh \ https://wiki.myarena.ru/soft/geo-shaper.sh sudo chmod +x /opt/geo-shaper.sh
Как узнать имя сетевого интерфейса
Перед запуском скрипта нужно знать имя интерфейса (например, eth1, enp1s0), через который сервер отдаёт трафик наружу.
Способ 1: автоматически, только имя
Команда определяет, через какой интерфейс реально уходит трафик в интернет, и выводит только его имя:
ip route get 1.1.1.1 | grep -oP 'dev \K\S+'
Пример вывода:
eth1
Способ 2: список всех интерфейсов и как выбрать нужный
ip -br addr show
Пример вывода:
lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 10.0.0.5/24 fe80::a00:27ff:fe.../64 eth1 UP 203.0.113.10/24 fe80::a00:27ff:fe.../64 docker0 DOWN 172.17.0.1/16
Как понять, какой интерфейс нужен:
-
lo(loopback) - всегда игнорировать, это не сетевая карта. - Виртуальные интерфейсы вроде
docker0,br-*,veth*,tun*,tap*обычно тоже не нужны, если только вы не шейпите именно докер-сеть. - Нужен интерфейс в состоянии UP.
- Выбирайте интерфейс, у которого назначен публичный/внешний IP-адрес сервера - тот, на который клиенты подключаются к вашему сервису. В примере выше это
eth1с адресом203.0.113.10. - Если сомневаетесь - сверьте с выводом команды из способа 1 (
ip route get 1.1.1.1), она укажет ровно тот интерфейс, что смотрит в интернет.
Более подробная информация (MAC-адрес, MTU, статистика) - ip addr show или классический ifconfig -a (если установлен пакет net-tools).
Синтаксис запуска
sudo ./geo-shaper.sh <интерфейс> <порт> <лимит1_bps> <страны1> [<лимит2_bps> <страны2> [<лимит3_bps> <страны3>]]
Обязательны только первые четыре параметра (интерфейс, порт, лимит1, страны1). Вторая и третья группа - опциональны.
Параметры
- интерфейс - сетевой интерфейс, на котором шейпится исходящий трафик (например,
eth1,enp1s0). - порт - TCP-порт (source port), для которого применяется шейпинг, например
443или80. - лимит1_bps / лимит2_bps / лимит3_bps - лимит полосы в битах в секунду (не байтах), например
700000000= 700 Мбит/с. - страны1 / страны2 / страны3 - список ISO-кодов стран через запятую в кавычках, например
"RU,UA,KZ,BY", для этих стран действует свой лимит. Пустая строка""в любой из групп превращает её в тариф "для всех остальных" - допускается только в одной группе одновременно.
Если ни одна группа не оставлена с пустым списком стран, трафик, не попавший ни в одну группу, идёт без каких-либо ограничений (не шейпится).
Скрипт сам скачивает и кэширует базу MaxMind GeoLite2-Country (обновляется раз в 7 дней), поэтому первый запуск может занять чуть больше времени, чем последующие.
Примеры
Лимит только для одной группы стран, остальное без ограничений
sudo /opt/geo-shaper.sh eth1 80 600000000 "RU,UA,KZ,BY"
Трафик к RU/UA/KZ/BY ограничен 600 Мбит/с суммарно, весь остальной трафик на порту 80 идёт как обычно, без шейпинга.
Единый лимит на весь трафик порта
sudo /opt/geo-shaper.sh eth1 80 100000000 ""
Пустой список стран у единственной группы - лимит 100 Мбит/с действует на весь трафик порта целиком, без разделения по странам.
Лимит для стран СНГ 700 Мбит/с, для остальных - 200 Мбит/с
sudo /opt/geo-shaper.sh eth1 80 \ 700000000 "RU,BY,KZ,KG,TJ,TM,UZ,MD,AM,AZ,UA" \ 200000000 ""
Что при этом происходит:
- 700 Мбит/с - суммарный лимит для трафика в адрес IP из стран СНГ: RU, BY, KZ, KG, TJ, TM, UZ, MD, AM, AZ, UA (список условный, набор стран можно скорректировать под свои задачи);
- 200 Мбит/с - лимит для всего остального трафика на порту 80 (все страны, не вошедшие в список выше) - вторая группа оставлена с пустым списком стран, поэтому работает как тариф "для всех остальных".
Замените eth1 на свой интерфейс и 80 на реальный порт сервиса.
Три группы (если нужно разделить "остальных" тоже на два тарифа)
sudo /opt/geo-shaper.sh eth1 443 \ 600000000 "RU,UA,KZ,BY" \ 400000000 "DE,US,FR" \ 100000000 ""
Проверка работы
Убедиться, что правила загружены и трафик классифицируется:
nft list table inet geoshaper tc -s class show dev eth1 tc -s qdisc show dev eth1
Счётчики пакетов/байт в выводе этих команд должны расти по мере того, как идёт трафик на шейпленный порт - так можно убедиться, что классификация по странам и ограничение скорости реально работают.
Автозагрузка (запуск при старте сервера)
Правила tc и nftables, которые создаёт скрипт, не сохраняются при перезагрузке сервера - после ребута их нужно применить заново. Проще всего сделать это через systemd-сервис, который будет запускать geo-shaper.sh автоматически после старта сети.
1. Создать unit-файл
sudo nano /etc/systemd/system/geo-shaper.service
Содержимое файла (замените интерфейс, порт и параметры на свои - в примере используются значения из раздела «Примеры» выше, вариант с двумя группами):
[Unit] Description=GEO-based TCP traffic shaping (geo-shaper.sh) After=network-online.target Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/bin/bash -c '/opt/geo-shaper.sh eth1 80 700000000 "RU,BY,KZ,KG,TJ,TM,UZ,MD,AM,AZ,UA" 200000000 ""' [Install] WantedBy=multi-user.target
Пояснения:
-
After=network-online.target/Wants=network-online.target- гарантирует, что сервис стартует только после того, как сеть реально поднята (интерфейс существует, есть доступ в интернет для загрузки базы GeoIP). -
Type=oneshot+RemainAfterExit=yes- скрипт не "висит" в фоне, а один раз настраиваетnftables/tcи завершается; при этом systemd считает сервис активным (не упавшим). - Команда обёрнута в
/bin/bash -c '...'- это надёжнее, чем передавать аргументы напрямую вExecStart, особенно из-за пустых строк""в параметрах стран, которые старые версии systemd могут разобрать некорректно без обёртки в bash.
2. Включить и запустить сервис
sudo systemctl daemon-reload sudo systemctl enable --now geo-shaper.service
3. Проверить статус
systemctl status geo-shaper.service
Должно быть видно active (exited) - это нормальное конечное состояние для Type=oneshot с RemainAfterExit=yes.
Изменение параметров в будущем
Если нужно поменять лимиты, списки стран или количество групп (1, 2 или 3) - отредактируйте строку ExecStart в файле /etc/systemd/system/geo-shaper.service, затем примените изменения:
sudo systemctl daemon-reload sudo systemctl restart geo-shaper.service
Скрипт идемпотентен, поэтому повторный запуск безопасен - старая конфигурация nftables/tc сбрасывается автоматически перед применением новой.