Pull to refresh

Кошерная организация туннелей в OpenWRT

Reading time6 min
Views35K
Итак нам требуется организовать туннель между двумя linux-хостами, одним из которых является наш soho-роутер с этой замечательной прошивкой на борту. Сразу оговоримся, что для нас самое важное — скорость соединения и уменьшение накладных расходов. Посему, о установке пакетов которые реализуют аутентификацию, шифровку трафика, но занимают большое количество заветных килобайт в памяти роутера, мы забудем.Рассматривать мы будем только ipv4-туннели. Стандартно ядром поддерживаются:
  • ipip — инкапсулирует только unicast'овый IPv4-трафик
  • gre — IPv4/IPv6 unicast/multicast трафик, одним словом — красота!
Выбор за вами, помимо некоторого естественного overhead'а в виде снижения скорости и увеличения нагрузки на систему, аргументов в пользу того или иного может служить ваш провайдер, он может просто блокировать один из протоколов.Насчёт снижения скорости, вот такая табличка получилась у меня при тестирование iperf'ом с стандартными настройками, значения в мегабитах:
туннель среднее минимум максимум
без туннеля 30.9 29.8 31.9
ipip 24.8 24.4 25.3
gre 24.0 22.3 24.9

Подготовка системы

opkg update #обновляем список доступных пакетов
opkg install ip #вот так незамысловато называется пакет, более известный как iproute2 - "must have"-утилита на любом linux-роутере
opkg install kmod-gre #поддержка gre-протокола(№47, для справки, если вы решите поработать с трафиком средствами iptables)
opkg install kmod-ipip #поддержка ipip-протокла(№4)
В OpenWRT настройки сетевых интерфейсов описываются в файле /etc/config/network. Я предлагаю придерживаться стиля свойственного этой системе, зададим настройки именно там, но вот незадача — нет стандартной возможности описать туннель, с той же лёгкость как это можно сделать для ifup. На помощь нам приходит мануал, в котором подробно описано как определить новый протокол.

Описание интерфейса

Вот так, в итоге, у меня выглядит описание нашего туннеля, в моём случае — gre:

#все строки не начинающиеся с решётки - обязательны
config 'interface' 'gretunnel'
	option 'proto' 'tunnel' #строка, сигнализирующая нашей системе о используемомо протоколе связи
	option 'tunnel_end' '1.2.3.4' #адрес удалённого конца туннеля
	option 'tunnel_start' '5.6.7.8'
        option 'remote_address' '192.168.0.1' #удалённый адрес соединения точка-точка
	option 'local_address' '192.168.0.2'	 
	option 'mtu' '1476' 
	option 'mode' 'gre' #gre/ipip
#option 'gateway' '192.168.0.1' #возможность назначить маршрут по умолчанию на этот интерфейс
Опять-же стандартным способом, в моём случае для ubuntu server'а, поднимаем второй конец туннеля:

auto gretunnel
iface gretunnel inet static
  address 192.168.0.1
  netmask 255.255.255.255 #знатоки, пожалуйста поясните зачем задавать маску, если она однозначно определяется следующей строкой? 
  pointopoint 192.168.0.2
  mtu 1476
  pre-up ip tunnel add gretunnel mode gre remote 5.6.7.8 local 1.2.3.4
  post-down ip tunnel del gretunnel

Определям новый протокол

В гайде описаны четыре процедуры, полностью описывающие жизненный цикл сетевого интерфейса. Хотя их можно дописать в любой из файлов находящихся в каталоге /lib/network/, я предлагаю создать отдельный tunnel.sh, чтобы при возможном апгрейде системы или отдельных пакетов изменения сохранились.
Scan
Протоколы создающие виртуальные интерфейсы(например — PPP, 6in4 или наш IP-туннель) должны реализовать процедуру scan для синтеза атрибута ifname нашей конфигурации интерфейса, необходимого для обработчиков событий ifup/ifdown в системе hotplug. На самом деле, можно напрямую прописать в конфигурации option ifname tunnel_name, но зачем?)
scan_tunnel() {
	config_set "$1" ifname "$1"
}
То-есть просто даём интерфейсу тоже имя, что и названию конфигурации.
Coldplug
По умолчанию, только интерфейсы присутствующие в /proc/net/dev/ поднимаются при загрузке системы. Именно здесь мы создаём наш интерфейс, практически полностью конфигурируя его — не задаём лишь point-o-point адреса, так как они всё равно будет сброшены в процедуре prepare_interface определённой в /lib/network/config.sh.
coldplug_interface_tunnel() {
	local mtu, tunnel_start, tunnel_end, mode, ifname
	config_get mode "$1" mode
	([[ "$mode" == "ipip" ]] && insmod ipip) || insmod ip_gre
	config_get mtu "$1" mtu 
	config_get tunnel_end "$1" tunnel_end
	config_get tunnel_start "$1" tunnel_start
	config_get ifname "$1" ifname
	ip tunnel add "$ifname" mode "$mode" local "$tunnel_start" remote "$tunnel_end"
}
Setup
Здесь интерфейс поднимается и конфигурируется до конца
setup_interface_tunnel() {
	local alocal, aremote, mtu, defroute
	config_get alocal "$2" local_address
	config_get aremote "$2" remote_address
        config_get mtu "$2" mtu
        ip link set dev "$1" up mtu "$mtu"
	ip address add dev "$1" "$alocal" peer "$aremote"
	config_get defroute "$2" gateway
	[[ -n "$defroute" ]] && ip route add default via "$aremote" dev "$1"
	env -i ACTION="ifup" INTERFACE="$2" DEVICE="$1" PROTO=tunnel /sbin/hotplug-call "iface" &
}
В последней строчке запускается механизм, устанавливающий важный атрибут up в булеву истину, что в свой черёд позволяет работать с интерфейсом в штатном фаерволе, а также появляется возможность назначить маршруты через этот интерфейс.
Stop
Опускаем интерфейс
stop_interface_tunnel() {
       local ifname "$1" ifname
       ip link set dev "$ifname" down
}

Работа с firewall'ом

По умолчанию в таблице filter стоит DROP политикой по умолчанию, впрочем как и в любой нормальной linux-системе. Нам надо описать работу с трафиком гуляющего через вышеописанный интерфейс gretunnel. Для этого создаём зону в файле /etc/config/firewall:
config 'zone'
	option 'name' 'gretunnel'
	option 'output' 'ACCEPT'
	option 'forward' 'ACCEPT'
	option 'mtu_fix' '1'
	option 'input' 'ACCEPT'
Чьё имя совпадает с названием интерфейса, не забываем про mtu отличный от стандартных 1500 байт. Через -j MASQUERADE у меня не вышло(может кто-нибудь подскажет почему?), поэтому будем SNAT'ить пакеты, тем-более это быстрее. Для этого описываем соответствующее правило:
config redirect
        option src              lan
        option dest             gretunnel
        option src_dip          192.168.0.2
	option proto		'udp icmp tcp'
        option target           SNAT
У вас может возникнуть очевидный вопрос — «почему бы в опции proto не поставить all?» — к сожалению, ответа у меня нет, с такой опцией не работает, хотя вроде бы и должно :( Наконец разрешим forward'ить трафик из локальной сети на второй конец туннеля.
config 'forwarding'
	option 'src' 'lan'
	option 'dest' 'gretunnel'

Маршрутизация или зачём всё это вообще было надо

Начнём с канонического случая — объединение двух территориально разнесённых хоста/сетей в одну локальную сеть. Предчувствую нападки security-специалистов, мол трафик не защищён, да и пофиг, мне важна лишь простота и скорость, шифрование оставим для другого топика. Прописываем в /etc/config/network что-то наподобие:
config 'route'
	option 'interface' 'gretunnel'
	option 'target' '192.168.2.0'
	option 'netmask' '255.255.255.0'
	option 'gateway' '192.168.0.1'
И на втором конце производим похожие манипуляции, всё — сети/хосты связанны.Теперь рассмотрим случай, который лично меня и подтолкнул перепрошить свой dlink-320. Мой провайдер 1-го числа не отключает абонентов от сети физически — на порту свитча — вместо этого он оставляет полноценный доступ к локальной сети, при этом ограничивая доступ к интернет-ресурсам и некоторым внутренним. Хорошо, поступим следующим образом — будем маршрутизировать весь «легальный трафик» на шлюз провайдера, а всё остальное в наш туннель. Местные старожилы советуют смотреть сети вашего провайдера тут. Приступаем, в файле /etc/config/network комментируем опцию указывающую на маршрут по умолчанию назначенный шлюзу провайдера:
config 'interface' 'wan'
	option 'ifname' 'eth0.1'
	option 'proto' 'static'
	option 'ipaddr' '5.6.7.8'
	option 'netmask' '255.255.255.252'
#	option 'gateway' '5.6.7.7'
	option 'defaultroute' '0'
	option 'peerdns' '0'
	option 'dns' '8.8.4.4' 
и раскомментируем соответствующую опцию в описание нашего туннеля
option 'gateway' '192.168.0.1'
Не забывая про «легальный» трафик
config 'route'
	option 'interface' 'wan'
	option 'target' '5.6.7.6'
	option 'netmask' '255.255.255.252'
	option 'gateway' '5.6.7.7'
#ну и например 
config 'route'
	option 'interface' 'wan'
	option 'target' '10.0.0.0'
	option 'netmask' '255.0.0.0'
	option 'gateway' '5.6.7.7'
Всё, теперь у нас халявный интернет :)На самом деле, «халява» предоставленная моим провайдером не бесконечная, увы и ах. Через 45-ь дней порт таки блокируется, но благодаря тому что они клиентоориентированны по самое не балуй, они ещё и перерасчёт делают — я могу заплатить лишь за полмесяца пользования их услугами. Вот так и имею, «халявные» 8-ь мегабит)После прочтения данной статьи, у вас опять же может возникнуть вопрос — «а зачем так городить, если тоже самое можно было сделать в 10-ь строчек вынесенные в какой-нибудь initscript?» — к счастью, на этот вопрос у меня ответ имеется, хотя возможно и спорный — я считаю, что с системой надо работать в рамках идеологии принятой там, стараясь обойтись без хаков, кашерно так сказать)Ах да, есть же ещё «халявное» телевидение, но это тема для другого топика.
Tags:
Hubs:
+6
Comments6

Articles

Change theme settings