Skip to content


Разворачивание трафика на основе policy routing

Предстала когда-то задача - выпустить из локальной сети трафик одного девайса (смартфона) так, чтобы в логах сайтов, куда будет сей девайс ходить, светился IP-адрес некоторого заморского сервера, расположенного в Австралии. Смартфон этот никаких средств организации VPN-ов не имел и даже был неспособен работать через прокси, в интернет ходил через Wi-Fi. Локальная сеть с Wi-Fi и смартфоном расположена в Киеве, в Интернет ходит через NAT.
схема сети

  1. На австралийском сервере c именем remote.server.au строим VPN-сервер, например pptpd
  2. На офисном роутере R настраиваем VPN-клиент, например pptp, подключаемся к VPN-серверу, например, так:
    pppd pty 'pptp remote.server.au --nolaunchpppd' call sidney debug dump logfd 2 nodetach

    имя сеанса sidney (после call) должно соответствовать прописанному в файле /etc/ppp/chap-secrets, nodetach --- необязательно, я его использовал для отслеживания процесса.

  3. Маршрутизация работает на основе IP-адреса назначения (destination IP-address), то есть для решения через какой интерфейс отправлять пакет принимается только этот адрес. А в нашей ситуации требуется обратное --- маршрутизировать через Сидней трафик одного только смартфона на основе его IP-адреса, а всех остальных клиентов локальной сети не трогать. Этого можно достичь благодаря одному замечательному умению linux, которое называется policy routing. Создаём еще одну таблицу с произвольным именем (я выбрал avztable) для правил маршрутизации на основе IP-адреса отправителя. Эта новая таблица будет жить параллельно и независимо от основной таблицы маршрутизации с названием main. Для этого в /etc/iproute2/rt_tables, добавляем строчку
    252 avztable
  4. Создаём правило для маршрутизации по source-адресу:
    ip rule add from 192.168.1.55 lookup avztable
  5. Разворачиваем трафик смартфона в туннель путём добавления маршрута по-умолчанию в таблицу avztable:
    ip r a default via 192.168.0.1 table avztable

    Здесь 192.168.0.1 --- адрес VPN-сервера, который становится доступным после поднятия туннеля, задаётся в /etc/pptpd.conf в Сиднее.

  6. В Сиднее добавляем правило для правильной маршрутизации входящего к смартфону трафика. Я для этого наваял такой простенький скрипт и засунул его в cron:
    net='192.168.1.0/24'
    /sbin/ip r s | grep "$net" > /dev/null || {
      gw=`/sbin/ip a s | grep -E "ppp[0-9]" | grep inet | sed -r "s/.*peer[[:space:]]([^/]+)\/.*/\1/"`
        if [ ! -z $gw ]; then
          echo "setting route to net $net via $gw"
          /sbin/ip r a $net via $gw
        fi
    }
    

    Во 2-ой строке проверяем наличие маршрута в таблице маршрутизации, если правила нет, идём дальше. В 3-ей строчке вычисляем IP-адрес VPN-клиента (офисного роутера), который подключился из Киева. В 6-ой строчке собственно добавляем маршрут. После чего австралийский сервер будет знать, что трафик с адресами назначения из сети 192.168.1.0/24 (куда входит наш смартфон), нужно слать в туннель, а не своему default gateway. Правильнее было бы, конечно, вызывать этот скрипт не из cron, а один раз сразу после установки туннеля. Но мне было лениво искать как это сделать автоматически.

  7. В Сиднее проверяем firewall, должно быть что-то такое:
    IPT=/sbin/iptables
    $IPT -A INPUT -p 47 -j ACCEPT
    $IPT -A INPUT -p tcp --dport 1723 -j ACCEPT
    $IPT -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
    $IPT -A FORWARD -s 192.168.1.55 -m comment --comment "phone" -j ACCEPT
    $IPT -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
    

    2-ая и 3-яя строчки нужны для того, чтобы можно было подключиться к VPN-серверу. В 4-ой и 5-ой разрешаем транзитный трафик от и к смартфону. В 6-ой включаем NAT, так чтобы серый адрес смартфона транслировался в в честный адрес австралийского сервера (eth0 - интерфейс в сторону Интернет).

  8. Включаем форфардинг между интерфейсами на сервере в Сиднее:
    sysctl -w net.ipv4.ip_forward = 1

Идём броузером со смартфона для проверки на какой-нибудь looking-glass и проверяём, что он нам показывает IP-адрес австралийского сервера.

Posted in *nix, Howto.


2 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Петровский says

    Изящное решение. Взял на заметку. Нашим тестерам часто такое бывает надо...

  2. Гога says

    Реально прикольно. Мне тоже такое надо было сделать недавно, да я не придумал как. Теперь знаю, спасибо!

You must be logged in to post a comment.