2026-09-16
Как пометить DSCP трафик конкретного приложения в Ubuntu и отправить его в VPN через mangle на Mikrotik

Как пометить DSCP трафик конкретного приложения в Ubuntu и отправить его в VPN через mangle на Mikrotik

В прошлой статье мы разбирали, как пометить трафик конкретного exe в Windows. Аналогичная задача на Ubuntu решается примерно так же, но вместо QoS Policy используется iptables (или nftables). Цель та же — чтобы трафик конкретного приложения уходил не в обычный интернет, а в VPN-туннель, причём без перевода всего компьютера в VPN.

Схема из двух шагов:

  1. Ubuntu: пометить пакеты нужного приложения меткой DSCP (в поле TOS/IP).
  2. Mikrotik: по DSCP-метке через mangle пометить соединение и отправить пакеты в VPN-туннель.

DSCP — 6-битное поле в IP-заголовке. Ubuntu умеет его выставлять через iptables -j DSCP, Mikrotik умеет по нему маршрутизировать. Дальше — полная рабочая схема.


Часть 1. Ubuntu: ставим DSCP-метку на конкретное приложение

Способ A. По пользователю (точный, но требует выделенного юзера)

Самый надёжный способ — запускать приложение от отдельного системного пользователя и помечать весь его исходящий трафик:

# Создаём пользователя для приложения (если ещё нет)
sudo useradd -r -m vpnapp
# Запускаем приложение от этого пользователя
sudo -u vpnapp /opt/myapp/myapp --daemon

# Правило iptables: метка DSCP 46 на весь трафик пользователя vpnapp
sudo iptables -t mangle -A OUTPUT -m owner --uid-owner vpnapp -j DSCP --set-dscp 46

Что происходит:
-t mangle — таблица для изменения полей пакета;
OUTPUT — цепочка для исходящего с этой машины трафика;
-m owner --uid-owner vpnapp — матч по «владельцу» сокета (UID запустившего процесса);
-j DSCP --set-dscp 46 — перезапись поля DSCP на значение 46 (EF).

Проверка, что правило встало:

sudo iptables -t mangle -L OUTPUT -nv

В выводе должен появиться ненулевой счётчик пакетов у правила по мере работы приложения.

Способ B. По порту (если приложение работает под общим пользователем)

Иногда приложению нельзя выделить отдельного юзера. Тогда метим по порту назначения:

# Например, 1С-сервер или свой клиент на TCP 1541
sudo iptables -t mangle -A OUTPUT -p tcp --dport 1541 -j DSCP --set-dscp 46

Нюанс: метка ставится каждому пакету, уходящему на этот порт, независимо от приложения. Если порт используется несколькими процессами — DSCP получится у всех.

Способ C. Через nftables (современный стандарт Ubuntu)

Начиная с Ubuntu 22.04 предпочтителен nftables. То же правило на nftables:

sudo nft add table inet mangle
sudo nft add chain inet mangle output '{ type filter hook output priority 0; }'
sudo nft add rule inet mangle output meta skuid vpnapp ip dscp set 0x2e
  • meta skuid vpnapp — то же самое, что --uid-owner;
  • ip dscp set 0x2e — установить DSCP 46 (0x2e = 46 в hex);
  • type filter hook output priority 0 — цепочка для исходящих пакетов.

Проверка nftables:

sudo nft list table inet mangle

Способ D. Через iproute2 / cgroup (для новейших ядер)

Если нужно метить не по UID, а по группе cgroup (например, для контейнера systemd unit), iptables поддерживает match --cgroup:

sudo iptables -t mangle -A OUTPUT -m cgroup --path /system.slice/myapp.service -j DSCP --set-dscp 46

Сохранение правил после перезагрузки

iptables-правила не сохраняются сами по себе. Ставим пакет iptables-persistent:

sudo apt install -y iptables-persistent
# Новая запись каждого правила после изменения:
sudo netfilter-persistent save

Для nftables:

sudo nft list ruleset > /tmp/ruleset.nft
sudo cp /tmp/ruleset.nft /etc/nftables.conf
sudo systemctl enable --now nftables

Проверка метки на Ubuntu

Самое наглядное — подглядеть за трафиком через tcpdump прямо на интерфейсе или на роутере:

# Смотрим DSCP у пакетов приложения
sudo tcpdump -i any 'ip dscp 46' -n
# Запустите приложение — появятся строки с этим фильтром

На Mikrotik можно посмотреть счётчики mangle-правил, но пока не было правил — лучше убедиться на Ubuntu, что пакеты реально уходят с меткой.


Часть 2. Mikrotik: mangle — в VPN туннель по DSCP

Роутер читает DSCP у пакетов с LAN-интерфейса, помечает их и заворачивает в туннель. Правила абсолютно те же, что для Windows-статьи.

Шаг 1. Помечаем соединение и пакет по DSCP

/ip firewall mangle
add chain=prerouting dst-address-list=!no_vpn in-interface=bridge-local 
    dscp=46 action=mark-connection new-connection-mark=vpn-conn 
    pass-through=yes comment="DSCP46 -> connection"
add chain=prerouting connection-mark=vpn-conn in-interface=bridge-local 
    action=mark-packet new-packet-mark=vpn-traffic 
    pass-through=no comment="marked conn -> packet"

Где:
in-interface=bridge-local — LAN-интерфейс (у вас может быть ether1-local и т.п.);
dscp=46 — та самая метка, которую ставит Ubuntu:
dst-address-list=!no_vpn — исключения (список no_vpn создайте заранее).

Шаг 2. Маршрутизация marked-пакетов в туннель

Делаем настройку через routing-rule + отдельную таблицу маршрутов:

/ip route
add dst-address=0.0.0.0/0 gateway=<vpn_interface> routing-table=vpn-table 
    distance=1 comment="VPN default"

/routing/rule
add src-address=0.0.0.0/0 routing-table=main table=vpn-table 
    packet-mark=vpn-traffic comment="VPN traffic -> vpn-table"
  • В vpn-table лежит один default — через интерфейс туннеля (wireguard1, l2tp-out1, ovpn-out1 и т.д.);
  • routing/rule перекладывает только пакеты с packet-mark=vpn-traffic из main-таблицы в vpn-table;
  • В main-таблицу default через туннель добавлять нельзя — иначе весь трафик уйдёт в VPN.

Шаг 3. NAT

Если приложение ходит во внешние сети через туннель:

/ip firewall nat
add chain=srcnat packet-mark=vpn-traffic out-interface=<vpn_interface> 
    action=masquerade comment="NAT VPN traffic"

Если туннель ведёт в вашу удалённую сеть (например 10.0.0.0/8) — masquerade не нужен.


Готовая схема «под ключ»

Пакет Где решается Результат
От myapp (UID vpnapp) iptables OUTPUT mangle DSCP=46
Приходит в LAN на Mikrotik mangle prerouting connection-mark=vpn-conn
Тот же пакет mangle prerouting (по conn) packet-mark=vpn-traffic
Маршрутизация routing/rule main → vpn-table
Исход route vpn-table gateway=<туннель>
В интернет NAT srcnat masquerade

Полный набор правил

# Mangle
/ip firewall mangle
add chain=prerouting in-interface=bridge-local dscp=46 
    action=mark-connection new-connection-mark=vpn-conn pass-through=yes 
    comment="DSCP 46 -> connection mark"
add chain=prerouting in-interface=bridge-local connection-mark=vpn-conn 
    action=mark-packet new-packet-mark=vpn-traffic pass-through=no 
    comment="conn mark -> packet mark"

# Routing table и правило
/ip route
add dst-address=0.0.0.0/0 gateway=wireguard1 routing-table=vpn-table 
    comment="Default via VPN"

/routing/rule
add packet-mark=vpn-traffic routing-table=main table=vpn-table 
    comment="Marked traffic uses vpn-table"

# NAT
/ip firewall nat
add chain=srcnat packet-mark=vpn-traffic out-interface=wireguard1 
    action=masquerade comment="Masq for VPN traffic"

Замените wireguard1 на ваш туннель (WireGuard, OpenVPN, L2TP, PPPoE) — схема не зависит от типа туннеля.


Выбор DSCP-значения

Значение Класс Для чего
46 EF (Expedited Forwarding) Видео, голос
34 AF41 Требовательно к задержкам
26 AF31 Обычный приоритет
0 Best Effort Весь остальной трафик

Для бизнес-приложений обычно хватает 46 (если в сети нет VoIP, которому нужно большее) или 26/34. В iptables значение задаётся десятичным (--set-dscp 46) либо классом (--set-dscp EF). В nftables — hex (0x2e).


Проверка результата

  1. На Ubuntu: sudo tcpdump -i any 'ip dscp 46' -n — при работе приложения видны помеченные пакеты.
  2. На Mikrotik: /tool sniffer quick interface=bridge-local + фильтр по dscp 46, либо счётчики mangle-правил (/ip firewall mangle print stats).
  3. Маршрут: на Ubuntu ip route get <IP_VPN_ресурса> — трафик уходит через шлюз (обычный), а сам пакет заворачивает Mikrotik. На удалённом сервере проверьте, что в туннель приходят пакеты с DSCP 46: tcpdump -i tun0 ip dscp 46.

Тонкости и подводные камни

  • Правило в mangle OUTPUT видит пакеты только локально исходящие. Если приложение работает на другом хосте в сети — правило нужно вешать на роутер или на тот хост.
  • owner-match работает по реальному UID. Если приложение вешается и становится сокетом от другого пользователя/root, правило перестанет попадать. Для демонов / systemd-юнитов выделяйте системного юзера.
  • DSCP теряется при шифровании. Если туннель терминируется на самой Ubuntu (IPsec/WireGuard на клиенте), метка внутри шифрованного пакета не видна роутеру — поэтому туннель должен терминировать Mikrotik, а Ubuntu оставляем «голой».
  • FastTrack на Mikrotik «проезжает» мимо mangle. Отключите fasttrack для этого трафика:
    routeros
    /ip firewall raw
    add chain=prerouting in-interface=bridge-local dscp=46 action=notrack
    comment="no fasttrack for dscp46"
  • Текущие правила не переживают перезагрузку Ubuntu — используйте iptables-persistent (или nftables) из раздела выше.
  • На Wi-Fi-клиентах AP и часть роутеров могут переписывать DSCP. Если цель — офисная сеть по проводу, проблем обычно нет.

Вывод

На Ubuntu задача решается через iptables/nftables с match по --uid-owner (или по порту), далее на Mikrotik ровно та же связка mangle + routing-rule в vpn-таблицу, что и в Windows-варианте. Три ключевые точки те же: DSCP на пакете → connection/packet mark в mangle → routing rule в vpn-таблицу.

Проверено на: Ubuntu 24.04/26.04 + RouterOS 7.
Дата: Сентябрь 2026.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *