Коли на сервері одночасно крутяться бекапи, веб і ви говорите по VoIP, легко отримати буферблоат і лаги. Давайте налаштуємо розумний QoS і traffic shaping за допомогою tc та nftables, щоб VoIP звучав чітко 📞, SSH підключення залишалося миттєвим, а веб‑трафік — стабільним. Це практичний гайд для вашого сервер Linux і щоденних мережеві налаштування у терміналі. Поїхали! 🚀

Передумови та перевірки

Потрібні права root (або sudo), встановлені iproute2 та nftables. Також визначте ваш WAN‑інтерфейс (наприклад, eth0) і реальні ліміти каналу від провайдера (краще взяти ~90–95% від піку).

# Debian/Ubuntu
sudo apt update
sudo apt install -y iproute2 nftables

# Увімкнути nftables
sudo systemctl enable --now nftables

# Дізнатися інтерфейси
ip -br a

Позначимо: WAN_IF=eth0, UPLOAD=40mbit, DOWNLOAD=200mbit (замініть на свої значення).

Крок‑за‑кроком: пріоритети для VoIP, SSH і веб

1) Встановлюємо HTB на вихід (egress)

Створимо дерево класів: high для VoIP/SSH, medium для веб, best‑effort — для всього іншого. Листові qdisc — fq_codel для зниження затримок.

export WAN_IF=eth0
export UPLOAD=40mbit   # ваш аплоуд

# Скинути попередні налаштування egress
sudo tc qdisc del dev $WAN_IF root 2>/dev/null || true

# Root HTB з класом за замовчуванням 1:30
sudo tc qdisc add dev $WAN_IF root handle 1: htb default 30

# Класи: high (1:10), medium (1:20), best-effort (1:30)
sudo tc class add dev $WAN_IF parent 1: classid 1:1 htb rate $UPLOAD ceil $UPLOAD
sudo tc class add dev $WAN_IF parent 1:1 classid 1:10 htb rate 10mbit ceil $UPLOAD
sudo tc class add dev $WAN_IF parent 1:1 classid 1:20 htb rate 15mbit ceil $UPLOAD
sudo tc class add dev $WAN_IF parent 1:1 classid 1:30 htb rate 1mbit  ceil $UPLOAD

# Листові qdisc з fq_codel
sudo tc qdisc add dev $WAN_IF parent 1:10 handle 110: fq_codel
sudo tc qdisc add dev $WAN_IF parent 1:20 handle 120: fq_codel
sudo tc qdisc add dev $WAN_IF parent 1:30 handle 130: fq_codel

2) Маркуємо трафік у nftables

Використаємо meta mark для класифікації: 0x1 — VoIP/SSH (high), 0x2 — веб (medium). Застосуємо як для IPv4, так і для IPv6 через таблицю inet. Збережемо мітки в conntrack, щоб усі пакети сесії потрапляли до того ж класу.

sudo nft -f - <<'EOF'
flush table inet qos

table inet qos {
  chain preroute {
    type filter hook prerouting priority mangle; policy accept;
    # Відновлюємо існуючу ct-марку в skb
    meta mark set ct mark

    # VoIP: SIP і RTP
    udp dport 5060           meta mark set 0x1; ct mark set meta mark
    udp dport 5061           meta mark set 0x1; ct mark set meta mark
    udp dport 10000-20000    meta mark set 0x1; ct mark set meta mark

    # SSH
    tcp dport 22             meta mark set 0x1; ct mark set meta mark

    # Web
    tcp dport {80, 443}      meta mark set 0x2; ct mark set meta mark
  }

  chain out {
    type route hook output priority mangle; policy accept;
    # Відновлюємо ct-марку
    meta mark set ct mark

    # Локально згенерований трафік
    udp dport 5060           meta mark set 0x1; ct mark set meta mark
    udp dport 5061           meta mark set 0x1; ct mark set meta mark
    udp dport 10000-20000    meta mark set 0x1; ct mark set meta mark
    tcp dport 22             meta mark set 0x1; ct mark set meta mark
    tcp dport {80, 443}      meta mark set 0x2; ct mark set meta mark
  }
}
EOF

# Переглянути активні правила
sudo nft list table inet qos

3) Прив’язуємо мітки до класів (tc filters)

Фільтр fw читає skbmark, виставлену в nftables. Додамо правила і для IPv4, і для IPv6.

# High: mark 0x1 -> 1:10
sudo tc filter add dev $WAN_IF protocol ip   parent 1: prio 1 handle 0x1 fw flowid 1:10
sudo tc filter add dev $WAN_IF protocol ipv6 parent 1: prio 1 handle 0x1 fw flowid 1:10

# Medium: mark 0x2 -> 1:20
sudo tc filter add dev $WAN_IF protocol ip   parent 1: prio 2 handle 0x2 fw flowid 1:20
sudo tc filter add dev $WAN_IF protocol ipv6 parent 1: prio 2 handle 0x2 fw flowid 1:20

Формуємо вхідний трафік (ingress) через IFB

Вхід не можна напряму «придушити», але можна перенаправити віртуальним інтерфейсом ifb0 й обмежити там.

export DOWNLOAD=200mbit  # ваш даунлоуд

# IFB і перенаправлення ingress
sudo modprobe ifb
sudo ip link add ifb0 type ifb || true
sudo ip link set dev ifb0 up

sudo tc qdisc del dev $WAN_IF ingress 2>/dev/null || true
sudo tc qdisc add dev $WAN_IF handle ffff: ingress
sudo tc filter add dev $WAN_IF parent ffff: protocol all u32 match u32 0 0 \
  action mirred egress redirect dev ifb0

# HTB на ifb0 для download
sudo tc qdisc del dev ifb0 root 2>/dev/null || true
sudo tc qdisc add dev ifb0 root handle 2: htb default 30
sudo tc class add dev ifb0 parent 2: classid 2:1  htb rate $DOWNLOAD ceil $DOWNLOAD
sudo tc class add dev ifb0 parent 2:1 classid 2:10 htb rate 20mbit ceil $DOWNLOAD
sudo tc class add dev ifb0 parent 2:1 classid 2:20 htb rate 50mbit ceil $DOWNLOAD
sudo tc class add dev ifb0 parent 2:1 classid 2:30 htb rate 5mbit  ceil $DOWNLOAD

sudo tc qdisc add dev ifb0 parent 2:10 handle 210: fq_codel
sudo tc qdisc add dev ifb0 parent 2:20 handle 220: fq_codel
sudo tc qdisc add dev ifb0 parent 2:30 handle 230: fq_codel

# Ті ж відповідності mark -> клас
sudo tc filter add dev ifb0 protocol ip   parent 2: prio 1 handle 0x1 fw flowid 2:10
sudo tc filter add dev ifb0 protocol ipv6 parent 2: prio 1 handle 0x1 fw flowid 2:10
sudo tc filter add dev ifb0 protocol ip   parent 2: prio 2 handle 0x2 fw flowid 2:20
sudo tc filter add dev ifb0 protocol ipv6 parent 2: prio 2 handle 0x2 fw flowid 2:20

Порада: встановіть UPLOAD/DOWNLOAD на 90–95% реальної швидкості. Це знімає буферблоат і різко покращує VoIP та взаємодію по SSH підключення.

Перевірка та моніторинг

# Статистика класів і qdisc
sudo tc -s class show dev $WAN_IF
sudo tc -s qdisc show dev $WAN_IF
sudo tc -s class show dev ifb0

# Перевірити nftables-марки
sudo nft list ruleset | sed -n '/table inet qos/,/}/p'

# iPerf3: згенерувати трафік (у різних портах) і подивитися розподіл
iperf3 -c ip.of.server -p 443

