|
||
Ответить |
|
#1
|
|
Вес репутации:
0
Регистрация: 27.02.2009
Адрес: Москва
Сообщений: 7,302
Сказал(а) спасибо: 578
Спасибок 2,623
в 1,832 сообщениях |
FreeBSD Netgraph на примере Ethernet тоннеля -
09.03.2010, 03:01
Думаю многим системным администраторам, работающим с FreeBSD, известно о существовании ядерной подсистемы Netgraph. Но не многие знают/понимают как это работает, и что из этого можно построить.
Расскажу что это такое, а также разберу на простом примере сборку Ethernet моста через интернет. Немного теории. Википедия рассказывает http://ru.wikipedia.org/wiki/Netgraph Netgraph — модульная сетевая подсистема ядра FreeBSD, основанная на принципе графов. В Netgraph строится граф из узлов различных типов, узел каждого типа имеет некоторое количество входов/выходов (далее хуков (hooks)). Узел netgraph позволяет производить определенные действия над пакетом, проходящим через него. Некоторые Netgraph узлы предоставляют поддержку различных протоколов, инкапсуляций, таких как L2TP, PPTP, PPPoE, PPP, ATM, bluetooth, другие служат для связки модулей и сортировки/маршрутизации между узлами netgraph, например bpf, split. Подсистема Netgraph представляет собой набор модулей, каждый со своей специфичной задачей. Это такая горстка кубиков лего, которые всегда можно соединить. Ярким примером использования множества netgraph модулей является массово используемый мелкими Российскими провайдерами демон mpd, для терминации клиентских туннелей, таких как PPPoE, PPTP, PPP и т.п. В версиях 3 ветки mpd многие операции делал сам, а к 5 текущей версии в основном занимается коммутацией модулей netgraph. Посмотрим на наши кубики. [root@bsd1] /boot/kernel/> ls | grep ng_ ng_UI.ko ng_async.ko ng_atm.ko ng_atmllc.ko ng_bluetooth.ko ng_bpf.ko ng_bridge.ko ng_bt3c.ko ng_btsocket.ko ng_car.ko ng_ccatm.ko ng_cisco.ko ng_deflate.ko ng_device.ko ng_echo.ko ng_eiface.ko ng_etf.ko ng_ether.ko ng_fec.ko ng_frame_relay.ko ng_gif.ko ng_gif_demux.ko ng_h4.ko ng_hci.ko ng_hole.ko ng_hub.ko ng_iface.ko ng_ip_input.ko ng_ipfw.ko ng_ksocket.ko ng_l2cap.ko ng_l2tp.ko ng_lmi.ko ng_mppc.ko ng_nat.ko ng_netflow.ko ng_one2many.ko ng_ppp.ko ng_pppoe.ko ng_pptpgre.ko ng_pred1.ko ng_rfc1490.ko ng_socket.ko ng_source.ko ng_split.ko ng_sppp.ko ng_sscfu.ko ng_sscop.ko ng_sync_ar.ko ng_sync_sr.ko ng_tag.ko ng_tcpmss.ko ng_tee.ko ng_tty.ko ng_ubt.ko ng_uni.ko ng_vjc.ko ng_vlan.ko [root@bsd1] /boot/kernel/> ls | grep ng_ | wc -l 58 [root@bsd1] /boot/kernel/> Для каждого модуля существует man, в котором описываются возможности модуля, входы/выходы (hook, крючки, зацепки, далее буду называть их «хуками»), принимаемые контрольные сообщения и их действия. Для управления подсистемой netgraph существует утилита ngctl воспринимающая разные команды, такие как list,mkpeer,connect,name и др., про них отдельно можно прочитать в man ngctl. Задача. Поставим простую задачу: необходимо соединить две физических сети Ethernet через интернет. Имеем 2 сервера. BSD1 2 сетевых интерфейса Интерфейс 1 em0 — внешний IP 1.1.1.1 Интерфейс 2 em1 — внутренний IP 192.168.1.1 BSD2 2 сетевых интерфейса Интерфейс 1 em0 — внешний IP 2.2.2.2 Интерфейс 2 em1 — внутренний IP 192.168.1.2 Между серверами имеется маршрутизируемая интернет связь через внешние интерфейсы. Соединить нужно сети на интерфейсах em1 обоих серверов. Посмотрим на наши кубики. Нам потребуются: netgraph.ko — основа. ng_ether.ko — модуль подключения к физическим сетевым интерфейсам. При загрузке этого модуля в пространстве графов автоматически создаются по одному узлу на каждый физический интерфейс, имеющих имя этого интерфейса. В нашем случае это будут «em0:», «em1:». Каждый узел ng_ether имеет 3 хука: lower, upper, orphans. Хук lower — это прямой выход в место драйвера сетевого интерфейса, откуда пакеты отправляются или принимаются устройством. Хук upper это прямой выход в место ядра системы, откуда пакеты должны отправляться или приниматься драйвером сетевого устройства. Хук orphans, это тот же lower, только в него попадают ошибочные, испорченные пакеты с сетевого устройства. Его во внимание не берем. Если сказать простым языком: при подключении ng_ether в обмене между ядром и драйвером сетевой карты появляется разрыв. Со стороны ядра этот разрыв называется upper, со стороны драйвера сетевого устройства lower. Этот разрыв соединен, до момента когда к lower или upper не подключится что-нибудь из подсистемы netgraph. ng_bridge.ko — модуль самого настоящего Ethernet свитча. Все, думаю, могут представить (а кто-то и сейчас видит), коробочку с Ethernet портами, моргающую лампочками, стоящую на столе и объединяющую компьютеры в сеть. ng_bridge — та самая коробочка в подсистеме netgraph. ng_bridge — простая реализация Ethernet свитча, снабженная arp таблицей и простым алгоритмом определения петель. Хуки ng_bridge именуются link0, link1 и т.д. К ним мы и будем подключать хуки наших ng_ether. Модуль принимает контрольные сообщения, такие как setconfig, getconfig, reset, getstats, clrstats, getclrstats, gettable. Действия сообщений интуитивно понятны, подробности в man ng_bridge. ng_ksocket.ko — модуль, позволяющий открыть на прослушку сокет прямо из ядра (любой сокет, поддерживаемый системной функцией socket()), и подключиться к другому сокету. Принимает единственное подключение к хуку "<family>/<type>/<proto>". В man socket рассказано какие бывают family, type, proto. В нашем случае это будет «inet/dgram/udp». inet — ipv4, dgram — дейтаграммы, proto — udp. Так же модуль принимает несколько контрольных сообщений, такие как bind, listen, connect, accept, getname, getpeername, setopt, getopt. Подробнее можно прочитать в man ng_ksocket. Используя сообщение «connect» мы можем подключить наш входящий хук «inet/dgram/udp» к удаленному, который был создан аналогично, но командой «bind». Составляем граф. Для лучшего понимания работы вашей системы графов, перед сборкой в системе желательно их нарисовать. 1. Подключаем оба хука модуля ng_ether интерфейса em1 к ng_bridge, чтобы система видела и физическую сеть, подключенную к интерфейсу, и нашу виртуальную. Делается одинаково на обоих серверах. 2. Следующий свободный линк ng_bridge подключаем к ng_ksocket с параметрами inet/dgram/udp. (Почему я выбрал UDP? Потому что никто не гарантирует успешную доставку сигнала в кабеле или радио сети, так же как аналогично в IP протоколе никто не гарантирует доставку UDP.) Делается одинаково на обоих серверах. 3. Командуем модулю ng_ksocket занять определенный порт, на определенном IP адресе, а так же подключиться к удаленному IP по нужному порту. Делаем это на обоих серверах, в том отличии, что меняются IP адреса и порты на противоположные друг другу. Собираем граф в системе. Выше я уже упомянул об утилите ngctl. С её помощью мы создадим наши модули в системе и свяжем их. Нам потребуются команды mkpeer, connect, name, msg. Опишу их простым языком. Команда mkpeer. Синтаксис: ngctl mkpeer модуль1 тип_модуля2 хук1 хук2 Создает «модуль2» с указанным типом и подключает его хук «хук2» к «хук1» модуля «модуль1» Команда connect. Синтаксис: ngctl connect модуль1 модуль2 хук1 хук2 Подключает «хук1» модуля «модуль1» к «хук2» модуля «модуль2». Команда name. Синтаксис: ngctl name модуль: хук имя Присваивает имя созданному через mkpeer модулю. Команда msg. Синтаксис: ngctl msg модуль: сообщение параметры Передает контрольное «сообщение» в «модуль» с «параметрами». Ну а теперь как будет выглядеть наш граф в живую для сервера «bsd1». Для тоннеля будем использовать udp порт 7777 Создаем узел ng_bridge и подключаем к его хуку «link0» хук сетевого интерфейса «em1» «lower». ngctl mkpeer em1: bridge lower link0 Называем только что созданный узел именем «switch», его можно найти по пути «em1:lower». ngctl name em1:lower switch Подключаем к «link1» нашего «switch» upper сетевого интерфейса «em1». ngctl connect switch: em1: link1 upper Создаем узел ng_ksocket и подключаем к его хуку «inet/dgram/udp» «link2» нашего «switch» ngctl mkpeer switch: ksocket link2 inet/dgram/udp Называем только что созданный ksocket «switch_socket», его можно найти по пути «switch:link2» ngctl name switch:link2 switch_socket Отправляем команду «bind» нашему «switch_socket», с параметрами. ksocket займет порт 7777 на IP 1.1.1.1. ngctl msg switch_socket: bind inet/1.1.1.1:7777 Отправляем команду «connect» нашему «switch_socket», с параметрами. ksocket подключится к порту 7777 по IP адресу 2.2.2.2. ngctl msg switch_socket: connect inet/2.2.2.2:7777 Отправляем команду модулю ng_ether сетевого интерфейса em1 перейти в режим прослушки пакетов, адресованных не ему. Нам ведь теперь необходимо принимать пакеты для устройств находящихся в нашей виртуальной сети. ngctl msg em1: setpromisc 1 ngctl msg em1: setautosrc 0 Для сервера «bsd2» нам лишь нужно поменять параметры команд bind и connect местами. Для простоты использования всё это я оформил в sh скрипт. В скрипте используется ещё одна команда ngctl shutdown. Эта команда посылает специальное контрольное сообщение модулю, указанному в параметре. Это сообщение принимает каждый модуль, подробнее в «man модуль». Обычно эта команда вызывает уничтожение модуля и разрыв всех его связей. #!/bin/sh self=1.1.1.1 peer=2.2.2.2 port=7777 if=em1 case "$1" in start) echo "Starting netgraph switch." ngctl mkpeer ${if}: bridge lower link0 ngctl name ${if}:lower switch ngctl connect switch: ${if}: link1 upper ngctl mkpeer switch: ksocket link2 inet/dgram/udp ngctl name switch:link2 switch_socket ngctl msg switch_socket: bind inet/${self}:${port} ngctl msg switch_socket: connect inet/${peer}:${port} ngctl msg ${if}: setpromisc 1 ngctl msg ${if}: setautosrc 0 echo "Ok." exit 0 ;; stop) echo "Stopping netgraph switch." ngctl shutdown switch_socket: ngctl shutdown switch: ngctl shutdown ${if}: echo "Ok." exit 0 ;; restart) sh $0 stop sh $0 start ;; *) echo "Usage: `basename $0` { start | stop | restart }" exit 64 ;; esac [root@bsd1] /usr/local/etc/rc.d/> ngctl list There are 5 total nodes: Name: em0 Type: ether ID: 00000001 Num hooks: 0 Name: em1 Type: ether ID: 00000002 Num hooks: 2 Name: switch Type: bridge ID: 000000f6 Num hooks: 3 Name: ngctl16408 Type: socket ID: 00000100 Num hooks: 0 Name: switch_socket Type: ksocket ID: 000000fa Num hooks: 1 Пакетики бегут: [root@bsd1] /root/> ping 192.168.1.2 PING 192.168.1.2 (192.168.1.2): 56 data bytes 64 bytes from 192.168.1.2: icmp_seq=0 ttl=64 time=3.760 ms 64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=3.527 ms 64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=3.479 ms 64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=4.052 ms [root@bsd1] /root/> ngctl msg switch: getstats 2 Rec'd response "getstats" (4) from "[f6]:": Args: { recvOctets=49333 recvPackets=532 recvMulticast=467 recvBroadcast=63 xmitOctets=580 xmitPackets=12 xmitMulticasts=10 xmitBroadcasts=1 } Написал много текста, надеюсь, доступного для понимания. На практике такой тоннель кому-то покажется не защищенным, т.к пакетики будут бегать в открытом виде по интернету, но никто не мешает вам пробросить этот тоннель внутри VPN соединения. Я, например, такую связку использую для проброса IPTV мультикаста в рабочую сеть. Источник <!-- Вопросы задаем на форуме, не в ЛС --> |
Ответить |
Опции темы | |
Опции просмотра | |
|
|
Похожие темы | ||||
Тема | Автор | Раздел | Ответов | Последнее сообщение |
Инфо Что такое FreeBSD? | Vector | FreeBSD | 4 | 06.05.2010 12:47 |
FAQ Установка FreeBSD 7 | Vector | FreeBSD | 2 | 01.05.2010 03:55 |
Новости Вышел FreeBSD 8.0-RC2 | Vector | Новости Software | 0 | 30.10.2009 00:44 |
Статья webmin for FreeBSD | Stive Stifler | FreeBSD | 0 | 17.09.2009 22:09 |
Статья Установка torrentflux for FreeBSD | Stive Stifler | FreeBSD | 0 | 17.09.2009 22:01 |