VPNSmith
self-host-vpnINFO

VPN de túnel dividido com tabelas de roteamento (ip-rule + ip-route) 2026

Túnel dividido no Linux via fwmark + regra ip: roteie apps através da VPN vs ISP direto. Contorno do Netflix + casos de VPN de trabalho. Scripts de configuração e melhores práticas.

Por Eric Gerard · Fondateur · VPNSmith — Spécialiste self-host VPN & VPS GDPR9 min de leituraPhoto via Unsplash

Você quer uma VPN para trabalho (acesso à LAN corporativa via túnel) e ao mesmo tempo que a Netflix veja o seu IP francês para que sirva o catálogo FR. Ou quer que o Steam faça downloads diretos (1 Gbps ISP) enquanto tudo o resto passa pela VPN. Solução: túnel dividido via tabelas de roteamento do Linux, mais poderoso do que o split de AllowedIPs do WireGuard.

Este guia implementa o padrão fwmark + regra ip + rota ip que roteia aplicativos específicos através da VPN e outros diretamente, sem tocar no túnel WireGuard em si.

Por que o túnel dividido com tabelas de roteamento do Linux supera o modo AllowedIPs

A opção AllowedIPs do WireGuard é elegante quando você quer excluir intervalos inteiros de IP do túnel — tipicamente sua LAN local (192.168.1.0/24) para manter o acesso ao NAS e impressora sem passar pela VPN. Isso é suficiente para 80% do uso doméstico. Mas assim que você quer diferenciar o roteamento por aplicativo ou por usuário do sistema, o AllowedIPs não consegue. Steam e Netflix não têm IPs fixos — eles usam CDNs com intervalos dinâmicos distribuídos globalmente. Manter manualmente a lista de IPs do Steam no AllowedIPs é impraticável e quebra a cada atualização do CDN da Akamai ou Fastly.

O roteamento fwmark resolve isso marcando pacotes à medida que saem do processo do usuário (via cgroup ou uid), antes que a decisão de roteamento IP seja feita. A tabela de roteamento 200 (VPN) ou principal (ISP direto) é escolhida com base na marca, independente do destino. Assim, você pode dizer "todo o tráfego do usuário steam sai via ISP direto, tudo o resto passa pelo wg0" sem nunca listar um IP. É o que a Mullvad faz internamente no seu aplicativo Linux desde 2022, e o que o Tor usa para o modo bridge.

Outra vantagem: o fwmark sobrevive a mudanças de rede. Se você mudar do WiFi de casa para o 4G móvel sem se desconectar, o seu túnel dividido permanece operacional porque a tabela de roteamento 200 aponta para wg0 (a interface WireGuard, independente da interface física subjacente). Com AllowedIPs você não teria problema também, mas com um kill switch clássico de netfilter pode acabar com regras iptables referenciando eth0 que não existe no 4G móvel (a interface torna-se wwan0).

Conceito em 4 passos

  1. Marcar tráfego de um aplicativo com iptables -j MARK (com base em cgroup, usuário ou destino).
  2. Criar uma tabela de roteamento alternativa para o tráfego marcado (por exemplo, tabela 200).
  3. Adicionar uma regra ip dizendo "tráfego com marca X → usar tabela 200".
  4. Preencher a tabela 200 com uma rota padrão diferente (ou wg0 ou direto eth0).

Resultado: o kernel inspeciona cada pacote de saída e escolhe a tabela 200 (VPN) ou a tabela principal (ISP direto).

Configuração básica

Pré-requisito: um túnel WireGuard configurado e ativo. Consulte o guia de configuração do WireGuard da Contabo se estiver começando do zero.

Primeiro, verifique se o iproute2 está instalado (deve estar em qualquer distro recente):

ip -V
# iproute2-6.x.x

Caso A — Excluir Steam da VPN (downloads diretos do ISP)

O Steam identifica-se via um cgroup quando lançado a partir do systemd. Roteamos o seu tráfego via a interface ISP eth0 em vez de wg0.

Passo 1 — Criar um cgroup para o Steam

sudo mkdir -p /sys/fs/cgroup/net_cls/steam
sudo bash -c 'echo 0x42 > /sys/fs/cgroup/net_cls/steam/net_cls.classid'

Passo 2 — Marcar pacotes de saída desse cgroup

sudo iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x42 -j MARK --set-mark 200

Passo 3 — Criar tabela de roteamento 200 com rota ISP

echo "200 isp_direct" | sudo tee -a /etc/iproute2/rt_tables