Альтернативні способи

  • CAKE (простий і розумний qdisc). Мінімум правил, авто‑пріоритезація за DSCP.
    # Egress CAKE
    sudo tc qdisc replace dev $WAN_IF root cake bandwidth $UPLOAD diffserv4 nat
    
    # Ingress CAKE через ifb0
    sudo tc qdisc replace dev ifb0 root cake bandwidth $DOWNLOAD diffserv4
    
  • FireQOS (декларативна конфігурація) — швидко описуєте сервіси і пріоритети в одному файлі, далі один запуск — і правила tc згенеровані.
  • DSCP‑мітки від застосунків. Якщо ваш VoIP‑клієнт ставить EF (46), можна фільтрувати за DSCP без портів.

GUI‑спосіб через Webmin

Якщо не хочете копатися у командних рядках, скористайтеся Webmin. Він уміє управляти traffic control і firewall Linux через веб‑панель.

# Додати репозиторій Webmin (Debian/Ubuntu)
wget -qO - https://download.webmin.com/jcameron-key.asc | sudo gpg --dearmor -o /usr/share/keyrings/webmin.gpg
echo "deb [signed-by=/usr/share/keyrings/webmin.gpg] https://download.webmin.com/download/repository sarge contrib" | \
  sudo tee /etc/apt/sources.list.d/webmin.list
sudo apt update && sudo apt install -y webmin

Далі відкрийте https://ваш-сервер:10000 → Networking → Traffic Control: додайте інтерфейс, створіть класи (high/medium/default), потім у Filters зв’яжіть порти SIP/RTP/SSH/Web з потрібними класами. Не забудьте також налаштувати інгресс через ifb0 у модулі.

FAQ

Чому швидкість стала нижчою за тариф?

Ви свідомо обмежуєте швидкість до 90–95% для усунення буферблоату. Підберіть UPLOAD/DOWNLOAD ближче до реальних значень за результатами кількох спідтестів.

tc каже, що qdisc/модуль не знайдено

Перевірте наявність модулів: sch_htb, ifb, act_mirred, sch_fq_codel. Завантажте їх через modprobe або додайте в /etc/modules.

nftables застосувався, але фільтри tc не спрацьовують

Переконайтеся, що використовуєте фільтр fw і handle відповідає значенню meta mark (0x1, 0x2). Марки мають бути встановлені до маршрутизації пакета (priority mangle) — ми саме так і зробили.

VoIP все ще рипить

Додайте до high‑класу більший гарантований rate, а також переконайтеся, що RTP‑діапазон портів відповідає вашому провайдеру VoIP. Перевірте, чи немає NAT‑перетворень, що знімають DSCP.

Як зробити правила сталими після перезавантаження?

Збережіть nftables: sudo nft list ruleset > /etc/nftables.conf (служба підніме їх на boot). Команди tc покладіть у systemd‑unit або скрипт у /etc/rc.local.

Що з IPv6?

Ми використовуємо таблицю inet у nftables (одразу IPv4+IPv6) і додаємо фільтри tc для protocol ipv6 — цього достатньо.

Працює в хмарі/віртуалці?

Так, але пам’ятайте: якщо ваш провайдер уже формує трафік, ефект може бути меншим. У VPS із віртуальними інтерфейсами назви NIC інші (ens3, enp1s0 тощо).

Порада від Kernelka

Починайте з простого: два‑три класи, чіткі мітки для критичних сервісів і чесні ліміти каналу. Рухайтеся маленькими кроками: вимір — зміна — вимір. І обов’язково тримайте резервну копію чинних правил перед експериментами, щоб швидко відкотитися у разі помилки.

Підсумок

  • tc+HTB формує дерево класів, fq_codel зменшує затримки.
  • nftables ставить мітки: VoIP/SSH → high, Web → medium, інше → best‑effort.
  • Ingress обробляємо через ifb0 і ті ж самі правила.
  • CAKE — зручна альтернатива з розумними дефолтами.
  • Webmin допоможе налаштувати через GUI.
  • Фіксуйте правила і робіть їх сталими через nftables і systemd.

Тепер ваш трафік слухняний: дзвінки — без лагів, SSH — миттєвий, веб — рівний. Це й є той акуратний QoS, якого так бракувало!