Получил очередное письмо с вопросом о демоне pppoed.
Решил, написать несколько статей на эту тему. Я конечно понимаю, что многое уже в Инете написано, но я, как обычно, изложу свое видение данного вопроса.
Как известно на FreeBSD одну и ту же задачу можно решить несколькими способами.
Задача поднять PPPoE сервер никак не отрицает данного утверждения.
Это можно сделать как и на демоне mpd5, который находится в портах /usr/ports/net/mpd5 и который придется собрать и установить , так и на уже встроенном во FreeBSD демоне pppoed:
pppoed — handle incoming PPP over Ethernet connections
который нужно только настроить и все.
Располагается файл pppoed тут: /usr/libexec/pppoed
Упреждая вопросы:
- Что такое PPPoE ?
- Как работает протокол PPPoE ?
- и т.д. и т.п.
Отвечаю: Читайте статью «PPPoE :: Теория вопроса»
Итак приступим к настройке.
1. Конфиг файлы
Располагаются в папке /etc/ppp/
- ppp.conf
- ppp.secret
вот два основных файла, дальше я расскажу ещё о нескольких.
Правим файл ppp.conf, простой пример:
default:
set log Phase tun Chat Command Warning Error Alert Connect ipcp LQM
pppoe:
allow mode direct
set cd 5
enable lqr echo chap chap81 MSCHAPv2 mssfixup
disable pap deflate pred1 acfcomp protocomp
deny deflate pred1 acfcomp
set timeout 0
set lqrperiod 10
set echoperiod 10
set mtu 1492
set mru 1492
set dns 10.1.1.1 10.1.2.254
accept lqr dns
Внимание: Все строчки после «метка:» (label) начинаются с пробела ! (в данном случае метки (labels): default, pppoe)
Данного конфига вполне достаточно, чтобы принимать подключения.
Я не буду подробно описывать каждую строчку этого конфига, т.к. это вполне вменяемо написано в мануале:
man ppp
Смотрите раздел: «PPP COMMAND LIST»
Теперь нам нужно задать логины и пароли, с которыми наши пользователи будут подключаться к серверу.
Правим файл ppp.secret:
user1 passwd1 10.255.254.158 * *
user2 passwd2 10.255.254.248 * *
и так далее. Первая «колонка» — логин, вторая — пароль, третья — IP-адрес, который будет выдан пользователю.
Если необходимо указать диапазон IP-адресов, то сделать это можно указав его в «колонке» IP-адресов, например гостевой вход:
guest guest 10.255.255.129-10.255.255.158 * *
2. Запуск
Синтаксис:
pppoed [-Fd] [-P pidfile] [-a name] [-e exec | -l label] [-n ngdebug] [-p provider] interface
Обеспечим запись логов, куда же без них:
Правим файл /etc/syslog.conf и добавляем в него:
!pppoed
*.* /var/log/pppoed.log
Создаем файл /var/log/pppoed.log:
touch /var/log/pppoed.log
Перезапускаем демон syslogd чтобы применить изменения:
killall -1 syslogd
Готовимся к запуску.
Допустим, что:
- наш PPPoE сервер имеет имя: vpn-01
- имя нашей службы (Service Name): mycoolprovider
- сетевой интерфейс для приема подключений: em1
Таким образом команда для запуска демона будет такой:
/usr/libexec/pppoed -a vpn-01 -p mycoolprovider -l pppoe em1
Обратите внимание что -l pppoe это метка из нашего ppp.conf.
Имя vpn-01 (с которым вы запустили демон, указано после ключа «-a») внесите в ваш файл /etc/hosts, т.к. это влияет на то какой IP-адрес сервера будет отображаться у пользователя, пример:
127.0.0.1 localhost localhost.mydomain.ru
1.1.1.1 vpn-01 vpn-01.mydomain.ru
Таким образом когда пользователь (например Windows), после установки соединения, нажмет «свойства соединения» то в колонке «IP-адрес» сервера он будет видеть именно этот адрес 1.1.1.1
Каким вы сделаете IP-адрес сервера абсолютно по барабану, т.е. его физически может и не присутствовать на этом сервере, т.к. это P2P соединение между сервером и клиентом и пакеты все равно дойдут до конца туннеля.
После подключения клиента к серверу, вы увидите, что на сервере поднимется новый интерфейс и называться он будет tunX (где Х это число от нуля и далее по кол-ву туннелей):
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1492 inet 1.1.1.1 --> 10.255.254.158 netmask 0xffffffff Opened by PID 66176
Если пользователь не может подключиться, то смотрите в лог /var/log/pppoed.log, например возможно, что пользователь не правильно написал логин или пароль.
Если в логах о нем вы ничего не нашли, то «послушайте» что приходит от MAC-адреса пользователя (допустим он 00:14:2a:b6:92:2c) на сетевом интерфейсе (в нашем примере это em1 c МАС-адресом 00:15:17:61:d0:a9):
tcpdump -ni em1 ether host 00:14:2a:b6:92:2c
Возможно, что пользователь не правильно указал имя службы (Service Name), какое имя он указал вы сразу увидите в tcpdump`е.
Запрос от пользователя:
14:15:09.379885 00:14:2a:b6:92:2c > ff:ff:ff:ff:ff:ff, ethertype PPPoE D (0x8863), length 60: PPPoE PADI [Service-Name "coolprovider"] [Host-Uniq 0x0100000001000000]
Видим что пользователь указал в кач-ве имени службы (Service Name): coolprovider
Если оно не соответствует тому, что указано при запуске демона pppoed (ключ -p) на сервере, а в нашем примере оно не соответствует, то сервер PPPoE ничего не ответит данному пользователю и соответственно подключения не состоится.
Существует возможность «избавиться» от имени службы (Service Name) вообще. Точнее будет сказать, что будет абсолютно все равно что пользователь указал в кач-ве имени службы (Service Name).
Для этого нужно при запуске демона в кач-ве имени службы (Service Name) указать символ *:
/usr/libexec/pppoed -a vpn-01 -p «*» -l pppoe em1
Если сервер запущен с «*», то вернувшись к примеру выше, в tcpdump`е мы увидим следующую картину:
Запрос пользователя -> «ищу PPPoE службу coolprovider«:
14:15:09.379885 00:14:2a:b6:92:2c > ff:ff:ff:ff:ff:ff, ethertype PPPoE D (0x8863), length 60: PPPoE PADI [Service-Name "coolprovider"] [Host-Uniq 0x0100000001000000]
Ответ сервера пользователю -> «я vpn-01 готов принять запрос на данную службу, т.к. мне все равно, я принимаю любой service name«:
14:15:09.383270 00:15:17:61:d0:a9 > 00:14:2a:b6:92:2c, ethertype PPPoE D (0x8863), length 58: PPPoE PADO [AC-Name "vpn-01"] [Service-Name] [Service-Name "*"] [Host-Uniq 0x0100000001000000] [AC-Cookie 0x004606CB]
Надеюсь теперь вам с подключением все понятно.
Далее рассмотрим ещё два конфигурационных файла:
- ppp.linkup
- ppp.linkdown
Как и следует из названия файлов их содержимое читается при каждом подключении (поднятие линка (интерфейса tunX)) и при отключении (падение линка (интерфейса tunX))
Для чего они вам могут пригодится ? Область применения этих файлов очень широка. Начиная от сбора какой либо статистики, заканчивая выполнением какой либо команды на сервере.
В мануале вы сможете найти названия переменных, которые можно использовать в этих файлах.
Приведу примеры содержимого файлов ppp.linkup и ppp.linkdown.
Файл ppp.linkup:
pppoe: ! sh -c "/etc/ppp/up.pl USER HISADDR &"
Файл ppp.linkdown:
pppoe: ! sh -c "/etc/ppp/down.pl USER HISADDR UPTIME"
Как вы видите, все опять начинается со строки название метки (label), которая идентифицирует метку в ppp.conf из которой и идет поднятие соединения.
Далее идет строка с выполнением скрипта PERL и передача ему некоторых параметров.
/etc/ppp/up.pl и /etc/ppp/down.pl — скрипты на PERL
USER, HISADDR, UPTIME — параметры
USER — это логин указанный пользователем при подключении
HISADDR — IP-адрес присвоенный пользователю
UPTIME — время, которое интерфейс провел online (был подключен)
Соответственно переданные PERL скрипту параметры помещаются в массив @ARGV. Первый переданный параметр будет @ARGV[0], второй @ARGV[1] и т.д.
Дальше вы в скрипте можете делать с этими данными что угодно.
Вот список всех возможных параметров (все они перечислены в мануале):
AUTHNAME This is replaced with the local authname value. See the ``set authname'' command below. COMPILATIONDATE This is replaced with the date on which ppp was compiled. DNS0 & DNS1 These are replaced with the primary and secondary nameserver IP numbers. If nameservers are negotiated by IPCP, the values of these macros will change. ENDDISC This is replaced with the local endpoint discriminator value. See the ``set enddisc'' command below. HISADDR This is replaced with the peers IP number. HISADDR6 This is replaced with the peers IPv6 number. INTERFACE This is replaced with the name of the interface that is in use. IPOCTETSIN This is replaced with the number of IP bytes received since the connection was established. IPOCTETSOUT This is replaced with the number of IP bytes sent since the connection was established. IPPACKETSIN This is replaced with the number of IP packets received since the connection was established. IPPACKETSOUT This is replaced with the number of IP packets sent since the connection was established. IPV6OCTETSIN This is replaced with the number of IPv6 bytes received since the connection was established. IPV6OCTETSOUT This is replaced with the number of IPv6 bytes sent since the connection was established. IPV6PACKETSIN This is replaced with the number of IPv6 packets received since the connection was established. IPV6PACKETSOUT This is replaced with the number of IPv6 packets sent since the connection was established. LABEL This is replaced with the last label name used. A label may be specified on the ppp command line, via the ``load'' or ``dial'' commands and in the ppp.secret file. MYADDR This is replaced with the IP number assigned to the local interface. MYADDR6 This is replaced with the IPv6 number assigned to the local interface. OCTETSIN This is replaced with the number of bytes received since the connection was established. OCTETSOUT This is replaced with the number of bytes sent since the connection was established. PACKETSIN This is replaced with the number of packets received since the connection was established. PACKETSOUT This is replaced with the number of packets sent since the connection was established. PEER_ENDDISC This is replaced with the value of the peers endpoint discriminator. PROCESSID This is replaced with the current process id. SOCKNAME This is replaced with the name of the diagnostic socket. UPTIME This is replaced with the bundle uptime in HH:MM:SS format. USER This is replaced with the username that has been authenticated with PAP or CHAP. Normally, this variable is assigned only in -direct mode. This value is available irrespective of whether utmp logging is enabled. VERSION This is replaced with the current version number of ppp.
Вы можете использовать любой параметр из этого списка. Если вам необходимо передавать несколько парамтров, то, как вы наверно уже поняли, они перечисляются через пробел.
- Подведем итог. Итак с помощью файлов ppp.linkup и ppp.linkdown вы можете анализировать параметры подключения и на их основе выполнять какие либо действия:
добавлять статичный роутинг
- запускать какой либо процесс (например natd)
- добавлять правила в firewall
- и т.д. и т.п.
Покажу применение ppp.linkup на одном жизненном примере, с которым сам столкнулся, а именно баг с роутингом.
( freebsd 7.2 routing problem, freebsd ppppoe routing problem, wrong route set by ppppoed)
Если у вас установлена FreeBSD версия 7.2-RELEASE (в этой версии баг точно есть) вы сможете заметить такую штуку, что после подключения пользователей к серверу по PPPoE и соответственно поднятия интерфейсов tunX у некоторых клиентов все равно отсутствует соединение с сетью (или доступ в Интернет) несмотря на то, что туннель успешно устанавливается и в логах нет никаких ошибок. Вы можете подумать, что вы что-то не так настроили, но это не так. Рассмотрим ситуацию подробнее.
Допустим есть 3 клиента, они подключаются к серверу и он выдает им IP-адреса:
- 10.255.255.130
- 10.255.255.131
- 10.255.255.132
На сервере их туннели присутствуют, убедимся в этом выполнив команду:
ifconfig -a
что мы видим:
tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1492 inet 1.1.1.1 --> 10.255.255.130 netmask 0xffffffff Opened by PID 4238 tun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1492 inet 1.1.1.1 --> 10.255.255.131 netmask 0xffffffff Opened by PID 4377 tun2: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1492 inet 1.1.1.1 --> 10.255.255.132 netmask 0xffffffff Opened by PID 6674
Вроде бы все хорошо, но пользователи с IP-адресами 10.255.255.131 и 10.255.255.132 жалуются, что у них ничего не работает. Почему ? А вот почему:
Смотрим в таблицу роутинга (выполняем команду netstat) и наблюдаем примерно такую картину:
netstat -rn | grep 10.255.255.131
Destination Gateway Flags Refs Use Netif Expire
10.255.255.131 tun0 US 0 0 tun0
netstat -rn | grep 10.255.255.132
Destination Gateway Flags Refs Use Netif Expire
10.255.255.132 tun0 US 0 0 tun0
Обратите внимание на колонки Gateway и Netif, если вы были внимательны, то сразу заметите разницу в «показаниях» вывода команды ifconfig и netstat.
А именно, что по ifconfig IP-адрес 10.255.255.131 находится за интерфейсом tun1, а 10.255.255.132 за интерфейсом tun2, но вывод netstat утверждает, что все эти адреса находятся за интерфейсом tun0!
А за tun0 должен быть только один IP-адрес и это 10.255.255.130 !
netstat -rn | grep tun0
Destination Gateway Flags Refs Use Netif Expire 10.255.255.130 tun0 US 0 0 tun0 10.255.255.131 tun0 US 0 0 tun0 10.255.255.132 tun0 US 0 0 tun0
Вот это и есть «корень» проблемы. Т.к. получается исходя из таблицы роутинга все пакеты для IP-адресов 10.255.255.131 и 10.255.255.132 отправляются в туннель tun0, а самт то IP-адреса надохятся за tun1 и tun2, поэтому то у этих клиентов ничего и не работает.
Как решить эту проблему ?
Опять же двумя способами:
- порыться в инете на тему патча (я встречал где то при поиске через гугл)
- или воспользоваться конфигурационным файлом ppp.linkup
Я выбрал второй вариант, т.к. уже пользуюсь возможности файла ppp.linkup. Способ моего решения такой:
в файл /etc/ppp/ppp.linkup пишем:
pppoe:
! sh -c "/etc/ppp/fix_route.sh INTERFACE &"
в моем случае полная «версия» файла /etc/ppp/ppp.linkup выглядит так:
pppoe:
! sh -c "/etc/ppp/up.pl USER HISADDR &"
! sh -c "/etc/ppp/fix_route.sh INTERFACE &"
содержимое файла /etc/ppp/fix_route.sh:
#!/bin/sh
sleep 3
`/etc/ppp/kill_route_tun0.pl`
iface=${1}
ip=`/sbin/ifconfig $iface | /usr/bin/grep inet | /usr/bin/cut -f 4 -d " " -`
/sbin/route delete $ip
/sbin/route add $ip/32 -iface $iface
Что делает fix_route.sh:
- запускает PERL скрипт kill_route_tun0.pl
- присваевает переменной iface переданный скрипту параметр имя интерфейса на который подключен клиент
- узнает IP-адрес присвоенный клиенту на интерфейсе (переменная ip)
- удаляет из таблицы роутинга IP-адрес клиента
- добавляет в таблицу роутинга IP-адрес клиента (ip) через интерфейс (iface)
Посмотрим скрипт /etc/ppp/kill_route_tun0.pl:
#!/usr/bin/perl
my $tun="tun0";
my $ip;
my $trash;
open TUN, "/sbin/ifconfig -a | /usr/bin/grep -A2 '\\\(^".$tun.":\\\)' |" or die "Can't find tunnel";
while (){
if (/^\s+inet\s+\d+\.\d+\.\d+\.\d+\s+\-\-\>\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+netmask\s+0xffffffff\s+\n$/){
$ip=$1;
}
}
close (TUN);
open NET, "/usr/bin/netstat -rn | /usr/bin/grep $tun |" or die "Can't open netstat";
while (){
if (/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/){
$trash=$1;
if ($ip ne $trash){
# print "tun0 has $ip but trash route for $trash found on it... delete...\n";
`/sbin/route delete $trash`;
}
}
}
close (NET);
Что делает kill_route_tun0.pl:
- смотрит вывод команды и запоминает какой IP-адрес висит на интерфейсе tun0
- смотрит вывод команды netstat -rn | grep tun0 и запоминает все строки роутинга через интерфейс tun0
- если IP-адрес на tun0 не совпадает с адресом полученным из таблицы роутинга, то такой маршрут (строка с роутингом) убивается
Таким образом мы избавляемся от этого бага и у каждого пользователя в таблице роутинга появляется правильная строчка:
netstat -rn | grep 10.255.255.130
Destination Gateway Flags Refs Use Netif Expire 10.255.255.130/32 tun0 US 0 0 tun0
netstat -rn | grep 10.255.255.131
Destination Gateway Flags Refs Use Netif Expire 10.255.255.131/32 tun1 US 0 0 tun1
netstat -rn | grep 10.255.255.132
Destination Gateway Flags Refs Use Netif Expire 10.255.255.132/32 tun2 US 0 0 tun2
После того как вы создали скрипты fix_route.sh и kill_route_tun0.pl на забудьте сделать их исполняемыми:
chmod a+x fix_route.sh
chmod a+x kill_route_tun0.pl
Вот такой вот пример области применения файла ppp.linkup
Что ещё можно рассказать.
I. Патч (patch) для демона pppoed
В Инете можно найти патч для демона, который позволяет ограничивать минимальное время между приемом нового соединения, для защиты от флуда на подключение к PPPoE серверу.
Для того, что бы применить патч, в системе должны присутствовать исходные коды (sources), т.е. папка /usr/src/ не должна быть пустой.
Если вы установили систему без исходных кодов, то их можно получить отдельно с установочного диска или через Инет, воспользовавшись командой:
sysinstall
После запуска передвигаемся по меню:
Configure Do post-install configuration of FreeBSD
Distributions Install additional distribution sets
Далее отмечаем «крестиком» (жмем пробел) на пункте:
[Х] src Sources for everything
Затем жмем:
All Select all of the below
и два раза
<<< X Exit Exit this menu (returning to previous)
Потом вам предложат выбрать откуда брать выбранное в меню, соответственно выберите нужное, например:
1 CD/DVD Install from a FreeBSD CD/DVD
или
2 FTP Install from an FTP server
или любой другой подходящий вам вариант
и соответственно ждем пока исходные коды перенесутся на HDD
Скачать патч можно тут: http://subnets.ru/saved/pppoed_patch.tar.gz
Распакуйте архив в папку /usr/local/sbin, получится /usr/local/sbin/pppoed
В /usr/local/sbin/pppoed находится файл patch.sh, запустив который вы тем самым пропатчите демон pppoed
Либо после рапаковки архива вы сами можете последовательно выполнить команды:
cd /usr/local/sbin/pppoed
mv /usr/libexec/pppoed /usr/libexec/pppoed_orig
mv /usr/src/libexec/pppoed/pppoed.c /usr/src/libexec/pppoed/pppoed.c_orig
cp /usr/local/sbin/pppoed/pppoed.c /usr/src/libexec/pppoed/
cd /usr/src/libexec/pppoed/
make
make install
make clean
После применения патча нам станет доступна опция -с, воспользовавшись котором можно выставить задержку в секудах на подключение.
Например я выставляю задержку флуда на 20 секунд:
/usr/libexec/pppoed -a vpn-01 -p * -l pppoe -c 20 em1
II. Обламываем домашние роутеры 🙂
Многие провайдеры озадачиваются вопросом как бы сделать так, чтобы пользователь не «раздавал» Интернет соседям через домашний роутер.
Т.е. подключается один пользователь, который покупает себе домашний роутер (например тот же Dlink) и «раздает» подключение к Интернету своим соседям.
В процессе пользования демоном pppoed мной случайно была обнаружена следующая фишка.
Если при запуске pppoed демона вы в ключе «-a» указали имя, но в файле /etc/hosts не опишите соответствие этого имени к IP-адресу, то в кач-ве IP-адреса сервера у клиента будет IP-адрес 127.0.0.1 (localhost).
Не поняли ? Покажу на примере.
запускаем демон pppoed с именем сервера vpn-01: /usr/libexec/pppoed -a vpn-01 -p * -l pppoe -c 20 em1
пример файла /etc/hosts:
127.0.0.1 localhost localhost.mydomain.ru 10.0.0.1 my-cool-gw my-cool-gw.mydomain.ru
Иными словами в /etc/hosts может содержаться сколько угодно хостов главное, чтобы там не было соответствия имени vpn-01 (с которым вы запустили демон, указано после ключа «-a») к какому либо IP-адресу.
После установки подлючения по PPPoE к серверу мы увидим на сервере такую картину, смотрим вывод команды:
ifconfig -a
tun171: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1492 inet 127.0.0.1 --> 10.255.255.132 netmask 0xffffffff Opened by PID 11017
127.0.0.1 — адрес сервера
10.255.255.132 — адрес выданный клиенту
Если подключается обычный компьютер, то он нормально «проглатывает» IP-адрес 127.0.0.1 в кач-ве IP-адреса сервера, а вот если это домашний роутер…. то у роутера сносит крышу и он отказывается выполнять свою прямую работу, а именно «раздавать» Интернет подключение :).
Сказать что все роутеры подвержены этому багу-фиче я не могу, как и назвать точные модели роутеров, но точно могу сказать что многие. Почему я так уверен ?
Ну как я говорил выше я обнаружил баг случайно, когда однажды, мой клиент попросил меня настроить PPPoE сервер для общежития, чтобы там предоставлять услуги доступа в Интернет.
Я настроил, но вот в файлик hosts забыл внести имя 🙂 Ну с кем не бывает.
Так вот посыпались звонки из общежития на тему того, что Интернет не работает. Все звонившие «сидели» за домашними роутерами. Вот так я и обнаружил эту баг-фичу 🙂
Может кому нить и пригодится данная информация 😉
III. Как подключить свой комп под FreeBSD к PPPoE серверу ?
Ну и напоследок рассморим как самому, с компа-клиента под ОС FreeBSD установить PPPoE туннель до сервера.
Делается это с помощью того же ppp.conf, добавим «метку» (label) pppoe-client:
pppoe-client:
set log Phase Chat LCP IPCP CCP tun command
set device PPPoE:IFACE:SERVICE-NAME
set authname login
set authkey password
enable lqr
set dial
set login
set ifaddr 0.0.0.0/0 0.0.0.0/0
add default HISADDR
Помним, что все строчки после «метка:» (label) начинаются с пробела! (в данном случае «метка» это pppoe-client)
Вам необходимо заменить IFACE на Ваше имя интерфейса в сторону провайдера (например, rl0), а SERVICE-NAME заменить на service-name вашего провайдера
Запуск подключения:
ppp -ddial pppoe-client
Остановка подключения:
killall -9 ppp
ifconfig delete tun0
Для запуска подключения при загрузке необходимо добавить в /etc/rc.conf:
ppp_enable="YES"
ppp_mode="ddial"
ppp_profile="pppoe"
ppp_nat="NO"
ppp_user="root"
Так же на эту тему можно почитать хендбук: http://www.freebsd.org/doc/ru/books/handbook/pppoe.html
З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА ! Пожалуйста, уважайте чужой труд.
Автор: Николаев Дмитрий (virus (at) subnets.ru)