Шейпинг исходящего трафика для TCP порта в Linux

Материал из Википедия MyArena.ru
Перейти к: навигация, поиск

Шейпинг исходящего трафика для 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