Как пометить DSCP трафик конкретного приложения в Ubuntu и отправить его в VPN через mangle на Mikrotik
В прошлой статье мы разбирали, как пометить трафик конкретного exe в Windows. Аналогичная задача на Ubuntu решается примерно так же, но вместо QoS Policy используется iptables (или nftables). Цель та же — чтобы трафик конкретного приложения уходил не в обычный интернет, а в VPN-туннель, причём без перевода всего компьютера в VPN.
Схема из двух шагов:
- Ubuntu: пометить пакеты нужного приложения меткой DSCP (в поле TOS/IP).
- 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).
Проверка результата
- На Ubuntu:
sudo tcpdump -i any 'ip dscp 46' -n— при работе приложения видны помеченные пакеты. - На Mikrotik:
/tool sniffer quick interface=bridge-local+ фильтр по dscp 46, либо счётчики mangle-правил (/ip firewall mangle print stats). - Маршрут: на 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.