Шейпинг исходящего трафика для TCP порта в Linux — различия между версиями

Материал из Википедия MyArena.ru
Перейти к: навигация, поиск
(Новая страница: «== Шейпинг исходящего трафика для TCP порта в Linux == Инструкция описывает, как ограничить (ш…»)
 
 
(не показана 1 промежуточная версия этого же участника)
Строка 13: Строка 13:
 
Важно: шейпинг применяется к '''исходящему (исходящему от сервера) трафику''' на указанном сетевом интерфейсе, для TCP-пакетов с '''source port''', равным указанному порту.
 
Важно: шейпинг применяется к '''исходящему (исходящему от сервера) трафику''' на указанном сетевом интерфейсе, для TCP-пакетов с '''source port''', равным указанному порту.
  
Схема поддерживает '''три группы (тарифа)''' по полосе пропускания:
+
Схема поддерживает от '''одной до трёх групп (тарифов)''' по полосе пропускания - обязательна только первая, остальные две опциональны:
* группа 1 - свой список стран, свой лимит;
+
* '''список стран непустой''' - для этих стран действует свой лимит, а весь остальной трафик на этом порту, не попавший ни в одну группу, '''не шейпится вообще''' (идёт без ограничений).
* группа 2 - свой список стран, свой лимит;
+
* '''список стран пустой ("")''' - эта группа становится "тарифом по умолчанию" на весь трафик, не попавший в другие группы (если это единственная группа - лимит действует на весь трафик порта целиком).
* группа 3 - '''всегда''' catch-all, то есть ловит весь трафик на этом порту, который не попал в группы 1 и 2 (даже если для неё указан непустой список стран - она всё равно работает как «всё остальное», см. раздел «Параметры» ниже).
+
* '''пустой список стран допускается максимум в одной группе''' - если оставить его пустым в двух и более, скрипт откажется запускаться с понятной ошибкой.
  
 
=== Требования ===
 
=== Требования ===
Строка 102: Строка 102:
  
 
<pre>
 
<pre>
sudo ./geo-shaper.sh <интерфейс> <порт> <лимит1_bps> <страны1> <лимит2_bps> <страны2> <лимит3_bps> <страны3>
+
sudo ./geo-shaper.sh <интерфейс> <порт> <лимит1_bps> <страны1> [<лимит2_bps> <страны2> [<лимит3_bps> <страны3>]]
 
</pre>
 
</pre>
 +
 +
Обязательны только первые четыре параметра (интерфейс, порт, лимит1, страны1). Вторая и третья группа - опциональны.
  
 
==== Параметры ====
 
==== Параметры ====
Строка 110: Строка 112:
 
* '''порт''' - TCP-порт (source port), для которого применяется шейпинг, например <code>443</code> или <code>80</code>.
 
* '''порт''' - TCP-порт (source port), для которого применяется шейпинг, например <code>443</code> или <code>80</code>.
 
* '''лимит1_bps / лимит2_bps / лимит3_bps''' - лимит полосы в '''битах в секунду''' (не байтах), например <code>700000000</code> = 700 Мбит/с.
 
* '''лимит1_bps / лимит2_bps / лимит3_bps''' - лимит полосы в '''битах в секунду''' (не байтах), например <code>700000000</code> = 700 Мбит/с.
* '''страны1 / страны2''' - список ISO-кодов стран через запятую в кавычках, например <code>"RU,UA,KZ,BY"</code>. Для этих стран действует свой лимит.
+
* '''страны1 / страны2 / страны3''' - список ISO-кодов стран через запятую в кавычках, например <code>"RU,UA,KZ,BY"</code>, для этих стран действует свой лимит. '''Пустая строка <code>""</code>''' в любой из групп превращает её в тариф "для всех остальных" - допускается только в одной группе одновременно.
* '''страны3''' - список стран для третьей группы. '''Группа 3 всегда работает как «все остальные страны»''' - то есть ловит весь трафик на данном порту, не попавший в группы 1 и 2, независимо от того, что указано в этом параметре. Обычно сюда передают просто пустую строку <code>""</code>.
+
 
 +
Если ни одна группа не оставлена с пустым списком стран, трафик, не попавший ни в одну группу, идёт '''без каких-либо ограничений''' (не шейпится).
  
 
Скрипт сам скачивает и кэширует базу MaxMind GeoLite2-Country (обновляется раз в 7 дней), поэтому первый запуск может занять чуть больше времени, чем последующие.
 
Скрипт сам скачивает и кэширует базу MaxMind GeoLite2-Country (обновляется раз в 7 дней), поэтому первый запуск может занять чуть больше времени, чем последующие.
  
=== Пример: лимит для стран СНГ 700 Мбит/с, для остальных - 200 Мбит/с ===
+
=== Примеры ===
 +
 
 +
==== Лимит только для одной группы стран, остальное без ограничений ====
 +
 
 +
<pre>
 +
sudo /opt/geo-shaper.sh eth1 80 600000000 "RU,UA,KZ,BY"
 +
</pre>
 +
 
 +
Трафик к RU/UA/KZ/BY ограничен 600 Мбит/с суммарно, весь остальной трафик на порту 80 идёт как обычно, без шейпинга.
 +
 
 +
==== Единый лимит на весь трафик порта ====
 +
 
 +
<pre>
 +
sudo /opt/geo-shaper.sh eth1 80 100000000 ""
 +
</pre>
 +
 
 +
Пустой список стран у единственной группы - лимит 100 Мбит/с действует на весь трафик порта целиком, без разделения по странам.
  
В этом сценарии нужны фактически только два тарифа, поэтому вторая группа оставляется с пустым списком стран (не используется), а вся «остальная» полоса (третья группа) ограничивается 200 Мбит/с.
+
==== Лимит для стран СНГ 700 Мбит/с, для остальных - 200 Мбит/с ====
  
 
<pre>
 
<pre>
 
sudo /opt/geo-shaper.sh eth1 80 \
 
sudo /opt/geo-shaper.sh eth1 80 \
 
   700000000 "RU,BY,KZ,KG,TJ,TM,UZ,MD,AM,AZ,UA" \
 
   700000000 "RU,BY,KZ,KG,TJ,TM,UZ,MD,AM,AZ,UA" \
  200000000 "" \
 
 
   200000000 ""
 
   200000000 ""
 
</pre>
 
</pre>
Строка 128: Строка 146:
 
Что при этом происходит:
 
Что при этом происходит:
 
* '''700 Мбит/с''' - суммарный лимит для трафика в адрес IP из стран СНГ: RU, BY, KZ, KG, TJ, TM, UZ, MD, AM, AZ, UA (список условный, набор стран можно скорректировать под свои задачи);
 
* '''700 Мбит/с''' - суммарный лимит для трафика в адрес IP из стран СНГ: RU, BY, KZ, KG, TJ, TM, UZ, MD, AM, AZ, UA (список условный, набор стран можно скорректировать под свои задачи);
* вторая группа не используется (пустой список стран, никакой трафик под неё не попадёт);
+
* '''200 Мбит/с''' - лимит для всего остального трафика на порту 80 (все страны, не вошедшие в список выше) - вторая группа оставлена с пустым списком стран, поэтому работает как тариф "для всех остальных".
* '''200 Мбит/с''' - лимит для всего остального трафика на порту 80 (все страны, не вошедшие в список выше).
 
  
 
Замените <code>eth1</code> на свой интерфейс и <code>80</code> на реальный порт сервиса.
 
Замените <code>eth1</code> на свой интерфейс и <code>80</code> на реальный порт сервиса.
 +
 +
==== Три группы (если нужно разделить "остальных" тоже на два тарифа) ====
 +
 +
<pre>
 +
sudo /opt/geo-shaper.sh eth1 443 \
 +
  600000000 "RU,UA,KZ,BY" \
 +
  400000000 "DE,US,FR" \
 +
  100000000 ""
 +
</pre>
  
 
=== Проверка работы ===
 
=== Проверка работы ===
Строка 155: Строка 181:
 
</pre>
 
</pre>
  
Содержимое файла (замените интерфейс, порт и параметры на свои - в примере используются значения из раздела «Пример» выше):
+
Содержимое файла (замените интерфейс, порт и параметры на свои - в примере используются значения из раздела «Примеры» выше, вариант с двумя группами):
  
 
<pre>
 
<pre>
Строка 166: Строка 192:
 
Type=oneshot
 
Type=oneshot
 
RemainAfterExit=yes
 
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 "" 200000000 ""'
+
ExecStart=/bin/bash -c '/opt/geo-shaper.sh eth1 80 700000000 "RU,BY,KZ,KG,TJ,TM,UZ,MD,AM,AZ,UA" 200000000 ""'
  
 
[Install]
 
[Install]
Строка 194: Строка 220:
 
==== Изменение параметров в будущем ====
 
==== Изменение параметров в будущем ====
  
Если нужно поменять лимиты или списки стран - отредактируйте строку <code>ExecStart</code> в файле <code>/etc/systemd/system/geo-shaper.service</code>, затем примените изменения:
+
Если нужно поменять лимиты, списки стран или количество групп (1, 2 или 3) - отредактируйте строку <code>ExecStart</code> в файле <code>/etc/systemd/system/geo-shaper.service</code>, затем примените изменения:
  
 
<pre>
 
<pre>

Текущая версия на 18:17, 23 июля 2026

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