📜 ⬆️ ⬇️

Simple HotSpot on FreeBSD

Good afternoon.
There is a need to create a HotSpot point. As a person familiar with UNIX, I decided to look for software solutions. Moreover, it was not possible to buy special hardware for this business. Yes, and the requirements were very minimal. Namely, organize the distribution of WiFi as it is done, for example, in the network McDonnalds. Those. the user came, connected to the network and calm got his 30 minutes of internet. Or 15 megabytes of traffic.


Initially I tried to adapt ChilliSpot, FreeRadius, EasyHotSpot and other solutions for this business. It never happened to me. That's why I continued searching and came across a HotSpot article from “lissyara” . And I realized that this is exactly what is needed. Having a little corrected the scripts, I got exactly what I need with practically standard FreeBSD tools.

I used FreeBSD 8.2 and packages installed from ports, but this scheme will work on almost any other system, since it does not use any specific programs.
')
So. There is a computer with 2 network cards, there is a WiFi access point. There are no other requirements for iron.
The computer is running FreeBSD, apache, PHP, MySQL, ISC-dhcpd.

First, you need to rebuild the kernel to enable NAT and pipes to limit the speed.
How to build a kernel and install it can be found in Google. Here I will give the parameters with which I compiled the kernel:

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"


Next, after building the kernel, you need to install Apache, PHP and MySQL. There is also a lot of information on this topic on the Internet; therefore, I will not describe this process here.

Register settings in 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" # .


If you read before this article, to which I referred at the beginning, then you are clear about the principle of this scheme. If not, I will explain briefly. And so, a client connecting to a Wi-Fi point receives an ip-address via DHCP. Then, when he tries to open any site he throws it on the welcome page. In the above article on that page login and password is requested. I removed the authorization, so in fact there is only one button on the page - “Access”. It is implemented as follows. The rules of the firewall spelled redirect requests to your web server. After visiting the page and clicking on a single button, a new one is added to the firewall rules, which allows the user with the specified UI to access the Internet. Every minute, the cron daemon calls a script that watches how long the user is online and how much traffic he has spent. And if one or the other is exceeded, it deletes the permitting rule and at the next request this user will again be redirected to the start page.
That's basically the whole essence of this scheme. And so, we will continue implementation.

Next we need to configure the firewall.

Just want to say that there are only basic settings. If you are setting up a machine over SSH, then you need to add a rule that will allow you access to this machine.

Allowing rules will be from number 200 to 600, according to this we will have the rule of redirection at number 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 # , .


In order for absolutely all requests to get to our login page, you need to create a " .htaccess " file in the root of the directory with the site (after allowing Rewrite in httpd.conf) with the following content:

RewriteEngine on
ErrorDocument 404 10.128.1.1/index.php

Then, even if the address bar contains not only the site (http://habrahabr.ru) but also a specific page (http://habrahabr.ru/i/habr.gif), the necessary page will still open.

Now it only remains to create a table in the database in which data on current sessions will be stored, set up a DHCP server and create the necessary PHP scripts that will do all the basic work.

MySQL table is quite simple. The rule number, the time when the session started

 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 ; 

Further, all the files described below should be located in the site directory.

Let's start with the settings page 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'); ?> 


Next, the main page to which users will be redirected is 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>'); ?> 


This page determines what address was entered initially, which would then redirect to it. This, as well as adding a firewall rule, is done by the open.php script:
 <?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; ?> 


After these manipulations, the user has full access to the Internet. The firewall does not block anything, so ICQ and the agent and everything else will work. But on the other hand, this is probably also not correct, and then it will be necessary to configure the firewall and leave only the necessary ports. But this is depending on the need.

Now it remains only to disable users when one of the limits is exceeded and that's it.
Initially it was planned to consider traffic using trafd, but as it turned out, rather than firing a cannon at sparrows, it’s better to work with what traffic already counts. Namely IPFW. When you call ipfw show , column 3 contains the number of bytes that went through the firewall. And since we have a separate rule for each user, then according to the rule number, you can easily calculate how much this traffic user used.
This will be done by the cron.php script:
 <?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); } ?> 


Now we add to the crontab to run, and once a minute our script will check if anyone has exceeded the limit:
*/1 * * * * /usr/local/bin/php /var/www/data/cron.php

Everything seems to be on this.
Naturally the paths, the names of the network cards and some trivia may differ,

PS I almost forgot about DHCP. Here is the config:
# .
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;
}


Now it seems everything.

Source: https://habr.com/ru/post/132369/


All Articles