ISP_GW=$(ip route | awk '/default/ && /eth0/ {print $3}')
sudo ip route add default via $ISP_GW dev eth0 table isp_direct

sudo ip rule add fwmark 200 table isp_direct

Passo 4 — Lançar o Steam nesse cgroup

cgexec -g net_cls:steam /usr/games/steam

Ou via .slice do systemd para configurações Flatpak / Steam Snap.

O Steam agora faz downloads diretos do ISP (até 1 Gbps de fibra) enquanto o resto da sua máquina permanece no túnel WireGuard.

Caso B — Roteie a Netflix via ISP para o catálogo FR

Você quer que a Netflix veja o seu IP francês. Abordagem por IP de destino, já que a Netflix usa múltiplos CDNs.

Passo 1 — Obter intervalos BGP da Netflix

A Netflix publica os seus prefixos: AS2906. Pode obtê-los com whois -h whois.radb.net -- '-i origin AS2906' ou usar uma lista mantida:

curl -s https://api.bgpview.io/asn/2906/prefixes | jq -r '.data.ipv4_prefixes[].prefix' > /tmp/netflix-ranges.txt

Passo 2 — Marcar tráfego para esses intervalos

while read range; do
  sudo iptables -t mangle -A OUTPUT -d "$range" -j MARK --set-mark 200
done < /tmp/netflix-ranges.txt

Passo 3 — Usar a mesma tabela isp_direct (já configurada no caso A).

Próximo netflix.com, DNS resolve, pacote TCP sai com mark 200, kernel envia via direto eth0, Netflix vê o seu IP francês e serve o catálogo correto.

Caso C — Inverso: forçar apenas Slack/Zoom através da VPN

Código fonte num editor de terminal
Código fonte num editor de terminal

Você trabalha do estrangeiro, quer o tráfego de trabalho (Slack, Zoom, GitHub) através da VPN corporativa, tudo o resto direto.

Configuração simétrica:

  • Marcar padrão = direto
  • Marcar aplicativos de trabalho = via VPN
echo "201 work_vpn" | sudo tee -a /etc/iproute2/rt_tables
sudo ip route add default dev wg0 table work_vpn
sudo ip rule add fwmark 201 table work_vpn

# Marcador para Slack (por destino)
for d in $(dig +short slack.com slack-edge.com app.slack.com); do
  sudo iptables -t mangle -A OUTPUT -d "$d" -j MARK --set-mark 201
done

# Zoom (intervalos que o Zoom publica nos seus documentos de administração)
sudo iptables -t mangle -A OUTPUT -d 3.7.35.0/25 -j MARK --set-mark 201
sudo iptables -t mangle -A OUTPUT -d 3.21.137.128/25 -j MARK --set-mark 201
# ... complete com a lista oficial do Zoom

Importante: a rota padrão da tabela principal deve apontar para eth0 (ISP direto), não wg0. Verifique com ip route show table main.

Prático: script completo split-tunnel.sh

Para evitar reescrever a cada inicialização, aqui está um script idempotente ligado ao systemd:

#!/usr/bin/env bash
# /usr/local/bin/split-tunnel.sh
set -euo pipefail

ISP_IF="eth0"
VPN_IF="wg0"

ISP_GW=$(ip route | awk -v iface="$ISP_IF" '/default/ && $0 ~ iface {print $3; exit}')

grep -q '^200 isp_direct' /etc/iproute2/rt_tables || echo "200 isp_direct" >> /etc/iproute2/rt_tables
grep -q '^201 work_vpn'   /etc/iproute2/rt_tables || echo "201 work_vpn" >> /etc/iproute2/rt_tables

ip route show table isp_direct | grep -q default || ip route add default via "$ISP_GW" dev "$ISP_IF" table isp_direct
ip route show table work_vpn   | grep -q default || ip route add default dev "$VPN_IF" table work_vpn

ip rule show | grep -q "fwmark 0xc8 lookup isp_direct" || ip rule add fwmark 200 table isp_direct
ip rule show | grep -q "fwmark 0xc9 lookup work_vpn"   || ip rule add fwmark 201 table work_vpn

mkdir -p /sys/fs/cgroup/net_cls/steam
echo 0x42 > /sys/fs/cgroup/net_cls/steam/net_cls.classid
iptables -t mangle -C OUTPUT -m cgroup --cgroup 0x42 -j MARK --set-mark 200 2>/dev/null \
  || iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x42 -j MARK --set-mark 200

echo "Regras de túnel dividido aplicadas."

unidade systemd:

