Вопрос или проблема
Я пытаюсь ограничить входящую и исходящую пропускную способность с задержкой через определенный порт, используя TC, но не могу заставить это работать на 100%. Я не уверен, правильно ли я это сделал.
Когда я пингую Google, задержка увеличивается. Я использовал iperf для тестирования пропускной способности через порт 5001. Ограничение пропускной способности, похоже, работает на клиенте, где я применил указанные ниже настройки (клиент как iperf -s, входящий), однако, если я использую этот клиент (исходящий) для подключения к другому, пропускная способность ограничивается 1–2 мбит, вместо ожидаемых 5 мбит.
То, что я хотел бы, например, установить задержку на 100 мс, исходящую/входящую пропускную способность ограничить до 5 мбит, и применить все эти правила к порту 5001. Я на правильном пути или неправильно понимаю концепцию tc?
Ip link add name ifb0 type ifb 2>/dev/null || :
Ip link set dev ifb0 up
Tc qdisc add dev ifb0 root handle 1: htb
Tc class dev ifb0 parent 1: classid 1:20 htb rate 5mbit
Tc qdisc add dev ifb0 parent 1:20 handle 20: sfq perturb 10
Tc filter add dev ifb0 parent 1: protocol ip prio 1 basic match ‘cmp(u16 at 2 layer transport eq 5001)’ flowid 1:20
Tc qdisc add dev ens192 root netem delay 200ms
Tc qdisc add dev ens192 ingress
Tc filter add add dev ens192 ingress protocol ip basic match ‘cmp(u16 at 2 layer transport eq 5001)’ action mirred egress redirect dev ifb0
Вот сценарий, который я использовал, когда у моего интернет-провайдера были проблемы с буферизацией несколько лет назад. Его необходимо запускать как root
для start
и stop
, но вы можете запускать status
без root
.
#!/bin/bash
# Скрипт управления трафиком (AQM, fq_codel+tbf)
# Автор: Mikko Rantalainen <[email protected]>
# Лицензия: MIT (X11)
# Использование:
# 21/0.8 Mbps соединение (ADSL2): DOWNLINK_RATE=21.7Mbit UPLINK_RATE=0.8Mbit TBF_LATENCY=500ms DOWNLINK_BURST=1500 UPLINK_BURST=1500 bin/traffic-shaping start
# 100/100 Mbps соединение: ./traffic-shaping
# 1/1 GBps соединение: DOWNLINK_RATE=1Gbit UPLINK_RATE=1Gbit TBF_LATENCY=15ms bin/traffic-shaping start
# Обратите внимание, что использование низкой TBF_LATENCY требует мощного процессора.
#
# См. также: https://www.bufferbloat.net/projects/codel/wiki/Best_practices_for_benchmarking_Codel_and_FQ_Codel/
# См. также: http://www.jfcarter.net/~jimc/documents/voip-qos-1609.html
# TODO: man 7 tc-hfcs (вместо tbf)
# TODO: попробуйте ограничить пропускную способность, используя только fq_codel (без tbf) - https://gist.github.com/eqhmcow/939373/8d2e8ad745a7e0a8ddb21abde42538034c2ea65b
#
set -e # прервать, если команда возвращает не нулевой статус (ошибка)
#set -x # подробное выполнение
# Примечание: ip route иногда выводит несколько строк с префиксом "default", используйте первую
DEV="${DEV:=$(ip route | grep "^default " | head -n1 | grep -Po "(?<=dev )[^ ]+")}"
# входящие:
DOWNLINK_RATE="${DOWNLINK_RATE:=103000kbit}" # или, например, "21.5Mbit"
# исходящие:
UPLINK_RATE="${UPLINK_RATE:=102000kbit}"
CODEL_INTERVAL="${CODEL_INTERVAL:=100ms}" # обычно 100ms, высокоскоростные соединения с низкой задержкой могут требовать меньших значений
CODEL_TARGET="${CODEL_TARGET:=5ms}" # единица "us" также доступна, обычно 5%-10% от CODEL_INTERVAL
CODEL_LIMIT="${CODEL_LIMIT:=1001}" # уменьшите, чтобы снизить задержку, слишком низкие значения будут ограничивать пропускную способность
CODEL_FLOWS="${CODEL_FLOWS:=1024}"
# установите всплеск как можно выше, не вызывая потерь пакетов в начале соединений
DOWNLINK_BURST="${DOWNLINK_BURST:=8000}"
UPLINK_BURST="${UPLINK_BURST:=55000}"
TBF_LATENCY="${TBF_LATENCY:=10ms}" # установите на более низкую задержку, чтобы улучшить контроль над ограничением пропускной способности, байты UPLINK_BURST должны быть отправлены за это время
IFB="$DEV.in" # логически это должно быть $DEV.ingress, но максимальный лимит может быть превышен (например, dev = enp0s29u1u6 -> enp0s29u1u6.ingress слишком длинное)
INITCWND="${INITCWND:=15}" # начальное окно заторов, уменьшите, если наблюдается потеря пакетов
INITRWND="${INITRWND:=30}" # начальное окно приема (рекламируемое от клиента к серверам), может быть довольно высоким, если у вас много пропускной способности (Windows и OS X имеют это около 40)
# См. также: https://www.cdnplanet.com/blog/tune-tcp-initcwnd-for-optimum-performance/
# См. также: https://www.acc.umu.se/~maswan/linux-netperf.txt
# См. также: http://intronetworks.cs.luc.edu/1/html/newtcps.html
# См. также: https://www.ietf.org/proceedings/84/slides/slides-84-iccrg-1.pdf
configure_shaping()
{
# ИСХОДЯЩИЙ (исходящий трафик, "загрузки"):
# настройка ограничения пропускной способности:
tc qdisc add dev "$DEV" root handle 1: tbf rate "$UPLINK_RATE" burst "$UPLINK_BURST" latency "$TBF_LATENCY"
# настройка fq_codel для формирования пропускной способности
tc qdisc add dev "$DEV" parent 1: fq_codel quantum 300 limit "$CODEL_LIMIT" target "$CODEL_TARGET" interval "$CODEL_INTERVAL" flows "$CODEL_FLOWS" noecn
# ВХОДЯЩИЙ (входящий трафик, "загрузки"):
ip link show ifb0 >&/dev/null && HAD_IFB0=1 || HAD_IFB0=0
ip link show ifb1 >&/dev/null && HAD_IFB1=1 || HAD_IFB1=0
# настройка ограничения пропускной способности (входящее ограничение требует IFB или Intermediate Functional Block, см. https://wiki.linuxfoundation.org/networking/ifb):
tc qdisc add dev "$DEV" handle ffff: ingress
ip link add name "$IFB" type ifb
tc qdisc add dev "$IFB" root handle 1: tbf rate "$DOWNLINK_RATE" burst "$DOWNLINK_BURST" latency "$TBF_LATENCY"
# настройка fq_codel для формирования пропускной способности
tc qdisc add dev "$IFB" parent 1: fq_codel quantum 300 limit "$CODEL_LIMIT" target "$CODEL_TARGET" interval "$CODEL_INTERVAL" flows "$CODEL_FLOWS" ecn
ip link set dev "$IFB" up
# подключение фильтрации входящего трафика к фактическому WAN устройству
tc filter add dev "$DEV" parent ffff: protocol all prio 10 u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev "$IFB"
# Настройка initcwnd и initrwnd
# Обратите внимание, что "ip route" иногда выводит несколько строк с префиксом "default" — мы всегда будем использовать первую
ip route change $(ip route | grep ^default | head -n1) initcwnd "$INITCWND" initrwnd "$INITRWND"
## настройка алгоритма управления заторами CDG
##modprobe tcp_cdg && echo cdg > /proc/sys/net/ipv4/tcp_congestion_control
# кубический алгоритм, кажется, в целом лучше с AQM, давайте настроим его
echo cubic > /proc/sys/net/ipv4/tcp_congestion_control || true
echo 13 > /sys/module/tcp_cubic/parameters/hystart_low_window
echo 0 > /proc/sys/net/ipv4/tcp_slow_start_after_idle
# TODO: попробуйте modprobe tcp_westwood
# Удалить все отключения, которые увеличивают задержку (обратите внимание, что если у вас недостаточно мощности процессора, это может снизить максимальную пропускную способность!)
# Обратите внимание, что из-за ошибок ethtool названия, используемые здесь, не совпадают с ethtool --show-offload, смотрите 'man ethtool' для подробностей!
# игнорировать возможные ошибки и продолжать
ethtool --offload "$DEV" gso off || true
ethtool --offload "$DEV" gro off || true
ethtool --offload "$DEV" tx off || true
ethtool --offload "$DEV" rx off || true
ethtool --offload "$DEV" rxvlan off || true
ethtool --offload "$DEV" txvlan off || true
# очистить поврежденные ip links add ... type ifb, иногда создавая дополнительные ifb links (называемые "ifb0" и "ifb1")
test "$HAD_IFB0" = "0" && ip link show ifb0 >&/dev/null && ip link del ifb0
test "$HAD_IFB1" = "0" && ip link show ifb1 >&/dev/null && ip link del ifb1
}
remove_shaping()
{
#set -x
tc qdisc list | grep -q "ingress" && tc qdisc del dev "$DEV" ingress || true
# Примечание: мы должны избегать удаления корневого qdisc, если данный ядро по умолчанию использует fq_codel, "qdisc list" выведет "fq_codel 0:" для корневого qdisc, поэтому мы ищем что-то другое
tc qdisc list | grep -q "fq_codel [1-9]" && tc qdisc del dev "$DEV" root || true
ip link show | grep -q "$IFB" && ip link del "$IFB" || true
# настройка алгоритма управления заторами CDG
modprobe tcp_cdg && echo cdg > /proc/sys/net/ipv4/tcp_congestion_control || true
#set +x
}
status()
{
echo "─── Конфигурация дисциплины очереди: ──────────────────"
tc qdisc list
echo " ПОДСКАЗКА: используйте, например, 'sudo tc qdisc del dev $DEV ingress', чтобы удалить фильтрацию входящего трафика"
echo " ПОДСКАЗКА: используйте, например, 'sudo tc qdisc del dev $DEV root', чтобы удалить фильтрацию исходящего трафика"
echo "─── ip link show: ────────────────────────────────────"
ip link show
echo " ПОДСКАЗКА: используйте, например, 'sudo ip link del $IFB', чтобы удалить устройство входящего трафика"
}
color_status()
{
status | grep --color=auto -E "^|$DEV|$IFB|rate [^ ]+"
}
# Обработка параметров
ACTION="$1"
shift || true
while [ ! -z "$1" ]
do
case "$1" in
-v|--verbose)
echo "Устройство: $DEV"
echo "Скорость загрузки (входящая): $DOWNLINK_RATE"
echo "Скорость выгрузки (исходящая): $UPLINK_RATE"
set -x
;;
*)
if [ ! -z "$2" ]; then
echo "Неизвестный параметр: '$2'" 1>&2
exit 1
fi
;;
esac
shift || true
done
case "$ACTION" in
start)
remove_shaping
configure_shaping
;;
stop)
remove_shaping
;;
status)
color_status
;;
restart)
remove_shaping
configure_shaping
;;
*)
echo "Неизвестное действие: $1" 1>&2
echo "Использование: $0 <start|stop|restart|status> [--verbose|-v]" 1>&2
exit 1
esac
Чтобы использовать этот сценарий, сохраните его, например, как traffic-shaping
и выполните chmod a+x traffic-shaping
, чтобы включить бит выполнения. Затем вы можете либо изменить значения по умолчанию в файле, либо использовать переменные окружения для настройки сценария. Например, если у вас соединение 100/100 Mbps, вы можете выполнить
DOWNLINK_RATE=95Mbit UPLINK_RATE=95Mbit TBF_LATENCY=25ms ./traffic-shaping start
как root
. И чтобы восстановить настройки по умолчанию, выполните
./traffic-shaping stop
и чтобы показать текущее состояние, выполните
./traffic-shaping stop
Обратите внимание, что указанные выше настройки не являются постоянными, поэтому вам нужно будет повторно запускать сценарий после каждой перезагрузки. Я использовал это с Ubuntu low-latency
(PREEMPT) ядром Linux и TBF_LATENCY=10ms
, поэтому я не знаю, какую задержку можно ожидать с generic
ядром. Вам, возможно, придется установить TBF_LATENCY=40ms
, если вы запускаете его с generic
ядром. Если вы запустите start
без каких-либо установленных переменных окружения, вы получите мою собственную конфигурацию, которую я использовал для соединения 100/100 Mbps FTTH с low-latency
ядром. Если у вас соединение менее 20 Mbps, вам, вероятно, нужно установить DOWNLINK_BURST=1500 UPLINK_BURST=1500
, чтобы избежать появления резких всплесков в начале новых соединений, которые могут вызвать потерю пакетов. Для медленной выгрузки вам также может потребоваться уменьшить INITCWND
(по умолчанию 15), если вы страдаете от потери пакетов / высокой задержки в начале соединения с быстрыми серверами (например, трафик веб-браузеров). Значения в диапазоне 7-10 должны быть приемлемыми для 1 Mbps соединения.
Ответ или решение
Для настройки ограничения пропускной способности и задержки с использованием tc
(Traffic Control) в Linux, с целью управления трафиком через определённый порт, вам необходимо правильно настроить правила и очереди. Давайте разберём ваш случай более подробно.
Шаг 1: Настройка интерфейса IFB
Следуйте следующим шагам для создания и настройки интерфейса ifb
(Intermediate Functional Block), который будет использоваться для ограничения входящего трафика.
ip link add name ifb0 type ifb
ip link set dev ifb0 up
Шаг 2: Ограничение пропускной способности и задержки
Для достижения цели ограничения входящего и исходящего трафика на порту 5001 с пропускной способностью 5 Mbit и задержкой 100 мс, выполните следующие команды:
# Ограничение исходящего трафика
tc qdisc add dev ens192 root handle 1: htb default 30
tc class add dev ens192 parent 1: classid 1:20 htb rate 5mbit
tc qdisc add dev ens192 parent 1:20 handle 20: sfq perturb 10
tc qdisc add dev ens192 root netem delay 100ms
# Настройка фильтра для исходящего трафика
tc filter add dev ens192 protocol ip parent 1: pref 1 basic match 'cmp(u16 at 2 layer transport eq 5001)' flowid 1:20
# Настройка входящего трафика
tc qdisc add dev ens192 ingress
tc filter add dev ens192 parent ffff: protocol ip basic match 'cmp(u16 at 2 layer transport eq 5001)' action mirred egress redirect dev ifb0
# Ограничение входящего трафика
tc qdisc add dev ifb0 root handle 1: htb default 30
tc class add dev ifb0 parent 1: classid 1:20 htb rate 5mbit
tc qdisc add dev ifb0 parent 1:20 handle 20: sfq perturb 10
Шаг 3: Проверка и диагностика
После выполнения вышеуказанных команд вы можете проверить настройки с помощью команды:
tc -s qdisc show dev ens192
tc -s qdisc show dev ifb0
Эти команды позволят вам увидеть текущие настройки и статистику использования полосы пропускания.
Объяснение кода
-
Создание и настройка IFB: Мы создаём интерфейс
ifb0
и устанавливаем его в активное состояние. -
Параметры для исходящего трафика: Используем
htb
(Hierarchical Token Bucket) для классификации пакетов по классу с максимальной пропускной способностью 5 Mbit. Для имитации задержки добавляемnetem
. -
Фильтры: Создаём фильтр на исходящий и входящий трафик, который будет направлять пакеты, уходящие и приходящие на порт 5001, в соответствующие очереди.
Заключение
После настройки вы должны быть в состоянии наблюдать задержку и ограниченную полосу пропускания для трафика, проходящего через указанный порт. Убедитесь, что другие факторы, такие как настройки вашего оборудования или программного обеспечения, не влияют на результаты тестирования.
Используйте такие инструменты, как iperf
, для мониторинга похоже на ожидаемое поведение вашей сети, проверяя, что пропускная способность и задержка настроены правильно.