Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save koolvn/f99ba845e66e6a2643adbd055d5d034b to your computer and use it in GitHub Desktop.
Save koolvn/f99ba845e66e6a2643adbd055d5d034b to your computer and use it in GitHub Desktop.
Domain VPN Routing via DNSmasq tagging AsusWRT-Merlin

Как настроить роутинг трафика через VPN по доменному имени

Описание

Подход основан на маркировке пакетов. Далее по этим маркерам роутер будет определять какие пакеты слать через VPN клиента, а какие нет

ВАЖНО:

  1. Клиенты роутера должны использовать адрес роутера как DNS (Дефолтное поведение)
  2. У вас уже должен быть настроен и подключен VPN клиент
  3. У вас включен и настроен доступ к роутеру по ssh
  4. Проверьте, что переменная TABLE_ID=300 в скрипте /jffs/scripts/vpnroute не пересекается с номерами из /etc/iproute2/rt_tables. Измените при необходимости
    cat /etc/iproute2/rt_tables
    100 wan0
    1 wgc1
    2 wgc2
    3 wgc3
    4 wgc4
    5 wgc5
    6 ovpnc1
    7 ovpnc2
    8 ovpnc3
    9 ovpnc4
    10 ovpnc5
    200 wan1

Установка

  1. В файл /jffs/configs/dnsmasq.conf.add прописываем домены, которые хотим открывать через VPN. В примере ниже адреса для Youtube и Jetbrains.

    ipset=/youtube.com/vpnroute
    ipset=/youtu.be/vpnroute
    ipset=/ggpht.com/vpnroute
    ipset=/youtubei.googleapis.com/vpnroute
    ipset=/ytimg.com/vpnroute
    ipset=/googlevideo.com/vpnroute
    ipset=/googleusercontent.com/vpnroute
    ipset=/gvt1.com/vpnroute
    ipset=/gvt2.com/vpnroute
    ipset=/jetbrains.com/vpnroute
    ipset=/jetbrains.ai/vpnroute
    ipset=/intellij.net/vpnroute
    

    Здесь vpnroute должна совпадать с переменной IPSET_NAME из скрипта

  2. Записываем скрипт в файл /jffs/scripts/vpnroute (можно назвать как угодно, главное, чтоб лежал в /jffs/scripts)

  3. Делаем файл исполняемым

    chmod +x /jffs/scripts/vpnroute
  4. Запускаем скрипт

    sh /jffs/scripts/vpnroute
  5. Проверяем, что всё успешно применилось

    Выполняем команды:

    ip rule: Ищите строку вроде 99: from all fwmark 0x55 lookup 300. Она должна быть в списке (выше приоритета 100).

    ip route show table 300: Должно показать:

       ```
       default dev wgc1 scope link
       192.168.0.0/16 dev br0 scope link
       ```
    

    ipset list vpnroute: Пустой список или с заголовком (после теста на youtube.com здесь появятся IP).

    iptables -t mangle -L -v -n: Ищите цепочку VPNROUTE с правилом MARK set 0x55, и в PREROUTING ссылку на неё с -i br0 и match-set vpnroute

  6. Перезапускаем сервис dnsmasq

    service restart_dnsmasq
  7. С устройства в вашей LAN откройте youtube.com в браузере или приложении

  8. Ставим скрипт в автозапуск после применения всех настроек NAT. Дописываем в конец файла /jffs/scripts/wgclient-start следующее:

    /jffs/scripts/vpnroute
  9. Делаем скрипт исполняемым chmod +x /jffs/scripts/wgclient-start

  10. Каждый раз, когда хотим добавить новый домен, прописываем его в /jffs/configs/dnsmasq.conf.add и выполняем команду

    service restart_dnsmasq