# /etc/systemd/system/split-tunnel.service
[Unit]
Description=Regras de roteamento de túnel dividido
After=wg-quick@wg0.service
Wants=wg-quick@wg0.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/split-tunnel.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now split-tunnel.service

Caso D — Dividir por usuário Linux

Você quer que todo o tráfego do usuário kid (a conta do seu filho) passe pela VPN com filtragem parental, e o seu principal eric direto.

sudo iptables -t mangle -A OUTPUT -m owner --uid-owner kid -j MARK --set-mark 201
# Marca 201 roteada via VPN (configurada acima)

Cada curl, firefox, apt lançado sob UID kid sai via wg0. Útil também para um navegador de teste (Chromium em outro usuário) que sempre mostra um IP VPN diferente.

Diagnóstico e depuração

Mostrar regras ativas

ip rule show
# Deve mostrar sua regra fwmark 200/201 com a prioridade correta
ip route show table isp_direct
# Deve mostrar padrão via X dev eth0
ip route show table work_vpn
# Deve mostrar padrão dev wg0

Rastrear o roteamento de um pacote

# Do shell do aplicativo para depurar (ou envolver em cgexec)
ip route get 1.1.1.1 mark 200
# Deve mostrar "via 192.168.1.1 dev eth0 table isp_direct"

ip route get 1.1.1.1 mark 201
# Deve mostrar "dev wg0 table work_vpn"

Ver a marca no tcpdump

sudo tcpdump -i any -nn -e 'iptables' 2>/dev/null
# Ou mais simples:
sudo nft monitor trace

Marca não aplicada?

Verifique a ordem das cadeias iptables. A cadeia OUTPUT da tabela mangle é executada antes do roteamento. Se a sua regra aparecer em iptables -t mangle -L OUTPUT -v -n mas a marca não ficar, geralmente é um cgroup não herdado (lançar via cgexec explícito).

Limites e armadilhas

IPv6

Todos os comandos acima são apenas para IPv4. Para IPv6, espelhe com:

sudo ip6tables -t mangle -A OUTPUT ...
sudo ip -6 rule add fwmark 200 table isp_direct
sudo ip -6 route add default via FE80::1 dev eth0 table isp_direct

Se também usar o nosso guia de kill switch, lembre-se de que as regras DROP de OUTPUT do kill switch podem bloquear o tráfego direto do túnel dividido. Solução: permitir as marcas 200/201 da regra global DROP.

Vazamento de DNS no túnel dividido

Se dividir por IP de destino, o DNS ainda é resolvido pelo seu DNS principal. Se esse DNS estiver no túnel, a Netflix pode resolver para um IP do Reino Unido quando você queria FR. Solução: configure um DNS direto para domínios divididos (dnsmasq com resolvers específicos para servidor).

Kernel armazena em cache as rotas

Após modificar uma ip rule, execute:

sudo ip route flush cache

Caso contrário, conexões já abertas mantêm o roteamento antigo.

Compatibilidade com Docker / containers

O Docker manipula iptables agressivamente. Se você coabitar túnel dividido + Docker, espere que as regras sejam sobrescritas ao reiniciar o Docker. Solução: lance split-tunnel.service com After=docker.service ou use nftables em vez de iptables.

Veredicto

O túnel dividido via fwmark + regra ip é a solução mais flexível no Linux. Você pode rotear por destino, usuário, cgroup, processo. Custo: ~10 linhas de configuração e um serviço systemd. Muito superior ao split de AllowedIPs do WireGuard, que apenas filtra por IP de destino.

Configuração recomendada para casa:

  • VPN padrão para tudo
  • Steam, Netflix, Time Machine → marcado 200 → ISP direto
  • Kill switch de netfilter com exceção para marca 200

Configuração recomendada para trabalho remoto:

  • ISP direto padrão
  • Slack, Zoom, GitHub Enterprise, jumphost → marcado 201 → via VPN corporativa

E para a base: guia do WireGuard na Contabo, modelos prontos para colar, e monitorização com Prometheus + Grafana para acompanhar o que sai por qual interface em tempo real.

Antes de construir o túnel dividido, use o nosso gerador de configuração do WireGuard para obter o wg0.conf base correto — ele define AllowedIPs e PostUp corretamente desde o início para que você possa focar na camada de tabela de roteamento. E se estiver a decidir entre auto-hospedagem e uma VPN comercial por razões de orçamento, o nosso calculador de custos de VPN auto-hospedada faz as contas de 3 anos para você.

★ Datacenter GDPR em Nuremberg · ✓ IPv4 dedicado incluído · 200+ Mbps garantidos

Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region