Pull to refresh

Простой HotSpot на FreeBSD

System administration *
Sandbox
День добрый.
Появилась необходимость в создании HotSpot точки. Как человек, немного знакомый с UNIX, я решил искать софтверные решения. Тем более что покупать специальное железо для этого дела не было возможности. Да и требования были весьма минимальными. А именно организовать раздачу WiFi так, как это сделано, к примеру, в сети McDonnalds. Т.е. пользователь пришел, подключился к сети и спокойной получил свои 30 минут интернета. Либо 15 мегабайт трафика.


Изначально пытался приспособить под это дело ChilliSpot, FreeRadius, EasyHotSpot и прочие решения. У меня это так и не завелось. По этому я продолжил поиски и наткнулся на статью HotSpot у «lissyara». И понял, что это именно то, что надо. Немного подправив скрипты я получил именно то, что мне надо практически штатными средствами FreeBSD.

Я использовал FreeBSD 8.2 и пакеты, установленные из портов, но эта схема будет работать практически на любой другой системе, так как она не использует никаких специфических программ.

И так. Есть компьютер с 2-мя сетевыми картами, есть WiFi точка доступа. Никаких иных требований к железу нет.
На компьютере установлена FreeBSD, apache, PHP, MySQL, ISC-dhcpd.

Во первых вам надо пересобрать ядро, для включения НАТ и пайпов для ограничения скорости.
Как собрать ядро и установить его можно найти в гугле. Тут я приведу параметры, с которыми я собирал ядро:

options IPFIREWALL
options IPFIREWALL_DEFAULT_TO_ACCEPT
options IPFIREWALL_FORWARD
options IPFIREWALL_VERBOSE
options IPFIREWALL_VERBOSE_LIMIT=50
options IPFIREWALL_NAT
options LIBALIAS
options ROUTETABLES=2
options DUMMYNET
options HZ="1000"


Далее, после сборки ядра надо установить Apache, PHP и MySQL. Информации по этой теме тоже полно в интернете, по этому тут этот процесс я расписывать не буду.

Прописываем настройки в rc.conf:

defaultrouter="1.1.1.1" # Шлюз по умолчанию.
gateway_enable="YES" # Наша машина работает как шлюз.

ifconfig_re0="inet 1.1.1.2 netmask 255.255.255.252" # Карточка, которая смотрит в мир.
ifconfig_re1="inet 10.128.1.1 netmask 255.255.255.0" # Карточка, к которой подключена Wi-Fi точка.

inetd_enable="YES"
sshd_enable="YES" #Включаем SSH
sendmail_enable="NO" #Выключаем Sendmail

firewall_enable="YES" # Включаем файрвол
firewall_nat_enable="YES" # Включаем на файрволе НАТ
dummynet_enable="YES" # Dummynet. Будет резать скорость пользователям.
firewall_script="/etc/firewall.sh" # Пусть в скрипту файрвола.


Если вы прочитали перед этим статью, на которую я ссылался вначале, то вас ясен принцип работы этой схемы. Если нет — то объясню вкратце. И так, клиент подключаясь к Wi-Fi точке получает по DHCP ip-адрес. Затем, когда он пытается открыть любой сайт его кидает на страницу с приветствием. В вышеупомянутой статье на той странице запрашивается логин и пароль. Я убрал авторизацию, по этому по сути на странице только одна кнопка — «Получить доступ». Реализуется это следующим образом. В правилах файрвола прописано перенаправлять запросы на свой веб-сервер. После посещения страницы и клика по единственной кнопке в правила файрвола добавляется новое, которое разрешает пользователю с указанный ИП получить доступ к Интернету. Каждую минуту демон cron вызывает скрипт, который смотрит сколько времени пользователь в сети и сколько трафика он израсходовал. И в случае превышения одного или другого он удаляет разрешающее правило и при следующем запросе этот пользователь снова будет переадресован на стартовую страницу.
Вот в принципе и вся суть этой схемы. И так, продолжим реализацию.

Далее нам надо настроить файрвол.

Сразу хочу сказать, что тут только основные настройки. Если вы настраиваете машину по SSH, то вам надо добавить правило, которое разрешит вам доступ к этой машине.

Разрешающие правила будут с номер 200 по 600, по этому правило переадресации у нас будет идти под номером 1000.

/etc/firewall.sh:

ipfw -q flush

ipfw pipe 1 config bw 512Kbit/s mask dst-ip 0x00000001 #Ограничение скорости закачки
ipfw pipe 2 config bw 256Kbit/s mask dst-ip 0x00000001 #Ограничение скорости отдачи

ipfw nat 1 config log if re0 reset same_ports #Включаем НАТ на сетевой карте, которая смотрит в мир.

ipfw add 120 nat 1 ip from 10.128.1.0/24 to any via re0 # НАТим локальную посдеть на выход
ipfw add 121 nat 1 ip from any to 192.168.24.154 via re0 # И на вход.

ipfw add 1000 fwd 10.128.1.1,80 tcp from any to any 80 via re1 # Правило, которое все запросы будет перекидывать на внутренний ВЕБ сервер.


Для того, что бы абсолютно все запросы попадали на нашу страницу авторизации надо создать в корне директории с сайтом файл ".htaccess" (предварительно разрешив Rewrite в httpd.conf) со следующим содержанием:

RewriteEngine on
ErrorDocument 404 10.128.1.1/index.php

Тогда, даже если в строке адреса будет не только сайт (http://habrahabr.ru) но и конкретная страница (http://habrahabr.ru/i/habr.gif), все равно откроется нужная страница.

Теперь остается только создать таблицу в базе данных, в которой будут храниться данные о текущих сессиях, настроить DHCP-server и создать необходимые PHP-скрипты, которые сделают всю основную работу.

Таблица MySQL совершенно простая. Номер правила, время начала сессии.

CREATE TABLE `hotspot` (
  `time_begin` timestamp NOT NULL default '0000-00-00 00:00:00',
  `rule_num` smallint(5) unsigned NOT NULL,
  KEY `rule_num` (`rule_num`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 ;

Далее все файлы, описаные далее должны лежать в директории сайта.

Начнем со страницы настроек config.php:

<?php

//Данные для подключения к базе данных:
define('conf_DB_HOST', 'localhost'); 
define('conf_DB_USER', 'root');        
define('conf_DB_PASS', '');               
define('conf_DB_NAME', 'hotspot');  


define('RULE_NUM_MIN', 200); //Номер, с которого будут начинаться правила, разрешающие доступ.


define('CLIENTS_TIME', '1800'); //Время для клиента 30 минут (в секундах)
define('CLIENTS_TRAF', '30');    //Лимит трафика в мегабайтах


//Команды, которыми открывается или закрывается доступ к интернету
define('RULE_ADD_IP', '/usr/local/bin/sudo /sbin/ipfw add %s pipe 1 ip from any to %s');
define('RULE_ADD_IP2', '/usr/local/bin/sudo /sbin/ipfw add %s pipe 2 ip from %s to any');

define('RULE_DEL_IP', '/usr/local/bin/sudo /sbin/ipfw delete %s');
define('RULE_DEL_IP2', '/usr/local/bin/sudo /sbin/ipfw delete %s');


//Подключение к базе данных.
$db_link = mysql_connect(conf_DB_HOST, conf_DB_USER, conf_DB_PASS);

if (!$db_link) echo('Connect Error!');

if (!mysql_select_db(conf_DB_NAME, $db_link)) 
echo('Connect Error');


?>


Далее основная страница, на которую будут переадресовываться пользователи — index.php:
<H1>
Немного бесплатного Wi-Fi каждому!<br>
Вы можете получить 30 минут или 30 мб трафика.
</H1>

<?php
$redir='http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
echo('<form method=GET action="open.php">');
echo("<input type=hidden name=redir value=$redir>");
echo('<input value="В сеть!" type="submit"></form>');
?>



Эта страница определяет, какой адрес был введен изначально, что бы потом переадресовать на него. Это, а так-же добавления правила файрвола делает скрипт open.php:
<?php

require_once('config.php');
$user_ip = $_SERVER['REMOTE_ADDR'];
$current_date = time();


//Тут мы находим первый свободный номер правил файрвола. У нас они начинаются с 200. Это прописано в RULE_NUM_MIN.

    $temp = 0;
    $sql = 'SELECT rule_num FROM hotspot ORDER BY rule_num';

    $res = mysql_query($sql);
    if ($res)
        {
            $t = mysql_fetch_array($res);
            if (!$t) $rule_num = RULE_NUM_MIN;
            else {
                    while ($temp = mysql_fetch_array($res))
	            {
	             if (($t[0]+1) < $temp[0]) break;
		     $t = $temp;
		    }
                   $rule_num = $t[0]+1
                }
    } else return false;


//Далее мы добавляем в файвол 2 правила. Первое - "на выход", второе "на вход". Номер второго правила получаем прибавив 200 к первому. Итого наша сеть может принять 200 компов.

$command = sprintf(RULE_ADD_IP, $rule_num, $user_ip);

exec($command);

$command2 = sprintf(RULE_ADD_IP2, $rule_num+200, $user_ip);

exec($command2);


//Записываем в базу номер правила и время начала сессии.
$sql = 'INSERT INTO hotspot (time_begin, rule_num) values (NOW(),'.$rule_num.')';
mysql_query($sql);


//И переадресовываем на ту страницу, которую пользователь хотел получить изначально.
$redir=$_GET['redir'];
header("Location: $redir");
return true;

?>


После этих манипуляций у пользователя появляется полный доступ к сети интернет. Файрвол не блокирует ничего, по этому будут работать и аська, и агент и все остальное. Но с другой стороны это наверно тоже не правильно и надо будет потом настроить файрвол и оставить только нужные порты. Но это уже в зависимости от потребности.

Теперь осталось только отключить пользователей при превышении одного из лимитов и все.
Изначально планировалось трафик считать при помощи trafd, но как оказалось, чем стрелять из пушки по воробьям, лучше работать с тем, что и так уже считает трафик. А именно IPFW. При вызове ipfw show в 3 столбце идет количество байт, которые прошли через файрвол. А так как у нас для каждого пользователя будет отдельное правило, то по номеру правила можно легко подсчитать, сколько этот пользователь трафика использовал.
Это у нас будет делать скрипт cron.php:
<?php

require_once('config.php');

//Тут из базы выбираются те пользователи, у которых привышен лимит по времени, и эти пользователи отключаются от интернета путем удаления их правил из файрвола.

$sql = 'SELECT * FROM hotspot WHERE time_begin > 0 AND (TIME_TO_SEC(TIMEDIFF(NOW(), time_begin)) > '.CLIENTS_TIME.')';

$res = mysql_query($sql);
if ($res)
{
    while ($user = mysql_fetch_assoc($res))
        {
		block($user['rule_num']);
        }
}


//Тут мы из данных IPFW вытаскиваем количество принятых данных и сравниваем с лимитом. При превышении лимита пользователь так-же блокируется.

$sql = 'SELECT * FROM hotspot WHERE 1';

$res = mysql_query($sql);
if ($res)
{
    while ($user = mysql_fetch_array($res))
        {
    	    $rule=$user['rule_num'];
	    exec("/usr/local/bin/sudo /sbin/ipfw show $rule | awk '{print($3)}'",$ct);
	    if($ct[0]>=CLIENTS_TRAF*1024*1024) block($rule);
        }
}
return true;




//Функция, которая удаляет правило файрвола и удаляет запись из базы.

function block($num)
{
        $command = sprintf(RULE_DEL_IP, $num);
        exec($command);

        $command2 = sprintf(RULE_DEL_IP2, $num+200);
        exec($command2);

        $sql = 'DELETE FROM hotspot WHERE rule_num='.$num;
        mysql_query($sql);
}

?>



Теперь добавляем в crontab запить, и раз в минуту наш скрипт будет проверять, не превысил ли кто лимит:
*/1 * * * * /usr/local/bin/php /var/www/data/cron.php

На этом кажется все.
Естественно пути, названия сетевых карт и некоторые мелочи могут отличаться,

P.S. Чуть не забыл про DHCP. Вот конфиг:
#Имя домена.
option domain-name "hotspot.my";
#ДНС сервер. Я использую ДНС гугла.
option domain-name-servers 8.8.8.8;

#Время выдачи. И максимальное время.
default-lease-time 600;
max-lease-time 7200;

#Собственно подсеть. С увазанием диапазона и шлюза для клиентов.
subnet 10.128.1.0 netmask 255.255.255.0 {
range 10.128.1.20 10.128.1.220;
option routers 10.128.1.1;
}


Вот теперь кажется все.
Tags:
Hubs:
Total votes 25: ↑19 and ↓6 +13
Views 23K
Comments Comments 20