#!/bin/sh
# === Configuration ===
# This section contains all the variables you might want to change.
# The VPN tunnel interface (e.g., wgc1, wg0, tun0).
readonly VPN_IF="wgc1"
readonly LOGGER_NAME="Domain VPN Routing"
readonly LOG_LEVEL=user.notice
# The routing table ID to use for VPN traffic.
readonly TABLE_ID="300"
# You should leave the following AS IS unless you know what you are doing.
# Your local network subnet.
# This is to ensure local traffic does not go through the VPN.
readonly LAN_SUBNET="192.168.0.0/16"
# The name of the ipset that will contain the destination IPs for the VPN.
readonly IPSET_NAME="vpnroute"
# The maximum number of elements in the ipset.
readonly IPSET_MAXELEM="10000"
# The firewall mark to identify VPN-bound packets.
readonly FWMARK="0x55"
# The priority of the routing rule.
readonly FWMARK_PRIORITY="99"
# The LAN interface.
readonly LAN_IF="br0"
# The name for the custom iptables chain.
readonly IPTABLES_CHAIN="VPNROUTE"
# === Script Execution ===
# Exit immediately if a command exits with a non-zero status.
set -e
logger -sc -t "${LOGGER_NAME}" -p ${LOG_LEVEL} "Starting VPN routing setup..."
# 1. Create the ipset if it doesn't already exist.
# This set will hold the destination IPs that need to be routed through the VPN.
logger -sc -t "${LOGGER_NAME}" -p ${LOG_LEVEL} "Creating ipset '${IPSET_NAME}'..."
ipset create "${IPSET_NAME}" hash:ip maxelem "${IPSET_MAXELEM}" -exist
# 2. Clean up previous routing rules and table.
# This ensures that we start from a clean state every time the script is run.
# Errors are suppressed in case the rules or routes don't exist.
logger -sc -t "${LOGGER_NAME}" -p ${LOG_LEVEL} "Cleaning up old routing rules and routes..."
ip rule del fwmark "${FWMARK}" table "${TABLE_ID}" 2>/dev/null || true
ip route flush table "${TABLE_ID}" 2>/dev/null || true
# 3. Set up iptables for packet marking.
logger -sc -t "${LOGGER_NAME}" -p ${LOG_LEVEL} "Setting up iptables..."
# Create a new custom chain for our VPN logic if it doesn't exist.
iptables -t mangle -N "${IPTABLES_CHAIN}" 2>/dev/null || true
# Flush the custom chain to remove any old rules.
iptables -t mangle -F "${IPTABLES_CHAIN}"
# Delete any old rule in PREROUTING to avoid duplicates.
iptables -t mangle -D PREROUTING -i "${LAN_IF}" -m set --match-set "${IPSET_NAME}" dst -j "${IPTABLES_CHAIN}" 2>/dev/null || true
# Create a rule to jump to our custom chain for traffic from LAN that matches the destination ipset.
iptables -t mangle -A PREROUTING -i "${LAN_IF}" -m set --match-set "${IPSET_NAME}" dst -j "${IPTABLES_CHAIN}"
# In our custom chain, mark the packets so the routing policy can identify them.
iptables -t mangle -A "${IPTABLES_CHAIN}" -j MARK --set-mark "${FWMARK}"
# 4. Configure policy-based routing.
logger -sc -t "${LOGGER_NAME}" -p ${LOG_LEVEL} "Adding new routing rule and routes..."
# Add a rule to direct all packets with our mark to use our custom routing table.
ip rule add fwmark "${FWMARK}" table "${TABLE_ID}" priority "${FWMARK_PRIORITY}"
# 5. Populate the custom routing table.
# Add a default route to this table to send all traffic through the VPN interface.
ip route add default dev "${VPN_IF}" table "${TABLE_ID}"
# Add a route for the local network to go through the regular LAN interface.
# This prevents local traffic from being sent over the VPN.
ip route add "${LAN_SUBNET}" dev "${LAN_IF}" table "${TABLE_ID}"
# 6. Flush the route cache to apply changes immediately.
logger -sc -t "${LOGGER_NAME}" -p ${LOG_LEVEL} "Flushing route cache..."
ip route flush cache
logger -sc -t "${LOGGER_NAME}" -p ${LOG_LEVEL} "VPN routing rules applied successfully."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment