Pull to refresh

Создание point-to-multipoint тоннелей на базе инкапсуляции GRE в Linux 2.6

Reading time 4 min
Views 16K
В ОС Linux встроена поддержка двух типов тоннелей: ipip и gre. В том виде, в котором тоннели традиционно используются в системе, без разницы, какой из них использовать: они оба дают в точности одинаковые накладные расходы к пакетам, посылаемым в тоннель IPv4-in-IPv4 (специально проверял), одинаково защищаются средствами IPsec и занимают одинаковое процессорное время для обработки. Однако, это разные типы тоннелей, и возможности gre значительно более широкие.
К сожалению, нигде в интернете не описывается очень удобная и замечательная особенность gre-тоннелей, и большинство (если не все) администраторов Linux не знают о такой возможности, как mGRE-тоннели. К счастью, мы намереваемся восполнить этот недостаток :-)



Итак, имеем три машины, все три имеют запущенный Linux версии 2.6 (я не уверен, возможно, и 2.4 тоже поддерживает). Также нам потребуется пакет iproute2 — это стандарт для современных Linux-систем (кстати, давно уже пора забыть про морально устаревшие утилиты ifconfig, route и прочие). Внешние ip-адреса систем: 1.1.1.1, 2.2.2.2 и 3.3.3.3; между ними есть маршрутизация.

Обойдёмся пока без шифрования и аутеникации пакетов — это всё несложно к нашему примеру добавить, «просто» настроив IPsec между нашими хостами в транспортном режиме. Это тема за рамками темы ;)

1. Создаём GRE-тоннель:
(на всех трёх)
ip tunnel add mgre0 mode gre key 0xfffffffe ttl 255

Заметьте, мы не указали здесь адрес пира. Это значит, что в принципе он может быть расположен по любому адресу. key в данном случае идентифицирует конкретную mGRE-сеть — это 32-битное число, одинаковое для всех нод.

2. Назначаем ему адрес:
(на 1.1.1.1)
ip addr add 10.0.0.1/24 dev mgre0

(на 2.2.2.2)
ip addr add 10.0.0.2/24 dev mgre0

(на 3.3.3.3)
ip addr add 10.0.0.3/24 dev mgre0


Для интерфейсов Ethernet этого было бы достаточно. В Ethernet есть Adress Resolution Protocol (ARP), который позволяет системам самостоятельно найти MAC-адрес, зная IP-адрес хоста назначения. Ethernet является средой Broadcast Multiple Access, и протокол ARP заключается в создании запроса ко всем станциям сети (по MAC-адресу FF:FF:FF:FF:FF:FF): «Эй, кто из вас имеет IP-адрес x.x.x.x?». Если станция с таким IP-адресом имеется, она уже в приватном порядке сообщает, что «x.x.x.x расположен по адресу yy:yy:yy:yy:yy:yy».

В нашей сети (Internet) нет такого средства, как ARP, а роль адресов «второго уровня», которые в случае Ethernet — MAC-адреса, здесь исполняют… внешние IP-адреса систем. Мы работаем со средой Non-Broadcast Multiple Access (NBMA), мы не можем крикнуть на весь интернет, как сделал бы ARP: «Эй, кто в GRE-сети 0xfffffffe имеет адрес 10.0.0.2?».

Для разрешения этой проблемы адресов предназначен протокол Next Hop Resolution Protocol (NHRP, аналог ARP для NBMA-сред), но мы на на первый раз сделаем работу за него — заодно разберёмся, как вообще сеть в Linux работает :)

3. Итак, сообщим вручную каждой станции, где искать соседей. Для этого выполним следующие команды:
(на 1.1.1.1)
ip neigh add 10.0.0.2 lladdr 2.2.2.2 dev mgre0
ip neigh add 10.0.0.3 lladdr 3.3.3.3 dev mgre0

(на 2.2.2.2)
ip neigh add 10.0.0.1 lladdr 1.1.1.1 dev mgre0
ip neigh add 10.0.0.3 lladdr 3.3.3.3 dev mgre0

(на 3.3.3.3)
ip neigh add 10.0.0.1 lladdr 1.1.1.1 dev mgre0
ip neigh add 10.0.0.2 lladdr 2.2.2.2 dev mgre0


Здесь каждая команда говорит: «соседняя станция с адресом сетевого уровня IP x.x.x.x имеет физический адрес (link layer address, lladdr) y.y.y.y, который доступен через устройство M». Если бы мы настраивали статический Ethernet (без ARP), вместо y.y.y.y стоял бы MAC-адрес соответствующей станции. (Кстати, если посмотреть ip neigh show dev ethN в работающей Ethernet-сети, мы увидим результаты работы ARP — динамически полученные адреса соседей).

Всё. На этом наш тоннель заработает: каждая из станций сможет пинговать любую другую. Если ядро собрано с поддержкой GRE multicast, то мы вообще получаем полнофункциональную «локалку» — в нашей вирутальной сети будут работать в полную силу протоколы динамической маршрутиации, типа RIP и OSPF!

Вот так это выглядит со второй станции (2.2.2.2):
linux2 # ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=4.41 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.429 ms
^C
--- 10.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1013ms
rtt min/avg/max/mdev = 0.429/2.419/4.410/1.991 ms
linux2 # ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.020 ms
^C
--- 10.0.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 999ms
rtt min/avg/max/mdev = 0.020/0.023/0.027/0.006 ms
linux2 # ping 10.0.0.3
PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.
64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=8.47 ms
64 bytes from 10.0.0.3: icmp_seq=2 ttl=64 time=0.164 ms
^C
--- 10.0.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1018ms
rtt min/avg/max/mdev = 0.164/4.318/8.472/4.154 ms
linux2 # ip addr show dev mgre0
5: mgre0@NONE: <UP,LOWER_UP> mtu 1472 qdisc noqueue
    link/gre 0.0.0.0 brd 0.0.0.0
    inet 10.0.0.2/24 brd 10.0.0.255 scope global mgre0
linux2 # ip neigh show dev mgre0
10.0.0.1 lladdr 1.1.1.1 PERMANENT
10.0.0.3 lladdr 3.3.3.3 PERMANENT


Конечно, если станций много, такой подход не годится — не прописывать же на каждой станции всех соседей! Но вполне понятно, как решать этй проблему. Но об этом как-нибудь потом.
Tags:
Hubs:
+15
Comments 6
Comments Comments 6

Articles