HotSpotАвтор: levantuev.В нашем городе очень плохо развита услуга Хот-спот.Хот-спот (от англ. hot spot — «горячее пятно») — участок местности (например, помещение офиса, кафе, кампуса, станция метро), где при помощи портативного устройства (ноутбука или наладонника), работающего по беспроводному протоколу радиодоступа Wi-Fi, можно получить доступ к интернету.Но назло развитию я решил внедрить это дело в Грилль-Бар.Имеем:1) Точка доступа D-Link 2100AP2) Тачку с FreeBSD 7.03) Канал в внешний мир4) ip адреса:re0 (192.168.0.1) - смотрит в точку доступаfxp0 (192.168.1.2) - смотрит в adsl модем (router)диапазон локальной сети - 192.168.0.0/24DNS сервер - 87.103.161.61IP Точки доступа - 192.168.10.10Задача: *Авторизация пользователей должна проходить через веб-интерфейс.*По истичению времени пользователь отключается автоматически.*Должна быть админ.панель для генерации логинов и паролей.*IP адреса должна присваиваться по DHCP.*Ифнормация о пользователях должна храниться в mysqlВ статье подразумевается что у вас - 1) Установлена связка apache20+php5+mysql502) Ядро собрано с поддержкой:options IPFIREWALLoptions IPFIREWALL_VERBOSEoptions IPFIREWALL_DEFAULT_TO_ACCEPToptions IPFIREWALL_FORWARDoptions IPDIVERToptions DUMMYNET3) Компьютер подключен к интернету и выступает в качетсве шлюзаИ так начнемАвторизация и отключение пользователей осуществляется добавлением/удалением правил в ipfw через phproot# ee /usr/local/www/data/config.php<?phpdefine('DEBUG', true);define('conf_DB_HOST', 'localhost'); // хост БДdefine('conf_DB_USER', ''); // юзер БДdefine('conf_DB_PASS', ''); // его парольdefine('conf_DB_NAME', 'wifi'); // Имя БДdefine('RULE_NUM_MIN', 400);define('RULE_NUM_MAX', 600);define('CLIENTS_IP_BEGIN', '192.168.0.2');define('CLIENTS_IP_COUNT', '253');define('CLIENTS_TIME', '3600'); // время для клиента (1 час)define('RULE_ADD_IP', '/usr/local/bin/sudo ipfw add %s allow ip from any to %s');define('RULE_ADD_IP2', '/usr/local/bin/sudo ipfw add %s allow ip from %s to any');define('RULE_DEL_IP', '/usr/local/bin/sudo ipfw del %s');define('RULE_DEL_IP2', '/usr/local/bin/sudo ipfw del %s');$db_link = mysql_connect(conf_DB_HOST, conf_DB_USER, conf_DB_PASS);if (!$db_link) return cms_errors('Подключение к базе данных не выполнено!');if (!mysql_select_db(conf_DB_NAME, $db_link)) return cms_errors('Подключение к базе данных не выполнено!!!');function cms_errors($text){ if (DEBUG) echo $text; return false;}function dumpVarX(&$Var, $Var_s = null){ echo "<div align='left' class='debug'>"; dumpVar($Var, 0, $Var_s); echo "<div>"; return true;}function dumpVar(&$Var, $Level = 0, $Var_s = null){ if ($Level > 4) { echo "<b>...</b> LEVEL > 4<br>"; return; } $is_ob_ar = false; $Type = gettype($Var); if (is_array($Var)) {$is_ob_ar = true; $Type = "Array[".count($Var)."]";} if (is_object($Var)) $is_ob_ar = true; if ($Level == 0) { if ($Var_s) echo "<br><b><span style=\"color:#ff0000\">".$Var_s." = {</span></b>"; if ($is_ob_ar && count($Var)) echo "<pre>"; else echo "<tt>"; $Level_zero = 0; } if ($is_ob_ar) { echo "<span style=\"color:#05a209\">$Type</span>"; for (Reset($Var), $Level++; list($k, $v)=each($Var);) { if (is_array($v) && $k==="GLOBALS") continue; for ($i=0; $i<$Level*3; $i++) echo " "; echo "<b>".HtmlSpecialChars($k)."</b> => "; dumpVar($v, $Level); } } else { if (is_string($Var) && strlen($Var)>400) echo '('.$Type.') <span style="color:#35BBFA"> strlen = '.strlen($Var).'</span>'.""; else echo '('.$Type.') "<span style="color:#0000FF"> ',HtmlSpecialChars($Var),'</span>"'.""; } if (isset($Level_zero)) { if ($is_ob_ar && count($Var)) echo "</pre>"; else echo "</tt>"; if ($Var_s) echo "<b><span style=\"color:#ff0000\">}</span></b><br>"; } return true;}?>Скрипт авторизации пользователяroot# ee /usr/local/www/data/add.php<?phprequire_once('config.php');$user = get_user($_GET['login'], $_GET['pass']);if ($user){ switch ($user['status']) { case 0: if (add_rule($user)) echo '<h2>Вы законектились !</h2>'; else echo 'Ошибка правило не добавлено !'; break; case 1: echo '<h2>Вы уже подклюдчены</h2>'; break; case 2: echo '<h2>Логин уже был использован</h2>'; break; case 3: echo '<h2>Логин отключен</h2>'; break; default: echo 'Error'; break; }} else echo '<h2>Невеный логин/пароль !</h2>';//dumpVarX($_GET, 'GET');//dumpVarX($user, 'User');//dumpVarX($_SERVER['REMOTE_ADDR'], 'REMOTE_ADDR');// авторизацияfunction get_user($login, $pass){ $user = null; if (!$login || !$pass) return null; $login = addslashes($login); $sql = 'SELECT * FROM users WHERE username="'.$login.'" AND password="'.$pass.'" LIMIT 1'; $res = mysql_query($sql); if ($res) $user = mysql_fetch_assoc($res); return $user;}// добавление правилаfunction add_rule($user){ $user_ip = $_SERVER['REMOTE_ADDR']; $current_date = time();// dumpVarX($user_ip, 'user_ip'); if (!checkip($user_ip)) return false; $temp = 0; $sql = 'SELECT rule_num FROM users WHERE status=1 ORDER BY rule_num'; $res = mysql_query($sql); if ($res) { $t = mysql_fetch_array($res); if (!$t) $rule_num = RULE_NUM_MIN; else {// dumpVarX($t,'t'); while ($temp = mysql_fetch_array($res)) {// dumpVarX($temp,'temp'); if (($t[0]+1) < $temp[0]) break; $t = $temp; } if ($t[0] < RULE_NUM_MAX) $rule_num = $t[0]+1; else return false; } } else return false; $command = sprintf(RULE_ADD_IP, $rule_num, $user_ip);// dumpVarX($command, 'command'); exec($command); $command2 = sprintf(RULE_ADD_IP2, $rule_num+100, $user_ip);// dumpVarX($command2, 'command'); exec($command2); $sql = 'UPDATE users SET status=1, time_begin=NOW(), rule_num='.$rule_num.' WHERE id='.$user['id'];// dumpVarX($sql, 'sql'); mysql_query($sql); return true;}function checkip($ip){ if (!$ip) return false; $user_ip = explode('.', $ip); $check_ip = explode('.', CLIENTS_IP_BEGIN); if (($check_ip[0] != $user_ip[0]) && $check_ip[0] != "*") return false; if (($check_ip[1] != $user_ip[1]) && $check_ip[1] != "*") return false; if (($check_ip[2] != $user_ip[2]) && $check_ip[2] != "*") return false; if (!(($check_ip[3] <= $user_ip[3] && ($check_ip[3] + CLIENTS_IP_COUNT) >= $user_ip[3])) && $check_ip[3] != "*") return false; return true;}?>Скрипт блокировки пользователя по истичении времениroot# ee /usr/local/www/data/cron.php<?phprequire_once('config.php');check_users();function check_users(){ $sql = 'SELECT * FROM users WHERE status=1 AND time_begin > 0 AND (TIME_TO_SEC(TIMEDIFF(NOW(), time_begin)) > '.CLIENTS_TIME.')';// dumpVarX($sql); $res = mysql_query($sql); if ($res) { while ($user = mysql_fetch_assoc($res)) { $command = sprintf(RULE_DEL_IP, $user['rule_num']);// dumpVarX($command); exec($command); $command2 = sprintf(RULE_DEL_IP2, $user['rule_num']+100);// dumpVarX($command2); exec($command2); $sql = 'UPDATE users SET status=2, time_end=NOW() WHERE id='.$user['id'];// dumpVarX($sql); mysql_query($sql); } } return true;}?>Формы для ввода данныхroot# ee /usr/local/www/data/index.html<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Администрирование</title> <link rel="stylesheet" type="text/css" href="admin.css" /></head><body><div class="login"><div class="form"><form method="get" action="add.php"><p><label>Логин:</label><input class="text" name="login" type="text" size="16"/></p><p><label>Пароль:</label><input class="text" name="pass" type="password" size="16"/></p><p><input class="submit" type="submit" value="Все верно!"/></form></div><div class="rules"> <h1>Правила пользования Wi-Fi</h1> <ol> <li>Wi-Fi в гриль-баре бесплатный!</li> <li>Закажите любую позицию Wi-Fi меню</li> <li>Получите логин и пароль</li> <li>Пользуйтесь</li> </ol></div></div></body></html>Админ.панель будет лежать в папке admin/ Для безопастности можно Защитить папку паролем root# ee /usr/local/www/data/admin/admin.php<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html> <head> <title>Админ.панель</title> <link type="text/css" rel="stylesheet" href="style.css"> </head><body><form method="post" action="admin.php"> Количество пользователей: <input type="text" value="" name="num" size=2> шт.<br><br> <input type="submit" value="Сгенерировать"></form><hr><?php require_once('config.php'); $n = (int) $_POST['num']; if ($n > 10) { echo 'Количество создаваемых пользователей превышено!<br><br>'; $n=0; } function generate_password($number=10) { $arr = array('1','2','3','4','5','6', '7','8','9','0'); // Генерируем пароль $pass = ""; for($i = 0; $i < $number; $i++) { // Вычисляем случайный индекс массива $index = rand(0, count($arr) - 1); $pass .= $arr[$index]; } return $pass; } for ($i=0; $i<$n; $i++) { $login = generate_password(4); $pass = generate_password(6);// echo 'original: '.$pass.'<br>'; // echo 'login: '.$login.'<br>';// echo 'pass: '.$pass.'<br>'; $sql = 'INSERT INTO users (username, password, status, rule_num) VALUES("apt'.$login.'", "'.$pass.'", 0, 0)'; $res = mysql_query($sql); } if ($res) echo 'Пользователи в количестве <b>'.$n.' </b> шт. добавлены.<br><br>'; $sql = 'SELECT * FROM users'; $res = mysql_query($sql); echo '<table width=\'30%\'> <td><b>Имя</b></td><td><b>Пароль</b></td> <td><b>Статус</b></td><td><b>Правило</b></td>'; while ($data = mysql_fetch_assoc($res)) { echo '<tr>'; echo '<td>'.$data['username'].'</td>'; echo '<td>'.$data['password'].'</td>'; if ($data['status'] == 0) { echo '<td class=\'blue\'>Неактивен</td>'; } if ($data['status'] == 1) { echo '<td class=\'green\'>Используется</td>'; echo '<td>'.$data['rule_num'].'</td>';} if ($data['status'] == 2) { echo '<td class=\'reds\'>Использован</td>'; } if ($data['status'] == 3) { echo '<td><b>Отключён</b></td>'; } echo '</tr>'; } echo '</table>';?> </body></html>Информация о статусе пользователя (Активен, использован, не использован) хранится в mysqlПоэтому создаем базу, пользователя и структуруCREATE TABLE `users` ( `id` int(10) unsigned NOT NULL auto_increment, `username` varchar(50) default NULL, `password` varchar(50) default NULL, `created` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, `time_begin` timestamp NOT NULL default '0000-00-00 00:00:00', `time_end` timestamp NOT NULL default '0000-00-00 00:00:00', `status` tinyint(4) NOT NULL default '0', `rule_num` smallint(5) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `rule_num` (`rule_num`)) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=cp1251 AUTO_INCREMENT=6 ;Правила ipfwroot# cat /etc/rc.ipfw#!/bin/shipfw -q -f flushcmd="ipfw -q add"ifout="fxp0" #Интерфейс смотрящий в модемlocal="re0" #Интерфейс смотрящий в локальную сетьstburdns="87.103.161.61" #DNS провайдераlocalip="192.168.0.0/24"$cmd 200 divert natd all from any to any via ${ifout}$cmd 1000 fwd 192.168.0.1,80 tcp from any to any 80 via ${local}$cmd 1100 allow ip from any to ${stburdns}$cmd 1200 allow ip from ${stburdns} to any$cmd 1300 deny ip from ${localip} to any via ${ifout}$cmd 1400 deny ip from any to ${localip} via ${ifout}Добавляем в cron задание для проверки пользователя (Истрачен лимит по времени или нет)*/1 * * * * root /usr/local/bin/php /usr/local/www/data/cron.phpПрименяем правила sh /etc/rc.ipfwТеперь, что бы правила добавлялись через sudo, создаем пользователя (vasya)и добавляем его в /usr/local/etc/sudoers:vasya ALL=(ALL) NOPASSWD: SETENV: ALLДелаем что бы все скрипты выполнялись от нашего пользователя/usr/local/etc/apache2/http.conf:User vasyaGroup vasyaвсе...