SlideShare ist ein Scribd-Unternehmen logo
1 von 54
Downloaden Sie, um offline zu lesen
Managing Internet Connections 
                                               With 
                     PPPoE, MikroTik and Radius

          Dashamir Hoxha      <dashohoxha@gmail.com>
          Artur Nurja                <tatanka@albaniaonline.net>

Copyright (C) 2009 Dashamir Hoxha, Artur Nurja. Permission is granted to copy, distribute and/or modify
this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version
published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and
with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free
Documentation License."




1 Abstract
This article will describe how to manage internet clients of an ISP with PPPoE and MikroTik. For 
centralized AAA (Authentication, Authorization and Accounting), freeRadius is used. This article is 
based on the work that we have done at AlbaniaOnline ISP (now called Primo).


2 Introduction
The aim of the project was to give PPPoE service to the internet clients, through MikroTik. This is not 
difficult and can be implemented easily, however we would like to use freeRadius for AAA, so that we 
could keep the data of the clients and their settings in a database. MikroTik should be just a dummy box 
with a simple configuration, so that we can throw it away and replace it by another one at anytime. All the 
client data (username, password, connection settings etc.) should be stored in the freeRadius database. If 
they are in a database the client data can be managed easily by an external application as well (by 
integrating the freeRadius database with the application). 


 2.1 Observations About Implementation Possibilities
Digging into the manual pages of MikroTik, freeRadius, etc. and making experiments, we made these 
observations about the different ways a PPPoE service can be implemented with MikroTik and Radius:

   ●   There can be more than one MikroTik servers in a LAN.

   ●   In a MikroTik server we can run more than one PPPoE service (with different names and 
       profiles).

   ●   Each of the PPPoE services can authenticate the internet users using one or more Radius servers.

   ●   The MikroTik consults the radius server about authenticating a PPPoE user only if this user is not 
       in his local database. 

   ●   A freeRadius server can use one or more MySQL databases for authenticating a user. If it cannot 
connect to one database, then it switches to another.

   ●   A freeRadius server can act as proxy, getting the answer from another radius server. It can use 
       more than one radius server for getting the answer and can do even load­balancing between them, 
       in case that they are the same (are used for the same thing). 

   ●   The attributes of the Internet connection of a client (IP, GW, DNS, bandwidth, etc.) can be defined 
       in the radius database or in the profile of the client in MikroTik.

   ●   In the freeRadius database, the connection attributes can be set for each client or for the group to 
       which the client belongs.

   ●   IP­s that are assigned to the clients can be random (from an IP pool) or each client can be assigned 
       a fixed IP, always the same IP no matter to which MikroTik it is connected.

   ●   The MikroTik gateway server can connect the clients to the Internet using IP­proxy (NAT) or 
       using ARP­proxy.


 2.2 The Architecture of The PPPoE Service
Based on the above observations and on the project goals, we decided to construct the PPPoE service 
infrastructure like this:

   ●   For client authentication we should use 2 freeRadius servers. Each of them will query its own 
       MySQL database and these databases will replicate with each­other (one of them as primary and 
       the other one as secondary). 

       The advantages of using two radius servers instead of one, are these:

               ●   Half of the requests will be handled by one and half will be handled by the other, so, 
                   we will have load­balancing.

               ●   If one of them is not working properly, the other one will handle all the requests and 
                   the service will not be interrupted. This provides high availability of the service (the 
                   service is more stable).

               ●   They also serve as a backup of each­other. If the database of one of them is damaged, 
                   the data can be restored from the other.

   ●   The external application that keeps  the client data will be connected to the radius database (only 
       to the primary one) and will manage the client  data there.

   ●   To manage the client connections, 3­4 or more MikroTik servers can be used. Each of them has 
       the same configuration and they are connected in the same hub/switch with the clients. Since the 
       clients connect randomly to each of them, we are going to have load­balancing between the 
       MikroTik servers. In general, each of the MikroTik servers can serve up to 400­500 clients 
       simultaneously. 

   ●   MikroTik gateways will authenticate the clients by asking the radius servers. Each of them will 
       have two radius servers in its configuration. Half of them ask first radius1 and if they get no 
       answer they ask radius2. The other half ask first radius2. This provides load­balancing for the 
       radius servers.
●      The clients are connected to the Internet through IP­proxy (NAT).

   ●      The IP that is assigned to the clients are random, taken from an IP pool (they are not fixed IP­s).


3 Installing And Configuring freeRadius, MySQL and MikroTik

 3.1 Installing freeRadius
We installed freeRadius on Fedora7. First I installed the packages freeradius and freeradius­mysql : 

 bash# yum install freeradius freeradius-mysql


Then I enabled the service radiusd and started it: 

 bash# /sbin/chkconfig --list radiusd
 bash# /sbin/chkconfig radiusd on
 bash# /sbin/chkconfig --list radiusd

 bash# /sbin/service radiusd start


Since freeradius uses the ports 1812 and 1813 (see e.g. the file /etc/services ), I had to open these 
ports in the firewall, both for tcp and udp . In order to do this, I edited the file 
/etc/sysconfig/iptables and added there these lines: 

 -A    RH-Firewall-1-INPUT       -m   state --state NEW -m tcp -p tcp --dport 1812 -j ACCEPT
 -A    RH-Firewall-1-INPUT       -m   udp -p udp --dport 1812 -j ACCEPT
 -A    RH-Firewall-1-INPUT       -m   state --state NEW -m tcp -p tcp --dport 1813 -j ACCEPT
 -A    RH-Firewall-1-INPUT       -m   udp -p udp --dport 1813 -j ACCEPT


To apply these modifications in firewall, I restarted the service iptables: 

 bash# /sbin/service iptables restart


              Tip: To check that the ports 1812 and 1813 are open in the firewall, we can use one 
              of these commands: 

                bash# /sbin/service iptables status | grep 1812
                bash# /sbin/iptables-save | grep 1812




 3.2 Testing freeRadius Installation
Just to test that freeRadius is correctly installed and works, we can make a simple configuration using the 
standard text files, like this: 

         Edit the file /etc/raddb/clients.conf . At the section client 127.0.0.1 modify the value 
          of secret , for example make it local1 . The entry 
           client 127.0.0.1
{
             . . .
         }

        will allow the localhost to use the radius service. 

       Edit the file /etc/raddb/users . Uncomment there the test user steve (or create another 
        user with similar details). It should look like this: 

         steve       Cleartext-Password := "testing"
                     Service-Type = Framed-User,
                     Framed-Protocol = PPP,
                     Framed-IP-Address = 172.16.3.33,
                     Framed-IP-Netmask = 255.255.255.0,
                     Framed-Routing = Broadcast-Listen,
                     Framed-Filter-Id = "std.ppp",
                     Framed-MTU = 1500,
                     Framed-Compression = Van-Jacobsen-TCP-IP


       Edit /etc/raddb/radiusd.conf and make sure that authorization using files is enabled. (It 
        should be enabled by default, so in general you don't need to modify anything.) 

Now we can use the command radtest to request access for user steve with password testing : 

 bash#   radtest     --help
 bash#   radtest     steve testing 127.0.0.1 10 local1
 bash#   radtest     steve testing localhost 10 local1
 bash#   radtest     steve testingX 127.0.0.1 10 local1
 bash#   radtest     steve testing 127.0.0.1 10 local1X


In the first and second tests you should get the answer 'Access­Accept'. In the last two tests you should 
get the answer 'Access­Reject'. 

Tip: In order to get more details about what happens in the server, run radiusd in debug mode. 
First stop the service: /sbin/service radiusd stop , then run it like this: /usr/sbin/radiusd ­x or 
/usr/sbin/radiusd ­X .

Note: If you have Windows, you may also wish to use NTradPing (downloadable from 
MasterSoft ) instead of radtest. If you do this, or test from any other machine, remember to put 
your PC (or the other machine) in your NAS list in the file /etc/raddb/clients.conf .


 3.3 Set Up freeRadius to Use a MySQL Database
Now that radius is installed and we have tested that it works correctly, we can create a mysql database for 
it and configure radius to use this database. 

       First let's create a new database and a new database user: 

         bash$ mysql -p -u root
         mysql> CREATE DATABASE radiusdb;
         mysql> GRANT ALL ON radiusdb.* TO raduser@localhost IDENTIFIED BY "radpass";
         mysql> exit;
   Now lets create the tables of the database by running the SQL script file that is in the directory 
        freeradius/doc/examples/: 

         bash$ mysql -p -u root -D radiusdb < /usr/share/doc/freeradius-
         1.1.7/examples/mysql.sql


       We should modify now /etc/raddb/sql.conf by setting there the database, the username 
        and the password that are needed to connect to the mysql server: 

                    # Connect info
                    server = "localhost"
                    login = "raduser"
                    password = "radpass"

                    # Database table configuration
                    radius_db = "radiusdb"


                Note: For testing/debug purposes, change sqltrace to yes. Then, freeradius will dump all 
                SQL commands to the debug output.
                Note: You may also need to modify the line about sql_user_name in this file.

       Edit the file /etc/raddb/radiusd.conf and make there these modifications: 

               Uncomment the line saying 'sql' in the authorize{} section and comment the line saying 
                'files'. 

               Also uncomment the line saying 'sql' to the accounting{} section to tell FreeRADIUS to 
                store accounting records in SQL as well. This file should then look something like this: 

                 authorise {
                        preprocess
                        chap
                        mschap
                        suffix
                        eap
                        # files
                        sql
                        pap
                 }

                 accounting {
                        # We leave "detail" enabled to _additionally_ log accounting to
                 /var/log/radius/radacct
                        detail
                        sql
                 }




 3.4 Testing MySQL
Enter some data into the database: 

 bash$ mysql -u raduser -p radpassw
 mysql> USE radiusdb;
 mysql> SHOW TABLES;
mysql>    INSERT   INTO usergroup (UserName, GroupName)
    -->    VALUES   ("radiustest", "testgroup");
 mysql>    SELECT   * FROM usergroup;
 mysql>    INSERT   INTO radcheck (UserName, Attribute, Value)
    -->    VALUES   ("radiustest", "Password", "testpassword");
 mysql>    SELECT   * FROM radcheck;
 mysql>    INSERT   INTO radgroupreply (GroupName, Attribute, op, Value)
    -->    VALUES   ("testgroup","Framed-Compression","==","Van-Jacobsen-TCP-IP");
 mysql>    INSERT   INTO radgroupreply (GroupName, Attribute, op, Value)
    -->    VALUES   ("testgroup","Framed-Protocol","==","PPP");
 mysql>    INSERT   INTO radgroupreply (GroupName, Attribute, op, Value)
    -->    VALUES   ("testgroup","Framed-MTU","==","1500");
 mysql>    INSERT   INTO radgroupreply (GroupName, Attribute, op, Value)
    -->    VALUES   ("testgroup","Service-Type","==","Framed-User");
 mysql>    quit;


Then stop the service /sbin/service radiusd stop and run radiusd in debug mode: /usr/sbin/radiusd ­x 
or /usr/sbin/radiusd ­X . 

Now check access for the user radiustest with password testpassword : 

 bash# radtest radiustest testpassword localhost 10 local1
 Sending Access-Request of id 224 to 127.0.0.1 port 1812
         User-Name = "radiustest"
         User-Password = "testpassword"
         NAS-IP-Address = 255.255.255.255
         NAS-Port = 10
 rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=224, length=44
         Framed-Compression = Van-Jacobson-TCP-IP
         Framed-Protocol = PPP
         Framed-MTU = 1500
         Service-Type = Framed-User




 3.5 Configure MikroTik for Being a PPPoE Server
       First, the package PPP needs to be installed in MikroTik. 

       Now let us suppose that ether1 is connected to WAN and ether2 is connected to LAN. Then add 
        an IP address to ether1, add a gateway, DNS etc. so that the MikroTik box can connect to internet. 

        Important: Do not add an IP address to the internal interface (ether2).

       If NAT is used, ensure that src­nat/masquerade firewall rule has been added ( /ip firewall nat ... ) 
        and it is working properly. It can be added like this: 

         > /ip firewall nat add chain=srcnat out-interface=ether1 action=masquerade


       Test the connection of the MikroTik server to internet: 

         > /ping www.google.com



       Once you have verified the server’s connectivity, create PPP profiles: 
> /ppp profile add name="pppoe-128k" local-address=10.10.1.1 dns-
         server="192.168.25.101" rate-limit=128k/128k
         > /ppp profile add name="pppoe-256k" local-address=10.10.1.1 dns-
         server="192.168.25.101" rate-limit=256k/256k

        These two profiles have different connection speeds. 

       Now create a PPPoE server instance (service) and enable it: 

         > /interface pppoe-server server add service-name="pppoe1" 
                           interface=ether2 
                           one-session-per-host=yes default-profile="pppoe-128k"
         > /interface pppoe-server server print
         > /interface pppoe-server enable 0


       Finally create user accounts: 

         > /ppp secret add name="test128" password="test128" service=pppoe 
                              profile="pppoe-128k" remote-address=10.10.1.111
         > /ppp secret add name="test256" password="test256" service=pppoe 
                              profile="pppoe-256k" remote-address=10.10.1.112
         > ppp secret print
         Flags: X - disabled
          #   NAME        SERVICE CALLER-ID PASSWORD PROFILE             REMOTE-
         ADDRESS
          0   test128     pppoe             test128   pppoe-128k         10.10.1.111
          1   test256     pppoe             test256   pppoe-256k         10.10.1.112


Now the PPPoE server is ready to answer PPPoE requests and to authenticate PPPoE clients. 

Important: We don't need to give an IP address to ether2 (the interface that is on the clients' side) for 
PPPoE to work. The PPPoE will assign automatically to the interface a new IP (which is like 10.10.1.1/32 
in our example). So, a new virtual IP will be assigned to the interface for each client that is connected to 
the server. If we assign an IP address to ether2, then the clients can connect to the internet using ethernet 
instead of using pppoe. In general this is not what we want, because the ethernet connections do not 
require a username and password and their bandwidth cannot be limited as easily as pppoe connections.

 3.6 Testing the PPPoE Service
To configure a windows computer to connect as a PPPoE client, do these: 

       Open the "New Connection Wizard" (from Network Connections). 

       In the next window (Network Connection Type) choose "Connect to the Internet". 

       In the next window (Getting Ready) select the choice "Set up my connection manually". 

       In the next step of the wizard (Internet Connection), select "Connect using a broadband connection 
        that requires a username and password". 

       Then, in the next window (Connection Name), write a name for the connection, something like 
        "PPPoE". 

       Next, give the username and password, for example username: test256 , password: test256 . 
   Finally finish the wizard. Now you should have a new connection at the Network Connections. 

To configure a Fedora7 computer to connect as a PPPoE client, follow these steps: 

       From the menus, open System > Administration > Network . You have to give the root password 
        in order to access this menu. 

       Create a new connection and select connection type xDSL. 

       Give username, password, etc. 

       Activate the new connection. 



 3.7 Getting MikroTik to Work with RADIUS
Right now we have a MikroTik that is configured as a PPPoE server and we have a freeRadius server that 
is configured to check the data in a MySQL database. Now we need to configure MikroTik to use the 
radius server for authenticating users. We also need to enter in the database of radius the data of the 
clients (username, password and connection properties). Let's see how these can be done. 

       In MikroTik, add a radius server for the service ppp : 

         > /radius add service=ppp address=192.168.25.101 secret=mikro1


        The IP of the radius server is 192.168.25.101 and it will be used for authenticating PPPoE clients 
        (users of service ppp ). The secret is like a password that the MikroTik and radius servers use to 
        verify each­other. 

       Tell the ppp service to use radius for AAA (Authentication, Authorization and Accounting): 

         > /ppp aaa set use-radius=yes
         > /ppp aaa print


       In the radius server, make sure that the configuration file /etc/raddb/clients.conf
        contains a section like this: 

         client 192.168.25.1 {
                 secret                    = mikro1
                 shortname                 = MikroTik1
         }


        Then restart the radius service.

       Make sure that the user test256 is not authenticated in the ppp service of MikroTik (or remove it if 
        it is there): 

         > /ppp secret print
         > /ppp secret remove 1


        Check and make sure that the client test256 cannot connect to internet using the PPPoE 
connection. 

       Now lets add some data about the user test256 in the mysql database of radius: 

         bash$ mysql -p -u root
         mysql> show databases;
         mysql> use radius;
         mysql> show tables;
         mysql> insert into usergroup (Username, GroupName) values ('test256',
         'static256');
         mysql> select * from usergroup where username='test256';
         +------------+-----------+----------+
         | UserName    | GroupName | priority |
         +------------+-----------+----------+
         | test256     | static256 |        1 |
         +------------+-----------+----------+
         1 row in set (0.02 sec)

         mysql> insert into radcheck (Username, Attribute, Value, Op)
            --> values ('test256', 'Cleartext-Password', 'test256', ":=");
         mysql> select * from radcheck where username='test256';
         +----+----------+--------------------+----+----------+
         | id | UserName | Attribute          | op | Value    |
         +----+----------+--------------------+----+----------+
         | 2 | test256 | Cleartext-Password | := | test256 |
         +----+----------+--------------------+----+----------+
         1 row in set (0.00 sec)

         mysql> insert into radreply (UserName, Attribute, Value, Op)
            --> values ('test256', 'Framed-IP-Address', '192.168.10.101', ':=');
         mysql> select * from radreply where username='test256';
         +----+----------+-------------------+----+----------------+
         | id | UserName | Attribute         | op | Value          |
         +----+----------+-------------------+----+----------------+
         | 1 | test256 | Framed-IP-Address | := | 192.168.10.101 |
         +----+----------+-------------------+----+----------------+
         1 row in set (0.02 sec)

         mysql> insert into radgroupreply (GroupName, Attribute, Value, Op)
            --> values ('static256', 'Framed-Protocol', 'PPP', ':=');
         mysql> insert into radgroupreply (GroupName, Attribute, Value, Op)
            --> values ('static256', 'Service-Type', 'Framed-User', ':=');
         mysql> insert into radgroupreply (GroupName, Attribute, Value, Op)
            --> values ('static256', 'Framed-Compression', 'Van-Jacobsen-TCP-IP',
         ':=');
         mysql> select * from radgroupreply where groupname='static256';
         +----+-----------+--------------------+----+---------------------+
         | id | GroupName | Attribute          | op | Value                |
         +----+-----------+--------------------+----+---------------------+
         | 5 | static256 | Framed-Protocol     | := | PPP                  |
         | 6 | static256 | Service-Type        | := | Framed-User          |
         | 7 | static256 | Framed-Compression | := | Van-Jacobsen-TCP-IP |
         +----+-----------+--------------------+----+---------------------+
         3 rows in set (0.00 sec)


If you try now to connect to internet as a client with username 'test256' and password 'test256', using 
PPPoE, it should work. However, to make sure that it really works through radius authentication, which 
gets the authentication data from the database, you can stop the radiusd service (in the radius server) and 
run it in debug mode: /usr/sbin/radiusd ­x . 
4 Using Two Radius Servers

 4.1 Adding a Second Radius Server in MikroTik
MikroTik can be configured to use more than one radius server for the authentication of the users. It is 
done simply by adding additional radius servers: 

  > /radius add service=ppp address=192.168.25.101                secret=mikro1
  > /radius add service=ppp address=192.168.25.102                secret=mikro1
  > /radius print
 Flags: X - disabled
  #   SERVICE          CALLED-ID     DOMAIN                       ADDRESS              SECRET
  0   ppp                                                         192.168.25.101       mikro1
  1   ppp                                                         192.168.25.102       mikro1


In this case, MikroTik tries to use the first server for authentication, and if it cannot be connected, tries 
the second one. This make the service of Radius more robust (if one server is down, there is still the other 
one). 

The configuration of the second radius server is the same as the first one. Both of their MySQL databases 
replicate with each­other (two­way, bidirectional replication), so that both of them can be used at the 
same time and they can synchronize the data automatically.

Since both of the servers can be used at the same time (and they synchronize with each­other), then we 
can have a kind of load balancing by configuring half of the MikroTik servers to have one radius server as 
primary, and the other half to have the other server as primary in the list. 

So, this redundancy of the radius servers ensures both high­availability and load balancing. 


 4.2 Replicating MySQL Databases of Radius
In order to ensure service backup, high availability and load balancing, we replicate the databases of the 
radius servers. We do it a two­way replication, so that both of them can be used for reading and writing, 
and so that in case that one of them goes off, we don't need to do any manual configuration. 

The two­way replication can be done like this: 

    1. In both servers modify the section [mysqld] in the configuration file /etc/my.cnf and add 
       these lines: 

         ### configuration as a master for replication
         server-id=1
         log-bin=mysql_bin
         innodb_flush_log_at_trx_commit=1
         sync_binlog=1
         slave-skip-errors=all
         auto_increment_offset=1
         auto_increment_increment=10


       Make sure the servers have unique (different) server IDs, that skip­networking is not set, and that 
       binary logging is enabled. Also make sure that auto_increment_offset is different for each server. 
       (For the meaning of these options check the manual of mysql.) 
2. In the first server create a user that has permission to do replication: 

    mysql> GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'192.168.25.%'
       --> IDENTIFIED BY 'replpassw';
    mysql> FLUSH PRIVILEGES;



3. Make a full backup of the mysql database in the first server: 

    /usr/bin/mysqldump --user=root --password --all-databases 
                       --lock-all-tables --flush-logs --flush-privileges 
                       --add-drop-database --add-drop-table 
                       --force --master-data > backup.sql



4. Copy it to the other server and restore it: 

    bash# scp root@192.168.25.101:backup.sql .
    bash# /sbin/service mysqld start
    bash# mysql -p -u root < backup.sql



5. Setup the second server as slave and start the replication in it: 

    mysql> CHANGE MASTER TO MASTER_HOST='192.168.25.101',
                            MASTER_USER='repluser',
                            MASTER_PASSWORD='replpassw';
    mysql> START SLAVE;
    mysql> show slave statusG



   The options MASTER_LOG_FILE and MASTER_LOG_POS can be given to CHANGE 
   MASTER TO as well, however they are restored from the backup file (since mysqldump was 
   called with the option ­­master­data). If you want to add them manually, then read their values 
   from the backup file backup.sql .

6. Setup also the first server as a slave of the first master. 

       a. In the second server (that will be master of the first one), get the status of the master: 
            mysql> show master status;
            +------------------+----------+--------------+------------------+
            | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
            +------------------+----------+--------------+------------------+
            | mysql_bin.000001 |    430585 |             |                  |
            +------------------+----------+--------------+------------------+
            1 row in set (0.00 sec)



       b. Set the values of File and Position that are displayed above to the slave (first server), along 
          with the host, username and password: 
mysql> stop slave;
                mysql> CHANGE MASTER TO MASTER_HOST='192.168.25.102',
                                        MASTER_USER='repluser',
                                        MASTER_PASSWORD='replpassw',
                                        MASTER_LOG_FILE='mysql_bin.000001',
                                        MASTER_LOG_POS=430585;
                mysql> start slave;
                mysql> show slave statusG



              The user 'repluser' has to be created in 192.168.25.102 and it should have replication 
              permissions. However, since this user was created in 192.168.255.101 and a full backup of 
              the database was done, this user should have been created when we restored the database to 
              it.

              In the output of the command show slave statusG , make sure that both 
              Slave_IO_Running and Slave_SQL_Running are Yes , otherwise find out what is the 
              problem.


 4.3 Checking Replicated Databases
It can happen that the replicated databases become inconsistent (especially since we have used the option 
slave­skip­errors=all in /etc/my.cnf ). So, it is important to check and make sure that both databases 
have exactly the same data. This can be done using the mysql external tools in 
http://maatkit.sourceforge.net/ . They can be installed with: 

 bash$ perl Makefile.PL
 bash# make install


Checking that the replicated databases are the same can be done with this command: 

 bash$ mysql-table-checksum --replicate=replcheck.checksum --replcheck --user=root
 --askpass 192.168.25.121


The server that is given in the command should be the master, however, since we use a two­way 
replication, it doesn't matter which one we give. 

The database replcheck and the table checksum must be created in the database, before we run this 
command. The table checksum can be created like this: 

      CREATE TABLE checksum (
         db         char(64)     NOT NULL,
         tbl        char(64)     NOT NULL,
         chunk      int          NOT NULL,
         boundaries char(64)     NOT NULL,
         this_crc   char(40)     NOT NULL,
         this_cnt   int          NOT NULL,
         master_crc char(40)          NULL,
         master_cnt int               NULL,
         ts         timestamp    NOT NULL,
         PRIMARY KEY (db, tbl, chunk)
      );
If it turns out that there are differences between the databases, an option for fixing them is the tool mysql­
table­sync . 




5 SQL API for Radius Manager
In order to manage the radius, we used Radius Manager (http://www.dmasoftlab.com/), which is an 
application for managing the database of freeRadius, services, clients, etc. It even had some simple billing 
functionality. Unfortunately, Radius Manager was not free software (open source), although it was cheap 
and the support was quite responsive. We needed to integrate Radius Manager with our own client 
application (built with SugarCRM), so we tried hard to convince the RM guys to sell us the source code 
as well. However they would not listen at all about it. Anyway, we managed to do some kind of 
integration by modifying the database of Radius Manager from our client application. In order to do this, 
we built an external SQL API for Radius Manager.

Since Radius Manager is not free software, we are not going to describe here our experience about its 
installation and configuration. It does not need it and it does not deserve it. Instead, we are going to 
describe just the SQL API that we built to work around the integration problems.


 5.1 Advantages of Using It
This SQL API helps to access the database of the Radius Manager from an outside program (from the 
program that manages the services and users). it is a library of MySQL procedures, which can be used to 
access and modify only those parts of the database that need to be touched. 

It has these advantages: 

     ●   It encapsulates (hides) the complexity of the database from the outside programmer. The 
         programmer doesn't have to know what tables or fields are there in the database, but just needs to 
         know the procedures/functions that are available in the API, their parameters, return values, etc. 

     ●   It makes simpler the code of the outside program, because instead of using complicated SQL 
         queries, he just needs to call a procedure with the appropriate parameters. 

     ●   The programmer is relived from the fear and the responsibility of touching something 
         inappropriately in the database. The responsibility of doing things appropriately is taken over by 
         the API. 

     ●   In case that the structure of the radius manager is modified in the future releases, it is the 
         responsibility of the API to take them into account, and the code of the external program doesn't 
         need to change at all. 


 5.2 Procedure Descriptions and Usage Examples

●   user_get

    procedure user_get(user varchar(32))
Returns the data of a given user. Gets the username of the user as a parameter (type: varchar(32)), and 
returns one or more records with the data of the users who match the data of the username. Matching is 
done with LIKE. It may return nothing if such a user does not exist. The record that is returned has these 
fields:     username, srvid, srvname, expiration, enabled, nr_conn

       > call radius.user_get('%');
           -- returns the list of all the users
       > call radius.user_get('%d%');
           -- returns all the users with a 'd' in username
       > call radius.user_get('xyz');
           -- returns nothing because such a user does not exist
       > call radius.user_get('test');
           -- returns the data of the user 'test':
           +----------+-------+----------------+------------+---------+---------+
           | username | srvid | srvname        | expiration | enabled | nr_conn |
           +----------+-------+----------------+------------+---------+---------+
           | test     |     2 | 256/128 Biznes | 2007-12-30 |       1 | 1       |
           +----------+-------+----------------+------------+---------+---------+
           1 row in set (0.00 sec)



●   user_add

    procedure user_add(user                      varchar(32),
                       passwd                    varchar(32),
                       service_id                int(11),
                       nr_conn                   varchar(5),
                       expxiration_date          date )


Add a user in the database. It deletes first this user, in case that such a user already exists. It is the 
responsibility of the application to check and make sure that the users that are added are unique (no two 
users with the same username are added), otherwise the second user will overwrite the first one. 

       > call   radius.user_add('xyz', 'passwd', 3, 2, 2007-12-15);
           --   inserts a new user with 2 simultaneous connections
       > call   radius.user_add('abc', 'passwd1', '4', '1', '2007-12-15');
           --   inserts another user with only 1 connection
       > call   radius.user_add('xyz', 'passwd', 3, 2, '2007-12-15');
           --   gives error because the user 'xyz' already exists



●   user_update

    procedure user_update(user                        varchar(32),
                          service_id                  int(11),
                          nr_conn                     varchar(253),
                          expiration_date             date )


Update the data of the given user in the database. 

       > call radius.user_update('xyz', 3, 2, '2008-1-30');
           -- update the data of the user 'xyz'
              (for example expiration date is changed)
●   user_set_password

    procedure user_set_password(user           varchar(32),
                                passwd         varchar(32) )


Set a new password to the given user. 

       > call radius.user_set_password('xyz', 'passwd-1');



●   user_change_service

    procedure user_change_service(old_srvid varchar(100),
                                  new_srvid varchar(100))


Change an old service to a new one for all the users. This can be used in case that a service becomes 
obsolete and all the users of this service should be upgraded to another service. 

       > call radius.user_change_service('3', '7');



●   user_del

    procedure user_del(user varchar(32))


Delete the given user. 

       > call radius.user_del('xyz');



●   service_get

    procedure service_get(service_id   varchar(100),
                          service_name varchar(40) )


Return a list of services that match the given service id and name. Matching is done with LIKE, so the 
service name and id may contain wildcards and the procedure may return more than one record (or none 
if nothing matches). The result that is returned contains these fields: 

    srvid, srvname, download_rate, upload_rate, enabled

Fields download_rate and upload_rate are in Kbps, integers. The field enabled can be 1 or 0. If it is 0, 
then this service should not be used for the new clients and after some time may be deleted from the table.
 
       > call   radius.service_get('%', '%');
           --   returns a list of all the services
       > call   radius.service_get('%', '%256%');
           --   returns a list of the services that contain '256' in the name
       > call   radius.service_get('%', '256');
-- returns nothing because no service has the name '256'
       > call radius.service_get('2', '%');
           -- returns the service with id=2:
           +-------+----------------+---------------+-------------+---------+
           | srvid | srvname        | download_rate | upload_rate | enabled |
           +-------+----------------+---------------+-------------+---------+
           |     2 | 256/128 Biznes |           256 |         128 |       1 |
           +-------+----------------+---------------+-------------+---------+
           1 row in set (0.00 sec)



●   service_add

    procedure service_add(service_name             varchar(40),
                          download_rate            int(11),
                          upload_rate              int(11) )


Add a new service. Download and upload rates are in Kbps. In case that a service with the same id 
already exists, it is deleted first and then the new service is added. So, it is the responsibility of the 
application to make sure that it does not overwrite existing services. 

       > call radius.service_add('256/128 Vetem 1 PC', 256, 128);
       > call radius.service_add('512/256 Vetem 1 PC', '512', '256');



●   service_update

    procedure service_update(service_id                int(11),
                             service_name              varchar(40),
                             download_rate             int(11),
                             upload_rate               int(11),
                             enabled                   int(11) )


Update the attributes of a service. 

       + call radius.service_update(2, '256/128 Biznes', 256, 128, 0);
       + call radius.service_update('3', '256/128 Familje', '256', '128', '0');
           -- disable these services




●   service_del

    procedure service_del(service_id int(11))


Delete the service with the given id. It is deleted only if there are no users having this service. See also 
user_change_service(). 

       + call radius.service_del('2');
       + call radius.service_del(3);
           -- delete these services
5.3 Implementation
The MySQL procedures can be declared in a file, for example rm_api.sql and then they can be loaded 
in the MySQL server like this: 

 bash$ mysql -p -u root
 mysql> ?
 mysql . rm_api.sql


or like this: 
 bash$ mysql -p -u root < rm_api.sql


The declaration of the procedures is like this: 

 /**
  * Set the delimiter of the SQL commands to double semicolon,
  * because semicolon needs to be used inside the procedure declaration
  * to separate the statements.
  */
 DELIMITER ;;

 /**
  * Select the database that will be used by the procedures and functions.
  * The procedures and functions will be attached to this database.
  */
 USE radius ;;

 /** procedure user_get
  * Returns the data of a given user.
  * Gets the username of the user as a parameter (type: varchar(32)),
  * and returns one or more records with the data of the users who
  * match the data of the username. Matching is done with LIKE.
  * It may return nothing if such a user does not exist.
  * The record that is returned has these fields:
  *    username, srvid, srvname, expiration, enabled, nr_conn
  */
 DROP PROCEDURE IF EXISTS user_get ;;
 CREATE PROCEDURE user_get(user varchar(32))
 BEGIN
   SELECT rm_users.username, rm_services.srvid, srvname, expiration,
           enableuser AS enabled, radcheck.Value AS nr_conn
   FROM rm_users
         LEFT JOIN rm_services USING (srvid)
         LEFT JOIN radcheck ON ( rm_users.username = radcheck.UserName
                                 AND radcheck.Attribute = 'Simultaneous-Use' )
   WHERE rm_users.username LIKE user;
 END
 ;;


 /** procedure user_add
  * Add a user in the database.
  * Takes these parameters:
  *   username, password, service_id, nr_conn, expiration_date
  */
 DROP PROCEDURE IF EXISTS user_add ;;
 CREATE PROCEDURE user_add(user             varchar(32),
                            passwd          varchar(32),
                            service_id      int(11),
                            nr_conn         varchar(5),
expiration_date date)
BEGIN
  ### first delete it, in case that such a user exists
  CALL user_del(user);

  ### insert a record into the table rm_users
  INSERT INTO rm_users
  SET
      username    = user,
      password    = MD5(passwd),
      srvid       = service_id,
      expiration = expiration_date,
      enableuser = '1',
      createdon   = NOW(),
      createdby   = 'admin';

  ### insert two records into the table radcheck
  INSERT INTO radcheck (UserName, Attribute, op, Value)
  VALUES
      (user, 'Simultaneous-Use', ':=', nr_conn),
      (user, 'User-Password',    ':=', passwd);
END
;;


/** procedure user_update
 * Update the data of the given user in the database.
 * Takes these parameters:
 *    username, service_id, nr_conn, expiration_date
 */
DROP PROCEDURE IF EXISTS user_update ;;
CREATE PROCEDURE user_update(user              varchar(32),
                              service_id       int(11),
                              nr_conn          varchar(253),
                              expiration_date date)
BEGIN
  UPDATE rm_users
  SET srvid        = service_id,
       expiration = expiration_date,
       enableuser = '1',
       createdon   = NOW(),
       createdby   = 'admin'
  WHERE username     = user;

  UPDATE radcheck
  SET Value = nr_conn
  WHERE UserName = user
    AND Attribute = 'Simultaneous-Use';
END
;;


/** procedure user_set_password
 * Set a new password to the given user.
 * Gets the parameters: user, passwd
 */
DROP PROCEDURE IF EXISTS user_set_password ;;
CREATE PROCEDURE user_set_password(user varchar(32), passwd varchar(32))
BEGIN
  UPDATE rm_users
  SET password = MD5(passwd)
  WHERE username = user;
END
;;
/** procedure user_change_service
 * Change an old service to a new one for all the users.
 * Gets the parameters: old_srvid, new_srvid
 */
DROP PROCEDURE IF EXISTS user_change_service ;;
CREATE PROCEDURE user_change_service(old_srvid varchar(100),
                                     new_srvid varchar(100))
BEGIN
  UPDATE rm_users
  SET    srvid = new_srvid
  WHERE srvid = old_srvid;
END
;;


/** procedure user_del
 * Delete the given user.
 */
DROP PROCEDURE IF EXISTS user_del ;;
CREATE PROCEDURE user_del(user varchar(32))
BEGIN
  DELETE FROM rm_users WHERE username = user;
  DELETE FROM radcheck WHERE UserName = user;
END
;;


/** procedure service_get
 * Return a list of services that match the given service id and name.
 * Matching is done with LIKE, so the service name and id may contain
 * wildcards and the procedure may return more than one records
 * (or none if nothing matches).
 * The result that is returned contains these fields:
 *    srvid, srvname, download_rate, upload_rate, enabled
 * 'download_rate' and 'upload_rate' are in Kbps, integers
 * 'enabled' can be 1 or 0. If it is 0, then this service
 * should not be used for the new clients and after some
 * time may be deleted from the table.
 */
DROP PROCEDURE IF EXISTS service_get ;;
CREATE PROCEDURE service_get(service_id     varchar(100),
                              service_name varchar(40) )
BEGIN
  SELECT srvid, srvname, (downrate DIV 1024) AS download_rate,
          (uprate DIV 1024) AS upload_rate, enableservice AS enabled
  FROM rm_services
  WHERE srvid    LIKE service_id
    AND srvname LIKE service_name;
END
;;


/** procedure service_add
 * Add a new service. Parameters that are given to this procedure
 * are these: service_name, download_rate, upload_rate
 * Download and upload rates are integers in Kbps.
 */
DROP PROCEDURE IF EXISTS service_add ;;
CREATE PROCEDURE service_add(service_id     varchar(100),
                             service_name   varchar(40),
                             download_rate int(11),
                             upload_rate    int(11))
BEGIN
  ### if such a service exists, delete it first
  CALL service_del(service_id);
### insert the new service
   INSERT INTO rm_services
   SET
       srvid = service_id,
       srvname = service_name,
       downrate = (download_rate * 1024),
       uprate = (upload_rate * 1024),
       enableservice = '1',
       limitexpiration = '1',
       poolname = 'pool0';
 END
 ;;


 /** procedure service_update
  * Update the attributes of a service.
  * The parameters of the procedure are:
  *    service_id, service_name, download_rate, upload_rate, enabled
  * Download and upload rates are integers in Kbps.
  */
 DROP PROCEDURE IF EXISTS service_update ;;
 CREATE PROCEDURE service_update(service_id      int(11),
                                  service_name   varchar(40),
                                  download_rate int(11),
                                  upload_rate    int(11),
                                  enabled        int(11))
 BEGIN
   UPDATE rm_services
   SET srvname = service_name,
        downrate = (download_rate * 1024),
        uprate = (upload_rate * 1024),
        enableservice = enabled
   WHERE srvid = service_id;
 END
 ;;


 /** procedure service_del
  * Delete the service with the given id.
  * It is deleted only if there are no users having this service.
  */
 DROP PROCEDURE IF EXISTS service_del ;;
 CREATE PROCEDURE service_del(service_id int(11))
 BEGIN
   ### get the number of the users which have this service
   DECLARE cnt INT DEFAULT 0;
   SELECT COUNT(*) INTO cnt FROM rm_users WHERE srvid = service_id;

   ### delete the service only if there are no users having it
   IF cnt = 0 THEN
     DELETE FROM rm_services WHERE srvid = service_id;
   END IF;
 END
 ;;


 /** Set the delimiter of the SQL commands back to semicolon. */
 DELIMITER ;




 5.4 User Access Rights
Access rights can be arranged to the user used by the program in such a way that it is able to execute only 
these procedures. It can be done as follows: 
/**
  * Create a user and assign privileges to be able to execute
  * the functions and procedures in this file.
  */
 USE radius;
 DROP USER 'prog'@'192.168.25.%';
 CREATE USER 'prog'@'192.168.25.%' IDENTIFIED BY 'progpassw';
 GRANT EXECUTE ON PROCEDURE user_get TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE user_add TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE user_update TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE user_set_password TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE user_change_service TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE user_del TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE service_get TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE service_add TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE service_update TO 'prog'@'192.168.25.%';
 GRANT EXECUTE ON PROCEDURE service_del TO 'prog'@'192.168.25.%';

 /**
  * Create a user for phpMyAdmin, which can see only certain tables.
  */
 USE radius;
 DROP USER 'prog'@'localhost';
 CREATE USER 'prog'@'localhost' IDENTIFIED BY 'progpassw';
 GRANT SELECT ON rm_users TO 'prog'@'localhost';
 GRANT SELECT ON rm_services TO 'prog'@'localhost';
 GRANT SELECT ON radcheck TO 'prog'@'localhost';



In order to check what is happening in the database with phpMyAdmin, another local user has been 
created, which has only SELECT access.


 5.5 Using It On SugarCRM
SugarCRM has some logic hooks where you can call custom code. These logic hooks are placed before 
saving a record, before deleting it etc. In these logic hooks we have placed our code which integrates 
SugarCRM with the database of freeRadius. So, the clients are managed on SugarCRM, however some 
relevant data (like username, password, etc.) are replicated on the freeRadius database as well.

The custom code that does it, looks like this:

SugarCRM/custom/modules/Accounts/UpdateRadiusClients.php:

 <?php
   /**
    * This file contains hooks of the module Account.
    */

 require_once('data/SugarBean.php');
 require_once('modules/Accounts/Account.php');
 require_once('custom/include/MysqlDB.php');

 /**
  * The class UpdateRadiusClients contains some hooks
  * for the module Accounts, which keep updated the data
  * of the clients in the radius manager.
  */
 class UpdateRadiusClients
 {
/** connection to the radius database */
var $radb;

var $default_email = 'reception@albaniaonline.net';

function __construct()
{
  $this->radb = new MysqlDB('192.168.25.101', 'usr', 'pass', 'radius');
}

function debug($msg)
{
  $GLOBALS['log']->fatal($msg);
}

/** Fired before a record is deleted. */
function before_delete(&$bean, $event, $arguments)
{
  //delete the user from the radius manager as well
  $username = $bean->username_c;
  try
    {
      $this->radb->query("call user_del('$username');");
    }
  catch (Exception $e)
    {
      $msg = 'UpdateRadiusClients::before_delete(): ' . $e->getMessage();
      $GLOBALS['log']->fatal($msg);
      print $e->getMessage();
      exit(1);
    }
}

/** Fired before a record is saved. */
function before_save(&$bean, $event, $arguments)
{
  try
    {
      //get the old (unmodified yet) values of the data
      $old_bean = new Account();
      $old_bean->retrieve($bean->id);

        //check whether this is a new record or an existing one
        if ($old_bean->id=='')
          {
             //this is a new record, add a new user
             $this->add_radius_user($bean);
          }
        else
          {
             //update the user data in radius manager
             $this->update_radius_user($bean, $old_bean);
          }
      }
    catch (Exception $e)
      {
        $msg = 'UpdateRadiusClients::before_save(): ' . $e->getMessage();
        $GLOBALS['log']->fatal($msg);
        print $e->getMessage();
        exit(1);
      }
}


/** add/insert a new user in the database of radius manager */
function add_radius_user(&$bean)
{
    //get variables that will be stored in radius
    $username = $bean->username_c;
    $password = $bean->password_c;
    $service = $bean->service_c;
    $nr_conn = $bean->simultaneous_connections_c;
    $mac      = $bean->mac_address_c;
    $expiration_date = $bean->expiration_date_c;
    $fullname = $bean->name;
    if ($fullname=='') $fullname = $username;

    //$email    = $bean->email1;
    $email = $_REQUEST[$_REQUEST['emailAddressPrimaryFlag']];
    if ($email=='') $email = $this->default_email;

    //make sure that the username is not already in use
    if ($this->username_exists_in_sugar($username))
      {
        $msg = "<strong>The username '$username' is already used "
          . "for another client.<br/>n"
          . "Please try again with another username.</strong>";
        print $msg;
        exit(1);
      }
    if ($this->username_exists_in_radius($username))
      {
        $msg = "<strong>The username '$username' is already used in RADIUS"
          . "for another client.<br/>n"
          . "Please try again with another username.</strong>";
        print $msg;
        exit(1);
      }

    //build the field comment of radius manager
    $comment = $bean->shipping_address_street . ' -- ' . $bean->description;

    //add a new user in radius
    $query = ("call user_add('$username', '$password', '$service', '$nr_conn',"
              . " '$expiration_date', '$mac', '$fullname', "
              . "'$email', '$comment');");
    $this->radb->query($query);
}

/** update user in the database of radius manager */
function update_radius_user(&$bean, &$old_bean)
{
  //get the fields of the client
  $username = $bean->username_c;
  $password = $bean->password_c;
  $service = $bean->service_c;
  $nr_conn = $bean->simultaneous_connections_c;
  $mac      = $bean->mac_address_c;
  $expiration_date = $bean->expiration_date_c;
  $fullname = $bean->name;
  if ($fullname=='') $fullname = $username;

    //$email    = $bean->email1;
    $email = $_REQUEST[$_REQUEST['emailAddressPrimaryFlag']];
    if ($email=='') $email = $this->default_email;

    //if the old username does not exist in radius, then create it
    $old_username = $old_bean->username_c;
    if (!$this->username_exists_in_radius($old_username))
      {
        $query = "call user_add('$username','$password','$service','$nr_conn',"
          . " '$expiration_date','$mac','$fullname','$email');";
$this->radb->query($query);
      }

    //update the username if it is changed
    if ($username != $old_username)
      {
        //make sure that the new username is not already in use
        if ( $this->username_exists_in_sugar($username) or
             $this->username_exists_in_radius($username) )
          {
            //use the old username
            $username = $old_username;
            $bean->username_c = $old_username;

                 //todo: display a notification message about this problem
            }
          else
            {
                 //change the username
                 $query = "call user_change_username('$old_username', '$username')";
                 $this->radb->query($query);
           }
      }

    //update the password if it is changed
    $old_password = $old_bean->password_c;
    $password = $bean->password_c;
    if ($password != $old_password)
      {
        $query = "call user_set_password('$username', '$password')";
        $this->radb->query($query);
      }

    //build the field comment of radius manager
    $comment = $bean->shipping_address_street . ' -- ' . $bean->description;

    //update the rest of the fields
    $query = ( "call user_update('$username', '$service', '$nr_conn', "
               . "'$expiration_date', '$mac', '$fullname', "
               . "'$email', '$comment');" );
    $this->radb->query($query);
}


/**
 * Return true if the given username already exists
 * in the sugar database. Otherwise return false.
 */
function username_exists_in_sugar($username)
{
  //check in the sugar database
  $client = new Account();
  $client->retrieve_by_string_fields(array('username_c'=>$username));
  if ($client->username_c == $username) return true;

    //not found
    return false;
}


/**
 * Return true if the given username already exists
 * in the radius database. Otherwise return false.
 */
function username_exists_in_radius($username)
{
//check in the radius database
       $query = "select user_check('$username') as username";
       $result = $this->radb->query($query);
       $record = $result->fetch_object();
       if ($record->username == $username) return true;

       //not found
       return false;
   }
 }
 ?>



SugarCRM/custom/include/MysqlDB.php:

 <?php
   /**
    * This class handles the queries to a mysql database.
    */
 class MysqlDB
 {
   var $conn = false; //connection to the database

  /** constructor */
  function __construct($db_host, $db_user, $db_pass, $db_name)
  {
    //create a new connection to the database
    $this->conn = new mysqli($db_host, $db_user, $db_pass, $db_name);

       //check for errors
       if (!$this->conn)
         {
           $errno = mysqli_connect_errno();
           $error = mysqli_connect_error();
           throw new Exception("Can't connect to DB: $errno : $error");
         }
  }

  function __destruct()
  {
    $this->conn->close();
  }

  /** run a mysql query and return the result */
  function query($query)
  {
    //if there is no db connection, cannot run the query
    if (!$this->conn) return;

       //debug
       //$GLOBALS['log']->fatal('MysqlDB::query(): '.$query);

       //run the query and get the result
       $result = $this->conn->query($query);

       //check for errors
       if (!$result)
         {
           $errno = mysqli_errno($this->conn);
           $error = mysqli_error($this->conn);
           throw new Exception("Query failed: $errno : $error : $queryn");
         }
//return the result
       return $result;
   }
 }
 ?>




6 Almost Automated Configuration of MikroTik
After a MikroTik server has been installed, it can be configured quickly by doing copy/paste of the 
configuration commands. This can be done easily if we login to MikroTik using a linux terminal. 

 Note: In order to login to MikroTik through a linux terminal, the network configuration has to be 
       done manually first, right after the installation. It can be done by adding an IP address. A 
       gateway may be needed as well.

We assume here that the MikroTik servers are named: MikroTik1, MikroTik2, etc. Initially we have some 
configuration files in cfg/ , named  MikroTik1.cfg, MikroTik2.cfg, etc. After running the 
script generate-mt-setup.sh, the files  MikroTik1.mt, MikroTik2.mt, etc. will be 
generated in  setup/.


 6.1 generate­mt­setup.sh
The script generate-mt-setup.sh is used to (re)generate the setup configuration scripts for all the 
MikroTik servers: 

 #!/bin/bash
 ### generate the setup configuration scripts for all the MikroTik servers

 ./mikromik-setup.sh      cfg/MikroTik1.cfg      >   setup/MikroTik1.mt
 ./mikrotik-setup.sh      cfg/MikroTik2.cfg      >   setup/MikroTik2.mt
 ./mikrotik-setup.sh      cfg/MikroTik3.cfg      >   setup/MikroTik3.mt
 ./mikrotik-setup.sh      cfg/MikroTik4.cfg      >   setup/MikroTik4.mt
 ./mikrotik-setup.sh      cfg/MikroTik5.cfg      >   setup/MikroTik5.mt




 6.2 mikrotik­setup.sh
The script mikrotik-setup.sh is used to generate and output the MikroTik commands that are used 
to configure a MikroTik server with PPPoE service. The commands that are generated can be executed on 
the MikroTik server with copy/paste (all of them or many of them at once). The script should get an 
argument, which is the name of a file which contains configuration parameters that are specific for each 
server (for example IP, gateway, etc.). 

 #!/bin/bash
 ### This shell script generates and output the commands that are used
 ### to configure a MikroTik server with PPPoE service.
 ### The commands that are generated can be executed on the MikroTik server
 ### with copy/paste (all of them at once).

 ### get the configuration file as the first parameter
 cfgfile=${1:-MikroTik.cfg}
### include the configuration file
. $cfgfile

###   generate and output the MikroTik commands
cat   <<EOF
###   add an address on the outside (WAN) interface of the MikroTik
# /   ip address add address=$ADDRESS interface=$WAN_IF

### add a gateway
# / ip route add gateway=$GATEWAY

### set a password
# / password
#
# mt-$ID
# mt-$ID

## change the name of the server
/ system identity set name=MikroTik$ID

### set the DNS servers
/ ip dns set primary-dns=$DNS1 secondary-dns=$DNS2

### setup NAT on the outside interface of the MikroTik
/ ip firewall nat add chain=srcnat out-interface=$WAN_IF action=masquerade

### create a PPPoE service on the inner (LAN) interface of the MikroTik
/ ppp profile add name=pppoe_profile local-address=10.0.0.0
/ interface pppoe-server server add service-name=pppoe_service 
            default-profile=pppoe_profile interface=$PPPOE_IF   
            authentication=pap,chap max-sessions=450 
            one-session-per-host=no disabled=no

### add another address for connecting to the radius server
/ ip address add address=192.168.25.$ID/24 interface=$RADIUS_IF

### disable masquerading for the radius LAN (192.168.25.0/24)
/ ip firewall nat add chain=srcnat out-interface=$WAN_IF 
                      src-address=192.168.25.0/24 action=return
/ ip firewall nat move 1 0

### add radius servers for any PPP service on MikroTik
/ radius add service=ppp address=192.168.25.101 secret=passw    
             timeout=2000ms called-id=pppoe_service
/ radius add service=ppp address=192.168.25.102 secret=passw    
             timeout=2000ms called-id=pppoe_service

### tell MikroTik to use the radius servers for authentication and accounting
/ ppp aaa set use-radius=yes
/ ppp aaa set accounting=yes
/ ppp aaa set interim-update=60
/ radius incoming set accept=yes

### add some pools of about 500 IPs each
/ ip pool add name=pool0 ranges=10.0.0.0/23
/ ip pool add name=pool1 ranges=10.0.2.0/23
/ ip pool add name=pool2 ranges=10.0.4.0/23
/ ip pool add name=pool3 ranges=10.0.6.0/23
/ ip pool add name=pool4 ranges=10.0.8.0/23
/ ip pool add name=pool5 ranges=10.0.10.0/23
/ ip pool add name=pool6 ranges=10.0.12.0/23
/ ip pool add name=pool7 ranges=10.0.14.0/23
/ ip pool add name=pool8 ranges=10.0.16.0/23
/ ip pool add name=pool9 ranges=10.0.18.0/23
### logout
 / quit

 EOF




 6.3 cfg/MikroTik1.cfg
The file cfg/MikroTik1.cfg contains some configuration variables for the values that are different 
for each MikroTik that is configured. 

 ###   These configuration variables are included by the script
 ###   that generates the MikroTik setup commands. Their values
 ###   should be set according to the server where MikroTik is
 ###   installed. The ID should be a different number from the
 ###   other MikroTiks that connect to the same server radius.

 ID=1

 WAN_IF=ether1
 ADDRESS=192.168.25.21/24
 GATEWAY=192.168.25.1
 DNS1=192.168.25.11
 DNS2=4.2.2.2

 PPPOE_IF=ether2
 RADIUS_IF=ether2




 6.4 setup/MikroTik1.mt
These are the configuration commands that are generated for a MikroTik server by the script 
mikrotik-setup.sh , which takes as input the configuration variables in the file 
cfg/MikroTik1.cfg . These commands can be copy/pasted to the MikroTik prompt from a terminal. 
The commands that are commented should be given manually right after the installation of the MikroTik. 

 ### add an address on the outside (WAN) interface of the MikroTik
 # / ip address add address=192.168.25.21/24 interface=ether1

 ### add a gateway
 # / ip route add gateway=192.168.25.1

 ### set a password
 # / password
 #
 # mt-1
 # mt-1

 ## change the name of the server
 / system identity set name=MikroTik1

 ### set the DNS servers
 / ip dns set primary-dns=192.168.25.11 secondary-dns=4.2.2.2

 ### setup NAT on the outside interface of the MikroTik
 / ip firewall nat add chain=srcnat out-interface=ether1 action=masquerade
### create a PPPoE service on the inner (LAN) interface of the MikroTik
 / ppp profile add name=pppoe_profile local-address=10.0.0.0
 / interface pppoe-server server add service-name=pppoe_service 
             default-profile=pppoe_profile interface=ether2 
             authentication=pap,chap max-sessions=450 
             one-session-per-host=no disabled=no

 ### add another address for connecting to the radius server
 / ip address add address=192.168.25.1/24 interface=ether2

 ### disable masquerading for the radius LAN (192.168.25.0/24)
 / ip firewall nat add chain=srcnat out-interface=ether1 
                       src-address=192.168.25.0/24 action=return
 / ip firewall nat move 1 0

 ### add radius servers for any PPP service on MikroTik
 / radius add service=ppp address=192.168.25.101 secret=radiuspassw                    
                          timeout=2000ms called-id=pppoe_service
 / radius add service=ppp address=192.168.25.102 secret=radiuspassw                    
                          timeout=2000ms called-id=pppoe_service

 ### tell MikroTik to use the radius servers for authentication and accounting
 / ppp aaa set use-radius=yes
 / ppp aaa set accounting=yes
 / ppp aaa set interim-update=60
 / radius incoming set accept=yes

 ### add some pools of about 500 IPs each
 / ip pool add name=pool0 ranges=10.0.0.0/23
 / ip pool add name=pool1 ranges=10.0.2.0/23
 / ip pool add name=pool2 ranges=10.0.4.0/23
 / ip pool add name=pool3 ranges=10.0.6.0/23
 / ip pool add name=pool4 ranges=10.0.8.0/23
 / ip pool add name=pool5 ranges=10.0.10.0/23
 / ip pool add name=pool6 ranges=10.0.12.0/23
 / ip pool add name=pool7 ranges=10.0.14.0/23
 / ip pool add name=pool8 ranges=10.0.16.0/23
 / ip pool add name=pool9 ranges=10.0.18.0/23

 ### logout
 / quit


7 Optimize and Test the Performance
We need to know (or estimate) how much load can keep the server, how many clients it can serve without 
creating problems. For this reason, we simulate a high load by sending too many requests to the server, in 
order to see up to what point the server can bear the load. 

However, before testing the server we should optimize it so that it can have the best performance with the 
resources that are available (RAM etc.). 


 7.1 Optimize RAM
The usage of RAM by the server can be seen at the statistics generated by MRTG. It can be seen also 
using linux commands such as top , free, ps and pstree. 

   ●   The command top shows the most active processes. The command pstree displays a graphical 
       tree of processes and their children. 
●   Using the command ps I can find out the processes that consume the most amount of RAM and 
    how much uses each of them. I do it like this:  

     bash# ps -e -o vsize,comm | sort -k1 -n -r | head -n 20
     146896 mysqld
     123064 radiusd
      36412 httpd
      36392 httpd
      36380 httpd
      36380 httpd
      36376 httpd
      36128 httpd
      36128 httpd
      36120 httpd
      30200 httpd
      29600 sort
      13072 dbus-daemon
      12800 pcscd
      11040 snmpd
      10720 restorecond
       9156 sendmail
       8252 sshd
       8252 sshd
       7996 sendmail




●   The command free shows the usage of memory, how much is free etc: 

     bash# free
                  total             used         free        shared       buffers        cached
     Mem:       1026844           449416       577428             0        141572        178628
     -/+ buffers/cache:           129216       897628
     Swap:      1052248                0      1052248


    From the graph of the free memory we can notice that gradually the server uses all of the free 
    RAM that is available. Initially I thought that maybe there is some problem with memory leaking 
    from some program (e.g. mysqld). However, after googling for this problem, I found out that this 
    is something normal for a linux server: 
           The kernel will attempt to use any available memory for buffering and
           cacheing, as this makes things run faster. When applications need more
           memory, buffer and cache space is released. So the figure you need to
           look at in the "free" output is the one in the row marked "­/+
           buffers/cache:", which shows memory usage not in buffers and cache; that
           represents how much memory you're "really" using.

    See also these discussions: http://linux.derkeiler.com/Mailing­Lists/Fedora/2005­02/3149.html 
    and http://linux.derkeiler.com/Newsgroups/linux.redhat.misc/2004­01/0220.html .

●   In order to optimize the usage of RAM, stop any services that use a lot of memory and are not 
    necessary. I stopped ConsoleKit and yum­updatesd like this: 
     service ConsoleKit stop
     service ConsoleKit status
     chkconfig ConsoleKit off
     chkconfig --list ConsoleKit

     service yum-updatesd stop
     service yum-updatesd status
chkconfig yum-updatesd off
         chkconfig --list yum-updatesd




 7.2 Optimize MySQL
In order to check the performance of the mysql server and to discover any problems, we can use the tools 
mysqlreport and mysqlsla , as described in this guide: Non­technical Guide to Isolating Slow MySQL 
Queries . 

    ●   First download mysqlreport and mysqlsla from http://hackmysql.com/ and place them at /usr/
        local/bin/ . 

    ●   Then create the cron job /etc/cron.daily/mysqlreport.sh (which will send a daily 
        report by email): 

         #!/bin/bash
         ### send a report to root by email, about the performance of the mysql
         server
         /usr/local/bin/mysqlreport --all --flush-status 
                                    --user root --password sqladmin --email root


        Alternatively, this command can be executed periodically from the command line: 

         mysqlreport --all --user root --password


    ●   Add these lines in /etc/my.cnf , in order to log the slow queries: 

         ### log slow queries
         log-slow-queries
         long_query_time=1


        Then restart mysql: service mysql restart .

    ●   Finally, wait for the report (which should come daily) and check the values of Read ratio , Slow , 
        Waited , etc. (as described in http://hackmysql.com/nontech ). If there are slow queries, find out 
        which are these queries, using a command like this: 

         mysqlsla --slow /var/lib/mysql/slow_queries.log > top_10_slow_queries




In order to optimize mysql we can do these things: 

    ●   Modify the file /etc/my.cnf similarly to the following lines and then restart mysqld: 

         [mysqld]
         . . . . .
         ### options for increasing performance
         skip-name-resolve
         max_connections=500
key_buffer=128M
     table_cache=256
     read_buffer_size=1M
     read_rnd_buffer_size=4M
     myisam_sort_buffer_size=64M
     thread_cache_size=8
     query_cache_size=16M
     thread_concurrency=2
     . . . . .

     [mysqldump]
     quick
     max_allowed_packet = 16M

     [isamchk]
     key_buffer = 128M
     sort_buffer_size = 128M
     read_buffer = 2M
     write_buffer = 2M

     [myisamchk]
     key_buffer = 128M
     sort_buffer_size = 128M
     read_buffer = 2M
     write_buffer = 2M


    These values are mainly copied from the file /usr/share/mysql/my-large.cnf . See 
    also the articles in http://www.databasejournal.com/features/mysql/article.php/3367871 and http://
    www.debianhelp.co.uk/mysqlperformance.htm . 

●   As advised in /usr/local/share/doc/freeradius/tuning_guide , we convert to 
    the engine innodb the table radacct : 

     mysql> ALTER TABLE radacct ENGINE=INNODB;


●   Then, to increase the performance of the innodb engine, we also add these parameters at 
    /etc/my.cnf : 

     ### innodb options for incleasing performance
     innodb-buffer_pool_size=256M
     innodb_additional_mem_pool_size=20M
     innodb_log_file_size=64M
     innodb_log_buffer_size=8M



●   We create a multi column index for the (UserName,AcctStopTime) attributes, as advised in 
    /usr/local/share/doc/freeradius/tuning_guide : 

     mysql>   EXPLAIN SELECT UserName, AcctStopTime FROM radacct;
     mysql>   ALTER TABLE radacct ADD INDEX username_acctsstoptime
        -->   (UserName, AcctStopTime);
     mysql>   EXPLAIN SELECT UserName, AcctStopTime FROM radacct;



    Read also this article: 
    http://www.databasejournal.com/features/mysql/article.php/10897_1382791_1
7.3 Optimize FreeRADIUS
The optimization of FreeRADIUS is based on the suggestions given in the file 

/usr/local/share/doc/freeradius/tuning_guide . 

      ●   First of all we add the noatime attribute to the log files: 

           chattr -R -A /usr/local/var/log/radius/
           lsattr /usr/local/var/log/radius/


          This will decrease the number of accesses to the disk because access time of the log files will not 
          be recorded. 


      ●   Disable at all detailed logs in the configuration file 
          /usr/local/etc/raddb/radiusd.conf by commenting them out (they can be found by 
          searching for the word detail ): 

 # Log authentication requests to the log file.
 #
 # allowed values: {no, yes}
 #
 #log_auth = yes
 log_auth = no

              # Write a detailed log of all accounting records received.
              #
 #            detail {
 #                    detailfile = ${radacctdir}/%{Client-IP-Address}/detail-%Y%m%d
 #                    detailperm = 0600
 #            }

 #            detail auth_log {
 #                    detailfile = ${radacctdir}/%{Client-IP-Address}/auth-detail-%Y%m%d
 #                    detailperm = 0600
 #            }

 #            detail reply_log {
 #                    detailfile = ${radacctdir}/%{Client-IP-Address}/reply-detail-%Y%m
 %d
 #                       detailperm = 0600
 #            }

 #            detail pre_proxy_log {
 #                    detailfile = ${radacctdir}/%{Client-IP-Address}/pre-proxy-detail-
 %Y%m%d
 #                       detailperm = 0600
 #            }

 #            detail post_proxy_log {
 #                    detailfile = ${radacctdir}/%{Client-IP-Address}/post-proxy-detail-
 %Y%m%
 #                       detailperm = 0600
 #            }

 authorize {
         # If you want to have a log of authentication requests,
         # un-comment the following line, and the 'detail auth_log'
         # section, above.
#           auth_log
}

accounting {
        #
        # Create a 'detail'ed log of the packets.
        # Note that accounting requests which are proxied
        # are also logged in the detail file.
#       detail
#       daily
}

post-auth {
        #
        # If you want to have a log of authentication replies,
        # un-comment the following line, and the 'detail reply_log'
        # section, above.
#       reply_log
}

pre-proxy {
        # If you want to have a log of packets proxied to a home
        # server, un-comment the following line, and the
        # 'detail pre_proxy_log' section, above.
#       pre_proxy_log
}

post-proxy {

            # If you want to have a log of replies from a home server,
            # un-comment the following line, and the 'detail post_proxy_log'
            # section, above.
#           post_proxy_log
}



    ●   We can also comment the lines related to unix , radwtmp and radutmp in radiusd.conf . 

    ●   Increase the value of cleanup_delay and max_requests : 

# cleanup_delay: The time to wait (in seconds) before cleaning up
# a reply which was sent to the NAS.
#
# The RADIUS request is normally cached internally for a short period
# of time, after the reply is sent to the NAS. The reply packet may be
# lost in the network, and the NAS will not see it. The NAS will then
# re-send the request, and the server will respond quickly with the
# cached reply.
#
# If this value is set too low, then duplicate requests from the NAS
# MAY NOT be detected, and will instead be handled as seperate requests.
#
# If this value is set too high, then the server will cache too many
# requests, and some new requests may get blocked. (See 'max_requests'.)
#
# Useful range of values: 2 to 10
#
#cleanup_delay = 5
cleanup_delay = 10

#   max_requests: The maximum number of requests which the server keeps
#   track of. This should be 256 multiplied by the number of clients.
#   e.g. With 4 clients, this number should be 1024.
#
# If this number is too low, then when the server becomes busy,
# it will not respond to any new requests, until the 'cleanup_delay'
# time has passed, and it has removed the old requests.
#
# If this number is set too high, then the server will use a bit more
# memory for no real benefit.
#
# If you aren't sure what it should be set to, it's better to set it
# too high than too low. Setting it to 1000 per client is probably
# the highest it should be.
#
# Useful range of values: 256 to infinity
#
#max_requests = 1024
max_requests = 15000000



  ●   Turn off proxy requests: 

       #proxy_requests = yes
       #$INCLUDE ${confdir}/proxy.conf
       proxy_requests = no



  ●   Increase some values at thread pool : 

       thread pool {
               #max_servers = 32
               max_servers = 128
               #min_spare_servers = 3
               #max_spare_servers = 10
               min_spare_servers = 5
               max_spare_servers = 16
       }



  ●   Modify /usr/local/etc/raddb/sql.conf and increase the number of sql connections. It 
      should be greater than the number of the radius servers: 

                 # number of sql connections to make to server
                 #num_sql_socks = 5
                 ### this value is greater than max_servers (=128)

                 num_sql_socks = 130
                 # number of seconds to dely retrying on a failed database
                 # connection (per_socket)
                 #connect_failure_retry_delay = 60
                 connect_failure_retry_delay = 10




  ●   Finally restart radiusd : /usr/local/sbin/rc.radiusd restart
7.4 Testing the Performance
When a client tries to connect to internet, MikroTik makes an authentication/authorization request to the 
radius server. Then, after the client is connected, MikroTik sends periodically an accounting update to the 
radius server, telling how much download/upload traffic the client has done during this interval. We have 
set this interval to one minute in MikroTik. So, each minute, the radius server gets an accounting request 
for each connected client, and it writes the data to the MySQL database.

To simulate a client, we use the script radius­client.sh . It keeps running and sending update requests to 
the radius server until the stop time, then it terminates. The interval between the update requests is 5sec, 
so, the load on the server that is generated by this client will be approximately equal to the load of 10 real 
clients. 

Then we use the scrip test­radius.sh which runs many of such clients in parallel, in order to increase the 
load. The number of the clients that is generated is configurable. The script stop­test.sh can be used to 
interrupt the simulation at any time. 

From the tests that we did, we found out that in general the bottleneck of the system is the MySQL 
database. One way to increase its performance is to build a MySQL cluster, with two or more MySQL 
servers. However this requires additional resources (additional servers with big amounts of RAM). At that 
time we could still handle out clients without a MySQL cluster, so we didn't try to build it.


●   radius­client.sh

    #!/bin/bash
    ### Simulate a radius client.
    ### It will keep running and sending requests to the radius server
    ### until the stop time, then it will terminate.

    ### go to this directory
    cd $(dirname $0)

    ### get the parameters
    if [ "$2" = "" ]
    then
      echo "Usage: $0 client_id stop_time"
      exit
    fi
    id=$1
    stop_time=$2

    ### convert the stop time into seconds
    stop_time_sec=$(date --date="$stop_time" +%s)

    ### variables
    radius_server="192.168.25.101"
    secret="test"
    user="testing-radius"
    passw="test"
    interval=5
    #radclient="/usr/local/bin/radclient -x"
    radclient="/usr/local/bin/radclient"

    ### simple packages for authentication and accounting
    auth_pkg="User-Name=$user,User-Password=$passw"
    acct_pkg_start="User-Name=$user,Acct-Session-Id=$id,Acct-Status-Type=Start"
    acct_pkg_stop="User-Name=$user,Acct-Session-Id=$id,Acct-Status-Type=Stop"
acct_pkg_update="
        User-Name=$user,
        Acct-Session-Id=$id,
        Acct-Status-Type=Interim-Update,
        Service-Type=Framed-User,
        Framed-Protocol=PPP,
        User-Name=$user ,
        Calling-Station-Id=00:11:43:9f:90:53,
        Called-Station-Id=Test,
        NAS-Port-Id=ether2,
        Framed-IP-Address="10.2.255.255",
        Acct-Authentic=RADIUS,
        Acct-Session-Time=5461,
        Acct-Input-Octets=974290,
        Acct-Input-Gigawords=0,
        Acct-Input-Packets=6458,
        Acct-Output-Octets=3578820,
        Acct-Output-Gigawords=0,
        Acct-Output-Packets=6338,
        NAS-Identifier=TEST,
        Acct-Delay-Time=0,
        Client-IP-Address=192.168.25.22"

    ### start the connection
    #echo $acct_pkg_stop | $radclient $radius_server acct $secret
    echo $auth_pkg | $radclient $radius_server auth $secret
    echo $acct_pkg_start | $radclient $radius_server acct $secret

    ### send update account packages periodically until stop time
    curr_time_sec=$(date +%s)
    #echo $curr_time_sec ## debug
    while [ $curr_time_sec -lt $stop_time_sec ]
    do
      echo $acct_pkg_update | $radclient $radius_server acct $secret
      sleep $interval
      curr_time_sec=$(date +%s)
      #echo $curr_time_sec ## debug
    done

    ### stop the connection
    echo $acct_pkg_stop | $radclient $radius_server acct $secret


●   test­radius.sh

    #!/bin/bash
    ### test the resources of the radius server by starting
    ### many clients and sending a lot of request packages to it

    ### go to this directory
    cd $(dirname $0)

    ### get the parameters of the script
    if [ "$2" = "" ]
    then
      echo "Usage: $0 nr_clients stop_time"
      exit
    fi
    nr_clients=$1
    stop_time=$2

    for (( i=0 ; i < $nr_clients ; i++ ))
    do
      id=$(expr $i + 1)
      echo ./radius-client.sh $id $stop_time
./radius-client.sh $id $stop_time &
      sleep 1
    done


●   stop­test.sh

    #!/bin/bash
    ### stop the test by killing the processes

    pslist=$(ps ax | grep radius-client.sh | gawk '{print $1}')
    for ps in $pslist
    do
      kill -9 $ps
    done


8 Making Backups
Backups are stored in the directory /backup/ on the radius server. Some of them are generated 
periodically by cron jobs, some of them manually. They can be downloaded from 
http://192.168.25.101/backup/ in order to write them in a CD or DVD. They are made accessible via http 
by the configuration file /etc/httpd/conf.d/backup.conf, which has this content: 

    Alias /backup /backup
    <Directory "/backup">
      Options +Indexes
      order deny,allow
      deny from all
      allow from 127.0.0.1
      allow from 192.168.25.0/24
    </Directory>




 8.1 MikroTik
Binary backups of the MikroTik servers can be done by the script 
/backup/mikrotik/backup.sh . It takes automatically backups from the MikroTik servers. In 
case that a MikroTik server needs to be reinstalled for some reason, its configuration can be restored 
easily from the backup. First the backup is uploaded by FTP on the MikroTik server, then it is restored 
using the command " / system backup save name=filename.backup " . 

    #!/bin/bash
    ### Get binary backups of all the MikroTik servers.
    ### This script can be executed manually or periodicly (by a cron job).
    ### However, since the configuration of the MikroTik servers is standard
    ### and does not change over the time (because all the data of the clients
    ### are stored in radius), there is no need to make the backup periodically.

    ### mtexec.exp is a script that can execute a MikroTik command remotely
    ### it requires expect (install it with `yum install expect`)
    exec_mt_cmd=/usr/local/bin/mtexec.exp

    ### Make a backup of the MikroTik router and get it here.
    ### Gets these parameters: server_ip passwd filename
    function get_backup
    {
      # get the parameters
server_ip=$1
     passwd=$2
     filename=$3

     # make a binary backup of the MikroTik server
     $exec_mt_cmd "$passwd" "$server_ip" "/ system backup save name=MikroTik.backup"

     # get it here
     wget "ftp://admin:$passwd@$server_ip/MikroTik.backup"

     # copy it to the given destination filename
     mv MikroTik.backup $filename
 }

 ### get the backup of all the MikroTik servers
 get_backup 192.168.25.101 "mt-1" backups/MikroTik1.backup
 get_backup 192.168.25.102 "mt-2" backups/MikroTik2.backup
 get_backup 192.168.25.103 "mt-3" backups/MikroTik3.backup
 #get_backup 192.168.25.104 "mt-4" backups/MikroTik4.backup
 #get_backup 192.168.25.105 "mt-5" backups/MikroTik5.backup



The script /usr/local/bin/mtexec.exp is used to execute a MikroTik command remotely (see 
the section Executing MikroTik Commands From a Script  below). 


 8.2 Database
A backup of the database of radius is made each night, however the backups older than a week are deleted 
automatically in order to free disk space. They can be downloaded from 
http://192.168.25.101/backup/radius_db/backups/ (by a script that uses wget, or manually) and then 
optionally can be written to another backup media (such as CD or DVD). 

The cron script that does this is /etc/cron.daily/backup-db.cron which is actually a symbolic 
link to /backup/radius_db/backup-db.cron . It just calls the script that makes the backup: 

 #!/bin/bash
 ### Make a backup of the database daily.
 ### It should be linked to cron.daily like this:
 ###   ln -s /backup/radius_db/backup-db.cron /etc/cron.daily/

 /backup/radius_db/backup-db.sh


The script /backup/radius_db/backup-db.sh makes a full backup of the database and cleans 
any backups that are older than a week: 

 #!/bin/bash
 ### This script makes a full backup of the database.
 ### It should be called periodically from a cron job.
 ###
 ### To restore, first unzip the backup file, and then use the command:
 ### bash$ mysql -p -u root < radiusdb.2007-10-27.sql

 ### go to this directory
 cd $(dirname $0)

 ### create a filename for the backup, which contains the date
 filename="backups/radiusdb.$(/bin/date +%Y-%m-%d).sql"
### backup all the databases
    /usr/bin/mysqldump --user=root --password=sqladmin --all-databases --flush-logs 
                       --lock-all-tables --force --master-data=2 > $filename
    gzip -f $filename

    ### find and clean any backup files older than a week
    find backups/ -ctime +7 | xargs rm -f



To restore a backup file, first unzip it, and then use the command: 

    bash$ mysql -p -u root < radiusdb.2007-10-27.sql




 8.3 Server
A backup of the data and configurations of the radius server can be done by the script 
/backup/radius_server/backup.sh . There is no need to do this backup periodically or 
automatically, it can be done manually whenever any modifications on the configuration are done. The 
files and directories that are backup­ed by the script are those that are listed in the file 
/backup/backup_server/backup-list.txt . 

The created backup archive can be retrieved remotely from http://192.168.25.101/backup/radius_server/ 
in order to store it to another backup media (such as CD, DVD, etc.). This backup can be used to build 
quickly another radius server that is almost identical as the original one (or to rebuild it in case of failure). 


●   backup/radius_server/backup_server.sh

The script /backup/radius_server/backup_server.sh is used to backup the data and 
configurations of the server: 

    #!/bin/bash
    ### Backup the data and configurations in the radius server.
    ### In case of failure, we should be able to setup the server by just
    ### installing fedora7 and restoring the backup.

    ### go to this directory
    cd $(dirname $0)

    ### get a current list of the installed packages
    #rpm -qa | sort > pkglist.txt
    #rpm -qa | sed 's/-[0-9].*//' | sort > pkglist_1.txt
    yum list installed > pkglist.txt
    yum list installed | gawk '{print $1}' | sed 's/.[^.]*$//' > pkglist_1.txt

    ### create a backup of the config files that are listed in 'backup-list.txt'
    echo radius-server-config.tgz
    tar --create --gzip --preserve 
        --files-from="backup-list.txt" --file radius-server-config.tgz

    ### create a backup of the directories /var/www/html/, /usr/local/, etc.
    echo radius-server-html.tgz
    tar cz --preserve --file radius-server-html.tgz /var/www/html/
    echo radius-server-local.tgz
tar cz --preserve --file radius-server-local.tgz               /usr/local/



●   backup/backup_server/backup­list.txt

The file /backup/backup_server/backup-list.txt is a list of the files and directories that 
should be backup­ed: 

    /etc/radiusmanager.cfg
    /var/www/html/radiusmanager/lic.txt
    /var/www/html/radiusmanager/config/config.php
    /etc/phpMyAdmin/config.inc.php
    /etc/my.cnf
    /etc/crontab
    /etc/cron.daily/backup-db.cron
    /etc/cron.daily/check-files.sh
    /etc/cron.monthly/backup-server.sh
    /etc/sysconfig/iptables
    /etc/resolv.conf
    /etc/aliases
    /home/dhoxha/.procmailrc
    /etc/rc.d/rc.local
    /etc/php.ini
    /etc/mail/sendmail.mc
    /etc/mail/sendmail.cf
    /etc/ntp.conf
    /etc/httpd/conf/httpd.conf
    /etc/httpd/conf.d/backup.conf
    /etc/httpd/conf.d/phpMyAdmin.conf
    /etc/httpd/conf.d/radiusmanager.conf
    /backup/mikrotik/backup.sh
    /backup/radius_db/backup-db.sh
    /backup/radius_db/backup-db.cron
    /backup/radius_server/backup-list.txt
    /backup/radius_server/pkglist.txt
    /backup/radius_server/pkglist_1.txt
    /backup/radius_server/backup-server.sh
    /usr/local/config/
    /usr/local/etc/raddb/
    /usr/local/bin/mtexec.exp
    /usr/local/scripts/




 8.4 Rebuild the Server From Backup
The backup radius_server.tgz can be used to build quickly another radius server that is almost 
identical as the original one. It can be done like this: 

      1. Initially install Fedora7 from CD/DVD. Install a system that is as small as possible (a minimal 
         system, no need for GUI etc., just the base system). 

      2. Transfer the backup file on the new system, using scp , wget or anything else. 

      3. Make sure that in this system are installed almost the same packages as in the original server. It 
         can be done like this: 

             a. First update the packages that are already installed: 
bash# yum update


            b. Then extract the pkglist_1.txt from the backup archive: 

                 bash# tar xfz radius-server.tgz backup/radius_server/pkglist_1.txt


            c. Next, make sure that all packages in pkglist_1.txt are installed in the server: 

                 bash# cat backup/radius_server/pkglist_1.txt | xargs yum install


            d. If you want, you can also make sure that no extra packages (more than those on the 
               original server) are installed. It can be done by generating a list of the installed packages 
               (using yum list installed ), comparing it to the file pkglist_1.txt (using diff ), 
               finding out which are the extra packages and removing them: 

                 bash# yum list installed | gawk '{print $1}' | sed 's/. [^.] *$//' 
                           >package_list.txt
                 bash# diff -u package_list.txt pkglist_1.txt > pkg.diff
                 bash# yum remove ...


               However, extra packages in general should not harm anything. 

    4. Now restore the backup file: 

         bash# cd /
         bash# tar xvz --preserve --file radius_server.tgz



    5. Don't forget to restore the database of radius manager. 

    6. Also make sure that the needed services are ON (like httpd, mysqld, etc.), and those that are not 
       needed are OFF. This can be done using the commands /sbin/chkconfig and /sbin/service . 




9 Misc

 9.1 Keeping the Time Correct
It is important that the time of the server be correct, since radius manager checks the expiration times of 
the clients. In order to make sure that the time of the server is correct, we can use ntp. It can be installed 
and configured like this: 

    1. Install the package ntp : 

         bash# yum install ntp


    2. Modify /etc/ntp.conf by adding time servers: 
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius

Weitere ähnliche Inhalte

Was ist angesagt?

OSPF On Router OS7
OSPF On Router OS7OSPF On Router OS7
OSPF On Router OS7GLC Networks
 
VXLAN and FRRouting
VXLAN and FRRoutingVXLAN and FRRouting
VXLAN and FRRoutingFaisal Reza
 
BGP on RouterOS7 -Part 1
BGP on RouterOS7 -Part 1BGP on RouterOS7 -Part 1
BGP on RouterOS7 -Part 1GLC Networks
 
MikroTik MTCNA
MikroTik MTCNAMikroTik MTCNA
MikroTik MTCNAAli Layth
 
Mikrotik Load Balancing with PCC
Mikrotik Load Balancing with PCCMikrotik Load Balancing with PCC
Mikrotik Load Balancing with PCCGLC Networks
 
Basic command to configure mikrotik
Basic command to configure mikrotikBasic command to configure mikrotik
Basic command to configure mikrotikTola LENG
 
Layer 7 Firewall on Mikrotik
Layer 7 Firewall on MikrotikLayer 7 Firewall on Mikrotik
Layer 7 Firewall on MikrotikGLC Networks
 
MPLS on Router OS V7 - Part 1
MPLS on Router OS V7 - Part 1MPLS on Router OS V7 - Part 1
MPLS on Router OS V7 - Part 1GLC Networks
 
Mikrotik IP Settings For Performance and Security
Mikrotik IP Settings For Performance and SecurityMikrotik IP Settings For Performance and Security
Mikrotik IP Settings For Performance and SecurityGLC Networks
 
Network LACP/Bonding/Teaming with Mikrotik
Network LACP/Bonding/Teaming with MikrotikNetwork LACP/Bonding/Teaming with Mikrotik
Network LACP/Bonding/Teaming with MikrotikGLC Networks
 
MikroTik Security
MikroTik SecurityMikroTik Security
MikroTik SecurityRofiq Fauzi
 
MTCNA - MikroTik Certified Network Associate - v2
MTCNA - MikroTik Certified Network Associate - v2MTCNA - MikroTik Certified Network Associate - v2
MTCNA - MikroTik Certified Network Associate - v2Yaser Rahmati
 
BGP tuning: Peer with loopback
BGP tuning: Peer with loopbackBGP tuning: Peer with loopback
BGP tuning: Peer with loopbackGLC Networks
 
Connection load balancing with mikrotik [workshop]
Connection load balancing with mikrotik [workshop]Connection load balancing with mikrotik [workshop]
Connection load balancing with mikrotik [workshop]Achmad Mardiansyah
 
Mikrotik Hotspot With Queue Tree BW Management
Mikrotik Hotspot With Queue Tree BW ManagementMikrotik Hotspot With Queue Tree BW Management
Mikrotik Hotspot With Queue Tree BW Managementgopartheredbuff
 
Build enterprise wireless with CAPsMAN
Build enterprise wireless with CAPsMANBuild enterprise wireless with CAPsMAN
Build enterprise wireless with CAPsMANGLC Networks
 

Was ist angesagt? (20)

OSPF On Router OS7
OSPF On Router OS7OSPF On Router OS7
OSPF On Router OS7
 
VXLAN and FRRouting
VXLAN and FRRoutingVXLAN and FRRouting
VXLAN and FRRouting
 
BGP on RouterOS7 -Part 1
BGP on RouterOS7 -Part 1BGP on RouterOS7 -Part 1
BGP on RouterOS7 -Part 1
 
MikroTik MTCNA
MikroTik MTCNAMikroTik MTCNA
MikroTik MTCNA
 
MikroTik Firewall : Securing your Router with Port Knocking
MikroTik Firewall : Securing your Router with Port KnockingMikroTik Firewall : Securing your Router with Port Knocking
MikroTik Firewall : Securing your Router with Port Knocking
 
Mikrotik Load Balancing with PCC
Mikrotik Load Balancing with PCCMikrotik Load Balancing with PCC
Mikrotik Load Balancing with PCC
 
Basic command to configure mikrotik
Basic command to configure mikrotikBasic command to configure mikrotik
Basic command to configure mikrotik
 
MTCNA
MTCNAMTCNA
MTCNA
 
Layer 7 Firewall on Mikrotik
Layer 7 Firewall on MikrotikLayer 7 Firewall on Mikrotik
Layer 7 Firewall on Mikrotik
 
MPLS on Router OS V7 - Part 1
MPLS on Router OS V7 - Part 1MPLS on Router OS V7 - Part 1
MPLS on Router OS V7 - Part 1
 
Mikrotik IP Settings For Performance and Security
Mikrotik IP Settings For Performance and SecurityMikrotik IP Settings For Performance and Security
Mikrotik IP Settings For Performance and Security
 
Network LACP/Bonding/Teaming with Mikrotik
Network LACP/Bonding/Teaming with MikrotikNetwork LACP/Bonding/Teaming with Mikrotik
Network LACP/Bonding/Teaming with Mikrotik
 
MikroTik Security
MikroTik SecurityMikroTik Security
MikroTik Security
 
MTCNA - MikroTik Certified Network Associate - v2
MTCNA - MikroTik Certified Network Associate - v2MTCNA - MikroTik Certified Network Associate - v2
MTCNA - MikroTik Certified Network Associate - v2
 
BGP tuning: Peer with loopback
BGP tuning: Peer with loopbackBGP tuning: Peer with loopback
BGP tuning: Peer with loopback
 
Connection load balancing with mikrotik [workshop]
Connection load balancing with mikrotik [workshop]Connection load balancing with mikrotik [workshop]
Connection load balancing with mikrotik [workshop]
 
Mikrotik Hotspot With Queue Tree BW Management
Mikrotik Hotspot With Queue Tree BW ManagementMikrotik Hotspot With Queue Tree BW Management
Mikrotik Hotspot With Queue Tree BW Management
 
macvlan and ipvlan
macvlan and ipvlanmacvlan and ipvlan
macvlan and ipvlan
 
Mikrotik firewall filter
Mikrotik firewall filterMikrotik firewall filter
Mikrotik firewall filter
 
Build enterprise wireless with CAPsMAN
Build enterprise wireless with CAPsMANBuild enterprise wireless with CAPsMAN
Build enterprise wireless with CAPsMAN
 

Ähnlich wie PPPoE With Mikrotik and Radius

Tutorial mikrotik step by step anung muhandanu
Tutorial mikrotik step by step  anung muhandanu Tutorial mikrotik step by step  anung muhandanu
Tutorial mikrotik step by step anung muhandanu Alessandro De Suoodh
 
Air Live Rs 1200
Air Live Rs 1200Air Live Rs 1200
Air Live Rs 1200guest52b3f5
 
26.1.7 lab snort and firewall rules
26.1.7 lab   snort and firewall rules26.1.7 lab   snort and firewall rules
26.1.7 lab snort and firewall rulesFreddy Buenaño
 
Tutorial mikrotik step by step anung muhandanu
Tutorial mikrotik step by step  anung muhandanu Tutorial mikrotik step by step  anung muhandanu
Tutorial mikrotik step by step anung muhandanu theviper0308
 
Web Server(Apache),
Web Server(Apache), Web Server(Apache),
Web Server(Apache), webhostingguy
 
Web Server(Apache),
Web Server(Apache), Web Server(Apache),
Web Server(Apache), webhostingguy
 
Crossfire DDoS Protection
Crossfire DDoS ProtectionCrossfire DDoS Protection
Crossfire DDoS ProtectionFarjad Noor
 
MySQL Connectors 8.0.19 & DNS SRV
MySQL Connectors 8.0.19 & DNS SRVMySQL Connectors 8.0.19 & DNS SRV
MySQL Connectors 8.0.19 & DNS SRVKenny Gryp
 
Basic Cisco ASA 5506-x Configuration (Firepower)
Basic Cisco ASA 5506-x Configuration (Firepower)Basic Cisco ASA 5506-x Configuration (Firepower)
Basic Cisco ASA 5506-x Configuration (Firepower)NetProtocol Xpert
 
FreeBSD, ipfw and OpenVPN 2.1 server
FreeBSD, ipfw and OpenVPN 2.1 serverFreeBSD, ipfw and OpenVPN 2.1 server
FreeBSD, ipfw and OpenVPN 2.1 serverTomaz Muraus
 
Load Balancer Device and Configurations.
Load Balancer Device and Configurations.Load Balancer Device and Configurations.
Load Balancer Device and Configurations.Web Werks Data Centers
 
Oracle Real Application Cluster ( RAC )
Oracle Real Application Cluster ( RAC )Oracle Real Application Cluster ( RAC )
Oracle Real Application Cluster ( RAC )varasteh65
 
Free radius billing server with practical vpn exmaple
Free radius billing server with practical vpn exmapleFree radius billing server with practical vpn exmaple
Free radius billing server with practical vpn exmapleChanaka Lasantha
 
Cisco asa firewall command line technical guide
Cisco asa firewall command line technical guideCisco asa firewall command line technical guide
Cisco asa firewall command line technical guideMDEMARCOCCIE
 
Squid proxy server
Squid proxy serverSquid proxy server
Squid proxy serverGreen Jb
 
Network Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage Service
Network Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage ServiceNetwork Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage Service
Network Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage ServiceCloudian
 

Ähnlich wie PPPoE With Mikrotik and Radius (20)

Tutorial mikrotik step by step
Tutorial mikrotik step by stepTutorial mikrotik step by step
Tutorial mikrotik step by step
 
Tutorial mikrotik step by step anung muhandanu
Tutorial mikrotik step by step  anung muhandanu Tutorial mikrotik step by step  anung muhandanu
Tutorial mikrotik step by step anung muhandanu
 
Air Live Rs 1200
Air Live Rs 1200Air Live Rs 1200
Air Live Rs 1200
 
Network Testing ques
Network Testing quesNetwork Testing ques
Network Testing ques
 
26.1.7 lab snort and firewall rules
26.1.7 lab   snort and firewall rules26.1.7 lab   snort and firewall rules
26.1.7 lab snort and firewall rules
 
Tutorial mikrotik step by step anung muhandanu
Tutorial mikrotik step by step  anung muhandanu Tutorial mikrotik step by step  anung muhandanu
Tutorial mikrotik step by step anung muhandanu
 
Web Server(Apache),
Web Server(Apache), Web Server(Apache),
Web Server(Apache),
 
Web Server(Apache),
Web Server(Apache), Web Server(Apache),
Web Server(Apache),
 
Crossfire DDoS Protection
Crossfire DDoS ProtectionCrossfire DDoS Protection
Crossfire DDoS Protection
 
MySQL Connectors 8.0.19 & DNS SRV
MySQL Connectors 8.0.19 & DNS SRVMySQL Connectors 8.0.19 & DNS SRV
MySQL Connectors 8.0.19 & DNS SRV
 
Basic Cisco ASA 5506-x Configuration (Firepower)
Basic Cisco ASA 5506-x Configuration (Firepower)Basic Cisco ASA 5506-x Configuration (Firepower)
Basic Cisco ASA 5506-x Configuration (Firepower)
 
FreeBSD, ipfw and OpenVPN 2.1 server
FreeBSD, ipfw and OpenVPN 2.1 serverFreeBSD, ipfw and OpenVPN 2.1 server
FreeBSD, ipfw and OpenVPN 2.1 server
 
Load Balancer Device and Configurations.
Load Balancer Device and Configurations.Load Balancer Device and Configurations.
Load Balancer Device and Configurations.
 
Oracle Real Application Cluster ( RAC )
Oracle Real Application Cluster ( RAC )Oracle Real Application Cluster ( RAC )
Oracle Real Application Cluster ( RAC )
 
Free radius billing server with practical vpn exmaple
Free radius billing server with practical vpn exmapleFree radius billing server with practical vpn exmaple
Free radius billing server with practical vpn exmaple
 
Cisco asa firewall command line technical guide
Cisco asa firewall command line technical guideCisco asa firewall command line technical guide
Cisco asa firewall command line technical guide
 
KrakenD API Gateway
KrakenD API GatewayKrakenD API Gateway
KrakenD API Gateway
 
Squid proxy server
Squid proxy serverSquid proxy server
Squid proxy server
 
Network Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage Service
Network Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage ServiceNetwork Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage Service
Network Setup Guide: Deploying Your Cloudian HyperStore Hybrid Storage Service
 
FreeBSD and Hardening Web Server
FreeBSD and Hardening Web ServerFreeBSD and Hardening Web Server
FreeBSD and Hardening Web Server
 

Mehr von Dashamir Hoxha

Easy Blogging With Emacs -- Cheatsheet
Easy Blogging With Emacs -- CheatsheetEasy Blogging With Emacs -- Cheatsheet
Easy Blogging With Emacs -- CheatsheetDashamir Hoxha
 
Autobiography of Benjamin Franklin
Autobiography of Benjamin FranklinAutobiography of Benjamin Franklin
Autobiography of Benjamin FranklinDashamir Hoxha
 
Easy Blogging With Emacs
Easy Blogging With EmacsEasy Blogging With Emacs
Easy Blogging With EmacsDashamir Hoxha
 
Easy Blogging With Emacs
Easy Blogging With EmacsEasy Blogging With Emacs
Easy Blogging With EmacsDashamir Hoxha
 
Development Setup of B-Translator
Development Setup of B-TranslatorDevelopment Setup of B-Translator
Development Setup of B-TranslatorDashamir Hoxha
 
Using Drupal Features in B-Translator
Using Drupal Features in B-TranslatorUsing Drupal Features in B-Translator
Using Drupal Features in B-TranslatorDashamir Hoxha
 
IT Strategy of Albanian Customs
IT Strategy of Albanian CustomsIT Strategy of Albanian Customs
IT Strategy of Albanian CustomsDashamir Hoxha
 
Strategjia për IT-në e Doganës Shqiptare
Strategjia për IT-në e Doganës ShqiptareStrategjia për IT-në e Doganës Shqiptare
Strategjia për IT-në e Doganës ShqiptareDashamir Hoxha
 
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as Service
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as ServiceCertificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as Service
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as ServiceDashamir Hoxha
 
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...Dashamir Hoxha
 
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...Dashamir Hoxha
 
Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...
Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...
Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...Dashamir Hoxha
 
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and RadiusPPPoE With Mikrotik and Radius
PPPoE With Mikrotik and RadiusDashamir Hoxha
 
Using OpenFire With OpenLDAP
Using OpenFire With OpenLDAPUsing OpenFire With OpenLDAP
Using OpenFire With OpenLDAPDashamir Hoxha
 
phpWebApp presentation
phpWebApp presentationphpWebApp presentation
phpWebApp presentationDashamir Hoxha
 
Managing HotSpot Clients With FreeRadius
Managing HotSpot Clients With FreeRadiusManaging HotSpot Clients With FreeRadius
Managing HotSpot Clients With FreeRadiusDashamir Hoxha
 
The Digital Signature and the X.509/OpenPGP Authentication Models
The Digital Signature and the X.509/OpenPGP Authentication ModelsThe Digital Signature and the X.509/OpenPGP Authentication Models
The Digital Signature and the X.509/OpenPGP Authentication ModelsDashamir Hoxha
 
Building a Gateway Server
Building a Gateway ServerBuilding a Gateway Server
Building a Gateway ServerDashamir Hoxha
 

Mehr von Dashamir Hoxha (20)

Easy Blogging With Emacs -- Cheatsheet
Easy Blogging With Emacs -- CheatsheetEasy Blogging With Emacs -- Cheatsheet
Easy Blogging With Emacs -- Cheatsheet
 
Autobiography of Benjamin Franklin
Autobiography of Benjamin FranklinAutobiography of Benjamin Franklin
Autobiography of Benjamin Franklin
 
Easy Blogging With Emacs
Easy Blogging With EmacsEasy Blogging With Emacs
Easy Blogging With Emacs
 
Easy Blogging With Emacs
Easy Blogging With EmacsEasy Blogging With Emacs
Easy Blogging With Emacs
 
Development Setup of B-Translator
Development Setup of B-TranslatorDevelopment Setup of B-Translator
Development Setup of B-Translator
 
Using Drupal Features in B-Translator
Using Drupal Features in B-TranslatorUsing Drupal Features in B-Translator
Using Drupal Features in B-Translator
 
IT Strategy of Albanian Customs
IT Strategy of Albanian CustomsIT Strategy of Albanian Customs
IT Strategy of Albanian Customs
 
Strategjia për IT-në e Doganës Shqiptare
Strategjia për IT-në e Doganës ShqiptareStrategjia për IT-në e Doganës Shqiptare
Strategjia për IT-në e Doganës Shqiptare
 
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as Service
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as ServiceCertificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as Service
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.1x Software as Service
 
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS188.1x Artificial Intelli...
 
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...
Certificate -- Dashamir Hoxha -- edX/BerkeleyX -- CS169.2x Advanced Software ...
 
Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...
Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...
Certificate -- Dashamir Hoxha -- edX/MITx -- 6.00x Introduction to Computer S...
 
Udhëzuesi i Kturtle
Udhëzuesi i KturtleUdhëzuesi i Kturtle
Udhëzuesi i Kturtle
 
PPPoE With Mikrotik and Radius
PPPoE With Mikrotik and RadiusPPPoE With Mikrotik and Radius
PPPoE With Mikrotik and Radius
 
Using OpenFire With OpenLDAP
Using OpenFire With OpenLDAPUsing OpenFire With OpenLDAP
Using OpenFire With OpenLDAP
 
phpWebApp presentation
phpWebApp presentationphpWebApp presentation
phpWebApp presentation
 
phpWebApp article
phpWebApp articlephpWebApp article
phpWebApp article
 
Managing HotSpot Clients With FreeRadius
Managing HotSpot Clients With FreeRadiusManaging HotSpot Clients With FreeRadius
Managing HotSpot Clients With FreeRadius
 
The Digital Signature and the X.509/OpenPGP Authentication Models
The Digital Signature and the X.509/OpenPGP Authentication ModelsThe Digital Signature and the X.509/OpenPGP Authentication Models
The Digital Signature and the X.509/OpenPGP Authentication Models
 
Building a Gateway Server
Building a Gateway ServerBuilding a Gateway Server
Building a Gateway Server
 

PPPoE With Mikrotik and Radius

  • 1. Managing Internet Connections  With  PPPoE, MikroTik and Radius           Dashamir Hoxha      <dashohoxha@gmail.com>           Artur Nurja                <tatanka@albaniaonline.net> Copyright (C) 2009 Dashamir Hoxha, Artur Nurja. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License." 1 Abstract This article will describe how to manage internet clients of an ISP with PPPoE and MikroTik. For  centralized AAA (Authentication, Authorization and Accounting), freeRadius is used. This article is  based on the work that we have done at AlbaniaOnline ISP (now called Primo). 2 Introduction The aim of the project was to give PPPoE service to the internet clients, through MikroTik. This is not  difficult and can be implemented easily, however we would like to use freeRadius for AAA, so that we  could keep the data of the clients and their settings in a database. MikroTik should be just a dummy box  with a simple configuration, so that we can throw it away and replace it by another one at anytime. All the  client data (username, password, connection settings etc.) should be stored in the freeRadius database. If  they are in a database the client data can be managed easily by an external application as well (by  integrating the freeRadius database with the application).  2.1 Observations About Implementation Possibilities Digging into the manual pages of MikroTik, freeRadius, etc. and making experiments, we made these  observations about the different ways a PPPoE service can be implemented with MikroTik and Radius: ● There can be more than one MikroTik servers in a LAN. ● In a MikroTik server we can run more than one PPPoE service (with different names and  profiles). ● Each of the PPPoE services can authenticate the internet users using one or more Radius servers. ● The MikroTik consults the radius server about authenticating a PPPoE user only if this user is not  in his local database.  ● A freeRadius server can use one or more MySQL databases for authenticating a user. If it cannot 
  • 2. connect to one database, then it switches to another. ● A freeRadius server can act as proxy, getting the answer from another radius server. It can use  more than one radius server for getting the answer and can do even load­balancing between them,  in case that they are the same (are used for the same thing).  ● The attributes of the Internet connection of a client (IP, GW, DNS, bandwidth, etc.) can be defined  in the radius database or in the profile of the client in MikroTik. ● In the freeRadius database, the connection attributes can be set for each client or for the group to  which the client belongs. ● IP­s that are assigned to the clients can be random (from an IP pool) or each client can be assigned  a fixed IP, always the same IP no matter to which MikroTik it is connected. ● The MikroTik gateway server can connect the clients to the Internet using IP­proxy (NAT) or  using ARP­proxy. 2.2 The Architecture of The PPPoE Service Based on the above observations and on the project goals, we decided to construct the PPPoE service  infrastructure like this: ● For client authentication we should use 2 freeRadius servers. Each of them will query its own  MySQL database and these databases will replicate with each­other (one of them as primary and  the other one as secondary).  The advantages of using two radius servers instead of one, are these: ● Half of the requests will be handled by one and half will be handled by the other, so,  we will have load­balancing. ● If one of them is not working properly, the other one will handle all the requests and  the service will not be interrupted. This provides high availability of the service (the  service is more stable). ● They also serve as a backup of each­other. If the database of one of them is damaged,  the data can be restored from the other. ● The external application that keeps  the client data will be connected to the radius database (only  to the primary one) and will manage the client  data there. ● To manage the client connections, 3­4 or more MikroTik servers can be used. Each of them has  the same configuration and they are connected in the same hub/switch with the clients. Since the  clients connect randomly to each of them, we are going to have load­balancing between the  MikroTik servers. In general, each of the MikroTik servers can serve up to 400­500 clients  simultaneously.  ● MikroTik gateways will authenticate the clients by asking the radius servers. Each of them will  have two radius servers in its configuration. Half of them ask first radius1 and if they get no  answer they ask radius2. The other half ask first radius2. This provides load­balancing for the  radius servers.
  • 3. The clients are connected to the Internet through IP­proxy (NAT). ● The IP that is assigned to the clients are random, taken from an IP pool (they are not fixed IP­s). 3 Installing And Configuring freeRadius, MySQL and MikroTik 3.1 Installing freeRadius We installed freeRadius on Fedora7. First I installed the packages freeradius and freeradius­mysql :  bash# yum install freeradius freeradius-mysql Then I enabled the service radiusd and started it:  bash# /sbin/chkconfig --list radiusd bash# /sbin/chkconfig radiusd on bash# /sbin/chkconfig --list radiusd bash# /sbin/service radiusd start Since freeradius uses the ports 1812 and 1813 (see e.g. the file /etc/services ), I had to open these  ports in the firewall, both for tcp and udp . In order to do this, I edited the file  /etc/sysconfig/iptables and added there these lines:  -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 1812 -j ACCEPT -A RH-Firewall-1-INPUT -m udp -p udp --dport 1812 -j ACCEPT -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 1813 -j ACCEPT -A RH-Firewall-1-INPUT -m udp -p udp --dport 1813 -j ACCEPT To apply these modifications in firewall, I restarted the service iptables:  bash# /sbin/service iptables restart Tip: To check that the ports 1812 and 1813 are open in the firewall, we can use one  of these commands:  bash# /sbin/service iptables status | grep 1812 bash# /sbin/iptables-save | grep 1812 3.2 Testing freeRadius Installation Just to test that freeRadius is correctly installed and works, we can make a simple configuration using the  standard text files, like this:   Edit the file /etc/raddb/clients.conf . At the section client 127.0.0.1 modify the value  of secret , for example make it local1 . The entry  client 127.0.0.1
  • 4. { . . . } will allow the localhost to use the radius service.   Edit the file /etc/raddb/users . Uncomment there the test user steve (or create another  user with similar details). It should look like this:  steve Cleartext-Password := "testing" Service-Type = Framed-User, Framed-Protocol = PPP, Framed-IP-Address = 172.16.3.33, Framed-IP-Netmask = 255.255.255.0, Framed-Routing = Broadcast-Listen, Framed-Filter-Id = "std.ppp", Framed-MTU = 1500, Framed-Compression = Van-Jacobsen-TCP-IP  Edit /etc/raddb/radiusd.conf and make sure that authorization using files is enabled. (It  should be enabled by default, so in general you don't need to modify anything.)  Now we can use the command radtest to request access for user steve with password testing :  bash# radtest --help bash# radtest steve testing 127.0.0.1 10 local1 bash# radtest steve testing localhost 10 local1 bash# radtest steve testingX 127.0.0.1 10 local1 bash# radtest steve testing 127.0.0.1 10 local1X In the first and second tests you should get the answer 'Access­Accept'. In the last two tests you should  get the answer 'Access­Reject'.  Tip: In order to get more details about what happens in the server, run radiusd in debug mode.  First stop the service: /sbin/service radiusd stop , then run it like this: /usr/sbin/radiusd ­x or  /usr/sbin/radiusd ­X . Note: If you have Windows, you may also wish to use NTradPing (downloadable from  MasterSoft ) instead of radtest. If you do this, or test from any other machine, remember to put  your PC (or the other machine) in your NAS list in the file /etc/raddb/clients.conf . 3.3 Set Up freeRadius to Use a MySQL Database Now that radius is installed and we have tested that it works correctly, we can create a mysql database for  it and configure radius to use this database.   First let's create a new database and a new database user:  bash$ mysql -p -u root mysql> CREATE DATABASE radiusdb; mysql> GRANT ALL ON radiusdb.* TO raduser@localhost IDENTIFIED BY "radpass"; mysql> exit;
  • 5. Now lets create the tables of the database by running the SQL script file that is in the directory  freeradius/doc/examples/:  bash$ mysql -p -u root -D radiusdb < /usr/share/doc/freeradius- 1.1.7/examples/mysql.sql  We should modify now /etc/raddb/sql.conf by setting there the database, the username  and the password that are needed to connect to the mysql server:  # Connect info server = "localhost" login = "raduser" password = "radpass" # Database table configuration radius_db = "radiusdb" Note: For testing/debug purposes, change sqltrace to yes. Then, freeradius will dump all  SQL commands to the debug output. Note: You may also need to modify the line about sql_user_name in this file.  Edit the file /etc/raddb/radiusd.conf and make there these modifications:   Uncomment the line saying 'sql' in the authorize{} section and comment the line saying  'files'.   Also uncomment the line saying 'sql' to the accounting{} section to tell FreeRADIUS to  store accounting records in SQL as well. This file should then look something like this:  authorise { preprocess chap mschap suffix eap # files sql pap } accounting { # We leave "detail" enabled to _additionally_ log accounting to /var/log/radius/radacct detail sql } 3.4 Testing MySQL Enter some data into the database:  bash$ mysql -u raduser -p radpassw mysql> USE radiusdb; mysql> SHOW TABLES;
  • 6. mysql> INSERT INTO usergroup (UserName, GroupName) --> VALUES ("radiustest", "testgroup"); mysql> SELECT * FROM usergroup; mysql> INSERT INTO radcheck (UserName, Attribute, Value) --> VALUES ("radiustest", "Password", "testpassword"); mysql> SELECT * FROM radcheck; mysql> INSERT INTO radgroupreply (GroupName, Attribute, op, Value) --> VALUES ("testgroup","Framed-Compression","==","Van-Jacobsen-TCP-IP"); mysql> INSERT INTO radgroupreply (GroupName, Attribute, op, Value) --> VALUES ("testgroup","Framed-Protocol","==","PPP"); mysql> INSERT INTO radgroupreply (GroupName, Attribute, op, Value) --> VALUES ("testgroup","Framed-MTU","==","1500"); mysql> INSERT INTO radgroupreply (GroupName, Attribute, op, Value) --> VALUES ("testgroup","Service-Type","==","Framed-User"); mysql> quit; Then stop the service /sbin/service radiusd stop and run radiusd in debug mode: /usr/sbin/radiusd ­x  or /usr/sbin/radiusd ­X .  Now check access for the user radiustest with password testpassword :  bash# radtest radiustest testpassword localhost 10 local1 Sending Access-Request of id 224 to 127.0.0.1 port 1812 User-Name = "radiustest" User-Password = "testpassword" NAS-IP-Address = 255.255.255.255 NAS-Port = 10 rad_recv: Access-Accept packet from host 127.0.0.1:1812, id=224, length=44 Framed-Compression = Van-Jacobson-TCP-IP Framed-Protocol = PPP Framed-MTU = 1500 Service-Type = Framed-User 3.5 Configure MikroTik for Being a PPPoE Server  First, the package PPP needs to be installed in MikroTik.   Now let us suppose that ether1 is connected to WAN and ether2 is connected to LAN. Then add  an IP address to ether1, add a gateway, DNS etc. so that the MikroTik box can connect to internet.  Important: Do not add an IP address to the internal interface (ether2).  If NAT is used, ensure that src­nat/masquerade firewall rule has been added ( /ip firewall nat ... )  and it is working properly. It can be added like this:  > /ip firewall nat add chain=srcnat out-interface=ether1 action=masquerade  Test the connection of the MikroTik server to internet:  > /ping www.google.com  Once you have verified the server’s connectivity, create PPP profiles: 
  • 7. > /ppp profile add name="pppoe-128k" local-address=10.10.1.1 dns- server="192.168.25.101" rate-limit=128k/128k > /ppp profile add name="pppoe-256k" local-address=10.10.1.1 dns- server="192.168.25.101" rate-limit=256k/256k These two profiles have different connection speeds.   Now create a PPPoE server instance (service) and enable it:  > /interface pppoe-server server add service-name="pppoe1" interface=ether2 one-session-per-host=yes default-profile="pppoe-128k" > /interface pppoe-server server print > /interface pppoe-server enable 0  Finally create user accounts:  > /ppp secret add name="test128" password="test128" service=pppoe profile="pppoe-128k" remote-address=10.10.1.111 > /ppp secret add name="test256" password="test256" service=pppoe profile="pppoe-256k" remote-address=10.10.1.112 > ppp secret print Flags: X - disabled # NAME SERVICE CALLER-ID PASSWORD PROFILE REMOTE- ADDRESS 0 test128 pppoe test128 pppoe-128k 10.10.1.111 1 test256 pppoe test256 pppoe-256k 10.10.1.112 Now the PPPoE server is ready to answer PPPoE requests and to authenticate PPPoE clients.  Important: We don't need to give an IP address to ether2 (the interface that is on the clients' side) for  PPPoE to work. The PPPoE will assign automatically to the interface a new IP (which is like 10.10.1.1/32  in our example). So, a new virtual IP will be assigned to the interface for each client that is connected to  the server. If we assign an IP address to ether2, then the clients can connect to the internet using ethernet  instead of using pppoe. In general this is not what we want, because the ethernet connections do not  require a username and password and their bandwidth cannot be limited as easily as pppoe connections. 3.6 Testing the PPPoE Service To configure a windows computer to connect as a PPPoE client, do these:   Open the "New Connection Wizard" (from Network Connections).   In the next window (Network Connection Type) choose "Connect to the Internet".   In the next window (Getting Ready) select the choice "Set up my connection manually".   In the next step of the wizard (Internet Connection), select "Connect using a broadband connection  that requires a username and password".   Then, in the next window (Connection Name), write a name for the connection, something like  "PPPoE".   Next, give the username and password, for example username: test256 , password: test256 . 
  • 8. Finally finish the wizard. Now you should have a new connection at the Network Connections.  To configure a Fedora7 computer to connect as a PPPoE client, follow these steps:   From the menus, open System > Administration > Network . You have to give the root password  in order to access this menu.   Create a new connection and select connection type xDSL.   Give username, password, etc.   Activate the new connection.  3.7 Getting MikroTik to Work with RADIUS Right now we have a MikroTik that is configured as a PPPoE server and we have a freeRadius server that  is configured to check the data in a MySQL database. Now we need to configure MikroTik to use the  radius server for authenticating users. We also need to enter in the database of radius the data of the  clients (username, password and connection properties). Let's see how these can be done.   In MikroTik, add a radius server for the service ppp :  > /radius add service=ppp address=192.168.25.101 secret=mikro1 The IP of the radius server is 192.168.25.101 and it will be used for authenticating PPPoE clients  (users of service ppp ). The secret is like a password that the MikroTik and radius servers use to  verify each­other.   Tell the ppp service to use radius for AAA (Authentication, Authorization and Accounting):  > /ppp aaa set use-radius=yes > /ppp aaa print  In the radius server, make sure that the configuration file /etc/raddb/clients.conf contains a section like this:  client 192.168.25.1 { secret = mikro1 shortname = MikroTik1 } Then restart the radius service.  Make sure that the user test256 is not authenticated in the ppp service of MikroTik (or remove it if  it is there):  > /ppp secret print > /ppp secret remove 1 Check and make sure that the client test256 cannot connect to internet using the PPPoE 
  • 9. connection.   Now lets add some data about the user test256 in the mysql database of radius:  bash$ mysql -p -u root mysql> show databases; mysql> use radius; mysql> show tables; mysql> insert into usergroup (Username, GroupName) values ('test256', 'static256'); mysql> select * from usergroup where username='test256'; +------------+-----------+----------+ | UserName | GroupName | priority | +------------+-----------+----------+ | test256 | static256 | 1 | +------------+-----------+----------+ 1 row in set (0.02 sec) mysql> insert into radcheck (Username, Attribute, Value, Op) --> values ('test256', 'Cleartext-Password', 'test256', ":="); mysql> select * from radcheck where username='test256'; +----+----------+--------------------+----+----------+ | id | UserName | Attribute | op | Value | +----+----------+--------------------+----+----------+ | 2 | test256 | Cleartext-Password | := | test256 | +----+----------+--------------------+----+----------+ 1 row in set (0.00 sec) mysql> insert into radreply (UserName, Attribute, Value, Op) --> values ('test256', 'Framed-IP-Address', '192.168.10.101', ':='); mysql> select * from radreply where username='test256'; +----+----------+-------------------+----+----------------+ | id | UserName | Attribute | op | Value | +----+----------+-------------------+----+----------------+ | 1 | test256 | Framed-IP-Address | := | 192.168.10.101 | +----+----------+-------------------+----+----------------+ 1 row in set (0.02 sec) mysql> insert into radgroupreply (GroupName, Attribute, Value, Op) --> values ('static256', 'Framed-Protocol', 'PPP', ':='); mysql> insert into radgroupreply (GroupName, Attribute, Value, Op) --> values ('static256', 'Service-Type', 'Framed-User', ':='); mysql> insert into radgroupreply (GroupName, Attribute, Value, Op) --> values ('static256', 'Framed-Compression', 'Van-Jacobsen-TCP-IP', ':='); mysql> select * from radgroupreply where groupname='static256'; +----+-----------+--------------------+----+---------------------+ | id | GroupName | Attribute | op | Value | +----+-----------+--------------------+----+---------------------+ | 5 | static256 | Framed-Protocol | := | PPP | | 6 | static256 | Service-Type | := | Framed-User | | 7 | static256 | Framed-Compression | := | Van-Jacobsen-TCP-IP | +----+-----------+--------------------+----+---------------------+ 3 rows in set (0.00 sec) If you try now to connect to internet as a client with username 'test256' and password 'test256', using  PPPoE, it should work. However, to make sure that it really works through radius authentication, which  gets the authentication data from the database, you can stop the radiusd service (in the radius server) and  run it in debug mode: /usr/sbin/radiusd ­x . 
  • 10. 4 Using Two Radius Servers 4.1 Adding a Second Radius Server in MikroTik MikroTik can be configured to use more than one radius server for the authentication of the users. It is  done simply by adding additional radius servers:  > /radius add service=ppp address=192.168.25.101 secret=mikro1 > /radius add service=ppp address=192.168.25.102 secret=mikro1 > /radius print Flags: X - disabled # SERVICE CALLED-ID DOMAIN ADDRESS SECRET 0 ppp 192.168.25.101 mikro1 1 ppp 192.168.25.102 mikro1 In this case, MikroTik tries to use the first server for authentication, and if it cannot be connected, tries  the second one. This make the service of Radius more robust (if one server is down, there is still the other  one).  The configuration of the second radius server is the same as the first one. Both of their MySQL databases  replicate with each­other (two­way, bidirectional replication), so that both of them can be used at the  same time and they can synchronize the data automatically. Since both of the servers can be used at the same time (and they synchronize with each­other), then we  can have a kind of load balancing by configuring half of the MikroTik servers to have one radius server as  primary, and the other half to have the other server as primary in the list.  So, this redundancy of the radius servers ensures both high­availability and load balancing.  4.2 Replicating MySQL Databases of Radius In order to ensure service backup, high availability and load balancing, we replicate the databases of the  radius servers. We do it a two­way replication, so that both of them can be used for reading and writing,  and so that in case that one of them goes off, we don't need to do any manual configuration.  The two­way replication can be done like this:  1. In both servers modify the section [mysqld] in the configuration file /etc/my.cnf and add  these lines:  ### configuration as a master for replication server-id=1 log-bin=mysql_bin innodb_flush_log_at_trx_commit=1 sync_binlog=1 slave-skip-errors=all auto_increment_offset=1 auto_increment_increment=10 Make sure the servers have unique (different) server IDs, that skip­networking is not set, and that  binary logging is enabled. Also make sure that auto_increment_offset is different for each server.  (For the meaning of these options check the manual of mysql.) 
  • 11. 2. In the first server create a user that has permission to do replication:  mysql> GRANT REPLICATION SLAVE ON *.* TO 'repluser'@'192.168.25.%' --> IDENTIFIED BY 'replpassw'; mysql> FLUSH PRIVILEGES; 3. Make a full backup of the mysql database in the first server:  /usr/bin/mysqldump --user=root --password --all-databases --lock-all-tables --flush-logs --flush-privileges --add-drop-database --add-drop-table --force --master-data > backup.sql 4. Copy it to the other server and restore it:  bash# scp root@192.168.25.101:backup.sql . bash# /sbin/service mysqld start bash# mysql -p -u root < backup.sql 5. Setup the second server as slave and start the replication in it:  mysql> CHANGE MASTER TO MASTER_HOST='192.168.25.101', MASTER_USER='repluser', MASTER_PASSWORD='replpassw'; mysql> START SLAVE; mysql> show slave statusG The options MASTER_LOG_FILE and MASTER_LOG_POS can be given to CHANGE  MASTER TO as well, however they are restored from the backup file (since mysqldump was  called with the option ­­master­data). If you want to add them manually, then read their values  from the backup file backup.sql . 6. Setup also the first server as a slave of the first master.  a. In the second server (that will be master of the first one), get the status of the master:  mysql> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql_bin.000001 | 430585 | | | +------------------+----------+--------------+------------------+ 1 row in set (0.00 sec) b. Set the values of File and Position that are displayed above to the slave (first server), along  with the host, username and password: 
  • 12. mysql> stop slave; mysql> CHANGE MASTER TO MASTER_HOST='192.168.25.102', MASTER_USER='repluser', MASTER_PASSWORD='replpassw', MASTER_LOG_FILE='mysql_bin.000001', MASTER_LOG_POS=430585; mysql> start slave; mysql> show slave statusG The user 'repluser' has to be created in 192.168.25.102 and it should have replication  permissions. However, since this user was created in 192.168.255.101 and a full backup of  the database was done, this user should have been created when we restored the database to  it. In the output of the command show slave statusG , make sure that both  Slave_IO_Running and Slave_SQL_Running are Yes , otherwise find out what is the  problem. 4.3 Checking Replicated Databases It can happen that the replicated databases become inconsistent (especially since we have used the option  slave­skip­errors=all in /etc/my.cnf ). So, it is important to check and make sure that both databases  have exactly the same data. This can be done using the mysql external tools in  http://maatkit.sourceforge.net/ . They can be installed with:  bash$ perl Makefile.PL bash# make install Checking that the replicated databases are the same can be done with this command:  bash$ mysql-table-checksum --replicate=replcheck.checksum --replcheck --user=root --askpass 192.168.25.121 The server that is given in the command should be the master, however, since we use a two­way  replication, it doesn't matter which one we give.  The database replcheck and the table checksum must be created in the database, before we run this  command. The table checksum can be created like this:  CREATE TABLE checksum ( db char(64) NOT NULL, tbl char(64) NOT NULL, chunk int NOT NULL, boundaries char(64) NOT NULL, this_crc char(40) NOT NULL, this_cnt int NOT NULL, master_crc char(40) NULL, master_cnt int NULL, ts timestamp NOT NULL, PRIMARY KEY (db, tbl, chunk) );
  • 13. If it turns out that there are differences between the databases, an option for fixing them is the tool mysql­ table­sync .  5 SQL API for Radius Manager In order to manage the radius, we used Radius Manager (http://www.dmasoftlab.com/), which is an  application for managing the database of freeRadius, services, clients, etc. It even had some simple billing  functionality. Unfortunately, Radius Manager was not free software (open source), although it was cheap  and the support was quite responsive. We needed to integrate Radius Manager with our own client  application (built with SugarCRM), so we tried hard to convince the RM guys to sell us the source code  as well. However they would not listen at all about it. Anyway, we managed to do some kind of  integration by modifying the database of Radius Manager from our client application. In order to do this,  we built an external SQL API for Radius Manager. Since Radius Manager is not free software, we are not going to describe here our experience about its  installation and configuration. It does not need it and it does not deserve it. Instead, we are going to  describe just the SQL API that we built to work around the integration problems. 5.1 Advantages of Using It This SQL API helps to access the database of the Radius Manager from an outside program (from the  program that manages the services and users). it is a library of MySQL procedures, which can be used to  access and modify only those parts of the database that need to be touched.  It has these advantages:  ● It encapsulates (hides) the complexity of the database from the outside programmer. The  programmer doesn't have to know what tables or fields are there in the database, but just needs to  know the procedures/functions that are available in the API, their parameters, return values, etc.  ● It makes simpler the code of the outside program, because instead of using complicated SQL  queries, he just needs to call a procedure with the appropriate parameters.  ● The programmer is relived from the fear and the responsibility of touching something  inappropriately in the database. The responsibility of doing things appropriately is taken over by  the API.  ● In case that the structure of the radius manager is modified in the future releases, it is the  responsibility of the API to take them into account, and the code of the external program doesn't  need to change at all.  5.2 Procedure Descriptions and Usage Examples ● user_get procedure user_get(user varchar(32))
  • 14. Returns the data of a given user. Gets the username of the user as a parameter (type: varchar(32)), and  returns one or more records with the data of the users who match the data of the username. Matching is  done with LIKE. It may return nothing if such a user does not exist. The record that is returned has these  fields:     username, srvid, srvname, expiration, enabled, nr_conn > call radius.user_get('%'); -- returns the list of all the users > call radius.user_get('%d%'); -- returns all the users with a 'd' in username > call radius.user_get('xyz'); -- returns nothing because such a user does not exist > call radius.user_get('test'); -- returns the data of the user 'test': +----------+-------+----------------+------------+---------+---------+ | username | srvid | srvname | expiration | enabled | nr_conn | +----------+-------+----------------+------------+---------+---------+ | test | 2 | 256/128 Biznes | 2007-12-30 | 1 | 1 | +----------+-------+----------------+------------+---------+---------+ 1 row in set (0.00 sec) ● user_add procedure user_add(user varchar(32), passwd varchar(32), service_id int(11), nr_conn varchar(5), expxiration_date date ) Add a user in the database. It deletes first this user, in case that such a user already exists. It is the  responsibility of the application to check and make sure that the users that are added are unique (no two  users with the same username are added), otherwise the second user will overwrite the first one.  > call radius.user_add('xyz', 'passwd', 3, 2, 2007-12-15); -- inserts a new user with 2 simultaneous connections > call radius.user_add('abc', 'passwd1', '4', '1', '2007-12-15'); -- inserts another user with only 1 connection > call radius.user_add('xyz', 'passwd', 3, 2, '2007-12-15'); -- gives error because the user 'xyz' already exists ● user_update procedure user_update(user varchar(32), service_id int(11), nr_conn varchar(253), expiration_date date ) Update the data of the given user in the database.  > call radius.user_update('xyz', 3, 2, '2008-1-30'); -- update the data of the user 'xyz' (for example expiration date is changed)
  • 15. user_set_password procedure user_set_password(user varchar(32), passwd varchar(32) ) Set a new password to the given user.  > call radius.user_set_password('xyz', 'passwd-1'); ● user_change_service procedure user_change_service(old_srvid varchar(100), new_srvid varchar(100)) Change an old service to a new one for all the users. This can be used in case that a service becomes  obsolete and all the users of this service should be upgraded to another service.  > call radius.user_change_service('3', '7'); ● user_del procedure user_del(user varchar(32)) Delete the given user.  > call radius.user_del('xyz'); ● service_get procedure service_get(service_id varchar(100), service_name varchar(40) ) Return a list of services that match the given service id and name. Matching is done with LIKE, so the  service name and id may contain wildcards and the procedure may return more than one record (or none  if nothing matches). The result that is returned contains these fields:      srvid, srvname, download_rate, upload_rate, enabled Fields download_rate and upload_rate are in Kbps, integers. The field enabled can be 1 or 0. If it is 0,  then this service should not be used for the new clients and after some time may be deleted from the table.   > call radius.service_get('%', '%'); -- returns a list of all the services > call radius.service_get('%', '%256%'); -- returns a list of the services that contain '256' in the name > call radius.service_get('%', '256');
  • 16. -- returns nothing because no service has the name '256' > call radius.service_get('2', '%'); -- returns the service with id=2: +-------+----------------+---------------+-------------+---------+ | srvid | srvname | download_rate | upload_rate | enabled | +-------+----------------+---------------+-------------+---------+ | 2 | 256/128 Biznes | 256 | 128 | 1 | +-------+----------------+---------------+-------------+---------+ 1 row in set (0.00 sec) ● service_add procedure service_add(service_name varchar(40), download_rate int(11), upload_rate int(11) ) Add a new service. Download and upload rates are in Kbps. In case that a service with the same id  already exists, it is deleted first and then the new service is added. So, it is the responsibility of the  application to make sure that it does not overwrite existing services.  > call radius.service_add('256/128 Vetem 1 PC', 256, 128); > call radius.service_add('512/256 Vetem 1 PC', '512', '256'); ● service_update procedure service_update(service_id int(11), service_name varchar(40), download_rate int(11), upload_rate int(11), enabled int(11) ) Update the attributes of a service.  + call radius.service_update(2, '256/128 Biznes', 256, 128, 0); + call radius.service_update('3', '256/128 Familje', '256', '128', '0'); -- disable these services ● service_del procedure service_del(service_id int(11)) Delete the service with the given id. It is deleted only if there are no users having this service. See also  user_change_service().  + call radius.service_del('2'); + call radius.service_del(3); -- delete these services
  • 17. 5.3 Implementation The MySQL procedures can be declared in a file, for example rm_api.sql and then they can be loaded  in the MySQL server like this:  bash$ mysql -p -u root mysql> ? mysql . rm_api.sql or like this:  bash$ mysql -p -u root < rm_api.sql The declaration of the procedures is like this:  /** * Set the delimiter of the SQL commands to double semicolon, * because semicolon needs to be used inside the procedure declaration * to separate the statements. */ DELIMITER ;; /** * Select the database that will be used by the procedures and functions. * The procedures and functions will be attached to this database. */ USE radius ;; /** procedure user_get * Returns the data of a given user. * Gets the username of the user as a parameter (type: varchar(32)), * and returns one or more records with the data of the users who * match the data of the username. Matching is done with LIKE. * It may return nothing if such a user does not exist. * The record that is returned has these fields: * username, srvid, srvname, expiration, enabled, nr_conn */ DROP PROCEDURE IF EXISTS user_get ;; CREATE PROCEDURE user_get(user varchar(32)) BEGIN SELECT rm_users.username, rm_services.srvid, srvname, expiration, enableuser AS enabled, radcheck.Value AS nr_conn FROM rm_users LEFT JOIN rm_services USING (srvid) LEFT JOIN radcheck ON ( rm_users.username = radcheck.UserName AND radcheck.Attribute = 'Simultaneous-Use' ) WHERE rm_users.username LIKE user; END ;; /** procedure user_add * Add a user in the database. * Takes these parameters: * username, password, service_id, nr_conn, expiration_date */ DROP PROCEDURE IF EXISTS user_add ;; CREATE PROCEDURE user_add(user varchar(32), passwd varchar(32), service_id int(11), nr_conn varchar(5),
  • 18. expiration_date date) BEGIN ### first delete it, in case that such a user exists CALL user_del(user); ### insert a record into the table rm_users INSERT INTO rm_users SET username = user, password = MD5(passwd), srvid = service_id, expiration = expiration_date, enableuser = '1', createdon = NOW(), createdby = 'admin'; ### insert two records into the table radcheck INSERT INTO radcheck (UserName, Attribute, op, Value) VALUES (user, 'Simultaneous-Use', ':=', nr_conn), (user, 'User-Password', ':=', passwd); END ;; /** procedure user_update * Update the data of the given user in the database. * Takes these parameters: * username, service_id, nr_conn, expiration_date */ DROP PROCEDURE IF EXISTS user_update ;; CREATE PROCEDURE user_update(user varchar(32), service_id int(11), nr_conn varchar(253), expiration_date date) BEGIN UPDATE rm_users SET srvid = service_id, expiration = expiration_date, enableuser = '1', createdon = NOW(), createdby = 'admin' WHERE username = user; UPDATE radcheck SET Value = nr_conn WHERE UserName = user AND Attribute = 'Simultaneous-Use'; END ;; /** procedure user_set_password * Set a new password to the given user. * Gets the parameters: user, passwd */ DROP PROCEDURE IF EXISTS user_set_password ;; CREATE PROCEDURE user_set_password(user varchar(32), passwd varchar(32)) BEGIN UPDATE rm_users SET password = MD5(passwd) WHERE username = user; END ;;
  • 19. /** procedure user_change_service * Change an old service to a new one for all the users. * Gets the parameters: old_srvid, new_srvid */ DROP PROCEDURE IF EXISTS user_change_service ;; CREATE PROCEDURE user_change_service(old_srvid varchar(100), new_srvid varchar(100)) BEGIN UPDATE rm_users SET srvid = new_srvid WHERE srvid = old_srvid; END ;; /** procedure user_del * Delete the given user. */ DROP PROCEDURE IF EXISTS user_del ;; CREATE PROCEDURE user_del(user varchar(32)) BEGIN DELETE FROM rm_users WHERE username = user; DELETE FROM radcheck WHERE UserName = user; END ;; /** procedure service_get * Return a list of services that match the given service id and name. * Matching is done with LIKE, so the service name and id may contain * wildcards and the procedure may return more than one records * (or none if nothing matches). * The result that is returned contains these fields: * srvid, srvname, download_rate, upload_rate, enabled * 'download_rate' and 'upload_rate' are in Kbps, integers * 'enabled' can be 1 or 0. If it is 0, then this service * should not be used for the new clients and after some * time may be deleted from the table. */ DROP PROCEDURE IF EXISTS service_get ;; CREATE PROCEDURE service_get(service_id varchar(100), service_name varchar(40) ) BEGIN SELECT srvid, srvname, (downrate DIV 1024) AS download_rate, (uprate DIV 1024) AS upload_rate, enableservice AS enabled FROM rm_services WHERE srvid LIKE service_id AND srvname LIKE service_name; END ;; /** procedure service_add * Add a new service. Parameters that are given to this procedure * are these: service_name, download_rate, upload_rate * Download and upload rates are integers in Kbps. */ DROP PROCEDURE IF EXISTS service_add ;; CREATE PROCEDURE service_add(service_id varchar(100), service_name varchar(40), download_rate int(11), upload_rate int(11)) BEGIN ### if such a service exists, delete it first CALL service_del(service_id);
  • 20. ### insert the new service INSERT INTO rm_services SET srvid = service_id, srvname = service_name, downrate = (download_rate * 1024), uprate = (upload_rate * 1024), enableservice = '1', limitexpiration = '1', poolname = 'pool0'; END ;; /** procedure service_update * Update the attributes of a service. * The parameters of the procedure are: * service_id, service_name, download_rate, upload_rate, enabled * Download and upload rates are integers in Kbps. */ DROP PROCEDURE IF EXISTS service_update ;; CREATE PROCEDURE service_update(service_id int(11), service_name varchar(40), download_rate int(11), upload_rate int(11), enabled int(11)) BEGIN UPDATE rm_services SET srvname = service_name, downrate = (download_rate * 1024), uprate = (upload_rate * 1024), enableservice = enabled WHERE srvid = service_id; END ;; /** procedure service_del * Delete the service with the given id. * It is deleted only if there are no users having this service. */ DROP PROCEDURE IF EXISTS service_del ;; CREATE PROCEDURE service_del(service_id int(11)) BEGIN ### get the number of the users which have this service DECLARE cnt INT DEFAULT 0; SELECT COUNT(*) INTO cnt FROM rm_users WHERE srvid = service_id; ### delete the service only if there are no users having it IF cnt = 0 THEN DELETE FROM rm_services WHERE srvid = service_id; END IF; END ;; /** Set the delimiter of the SQL commands back to semicolon. */ DELIMITER ; 5.4 User Access Rights Access rights can be arranged to the user used by the program in such a way that it is able to execute only  these procedures. It can be done as follows: 
  • 21. /** * Create a user and assign privileges to be able to execute * the functions and procedures in this file. */ USE radius; DROP USER 'prog'@'192.168.25.%'; CREATE USER 'prog'@'192.168.25.%' IDENTIFIED BY 'progpassw'; GRANT EXECUTE ON PROCEDURE user_get TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE user_add TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE user_update TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE user_set_password TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE user_change_service TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE user_del TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE service_get TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE service_add TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE service_update TO 'prog'@'192.168.25.%'; GRANT EXECUTE ON PROCEDURE service_del TO 'prog'@'192.168.25.%'; /** * Create a user for phpMyAdmin, which can see only certain tables. */ USE radius; DROP USER 'prog'@'localhost'; CREATE USER 'prog'@'localhost' IDENTIFIED BY 'progpassw'; GRANT SELECT ON rm_users TO 'prog'@'localhost'; GRANT SELECT ON rm_services TO 'prog'@'localhost'; GRANT SELECT ON radcheck TO 'prog'@'localhost'; In order to check what is happening in the database with phpMyAdmin, another local user has been  created, which has only SELECT access. 5.5 Using It On SugarCRM SugarCRM has some logic hooks where you can call custom code. These logic hooks are placed before  saving a record, before deleting it etc. In these logic hooks we have placed our code which integrates  SugarCRM with the database of freeRadius. So, the clients are managed on SugarCRM, however some  relevant data (like username, password, etc.) are replicated on the freeRadius database as well. The custom code that does it, looks like this: SugarCRM/custom/modules/Accounts/UpdateRadiusClients.php: <?php /** * This file contains hooks of the module Account. */ require_once('data/SugarBean.php'); require_once('modules/Accounts/Account.php'); require_once('custom/include/MysqlDB.php'); /** * The class UpdateRadiusClients contains some hooks * for the module Accounts, which keep updated the data * of the clients in the radius manager. */ class UpdateRadiusClients {
  • 22. /** connection to the radius database */ var $radb; var $default_email = 'reception@albaniaonline.net'; function __construct() { $this->radb = new MysqlDB('192.168.25.101', 'usr', 'pass', 'radius'); } function debug($msg) { $GLOBALS['log']->fatal($msg); } /** Fired before a record is deleted. */ function before_delete(&$bean, $event, $arguments) { //delete the user from the radius manager as well $username = $bean->username_c; try { $this->radb->query("call user_del('$username');"); } catch (Exception $e) { $msg = 'UpdateRadiusClients::before_delete(): ' . $e->getMessage(); $GLOBALS['log']->fatal($msg); print $e->getMessage(); exit(1); } } /** Fired before a record is saved. */ function before_save(&$bean, $event, $arguments) { try { //get the old (unmodified yet) values of the data $old_bean = new Account(); $old_bean->retrieve($bean->id); //check whether this is a new record or an existing one if ($old_bean->id=='') { //this is a new record, add a new user $this->add_radius_user($bean); } else { //update the user data in radius manager $this->update_radius_user($bean, $old_bean); } } catch (Exception $e) { $msg = 'UpdateRadiusClients::before_save(): ' . $e->getMessage(); $GLOBALS['log']->fatal($msg); print $e->getMessage(); exit(1); } } /** add/insert a new user in the database of radius manager */ function add_radius_user(&$bean)
  • 23. { //get variables that will be stored in radius $username = $bean->username_c; $password = $bean->password_c; $service = $bean->service_c; $nr_conn = $bean->simultaneous_connections_c; $mac = $bean->mac_address_c; $expiration_date = $bean->expiration_date_c; $fullname = $bean->name; if ($fullname=='') $fullname = $username; //$email = $bean->email1; $email = $_REQUEST[$_REQUEST['emailAddressPrimaryFlag']]; if ($email=='') $email = $this->default_email; //make sure that the username is not already in use if ($this->username_exists_in_sugar($username)) { $msg = "<strong>The username '$username' is already used " . "for another client.<br/>n" . "Please try again with another username.</strong>"; print $msg; exit(1); } if ($this->username_exists_in_radius($username)) { $msg = "<strong>The username '$username' is already used in RADIUS" . "for another client.<br/>n" . "Please try again with another username.</strong>"; print $msg; exit(1); } //build the field comment of radius manager $comment = $bean->shipping_address_street . ' -- ' . $bean->description; //add a new user in radius $query = ("call user_add('$username', '$password', '$service', '$nr_conn'," . " '$expiration_date', '$mac', '$fullname', " . "'$email', '$comment');"); $this->radb->query($query); } /** update user in the database of radius manager */ function update_radius_user(&$bean, &$old_bean) { //get the fields of the client $username = $bean->username_c; $password = $bean->password_c; $service = $bean->service_c; $nr_conn = $bean->simultaneous_connections_c; $mac = $bean->mac_address_c; $expiration_date = $bean->expiration_date_c; $fullname = $bean->name; if ($fullname=='') $fullname = $username; //$email = $bean->email1; $email = $_REQUEST[$_REQUEST['emailAddressPrimaryFlag']]; if ($email=='') $email = $this->default_email; //if the old username does not exist in radius, then create it $old_username = $old_bean->username_c; if (!$this->username_exists_in_radius($old_username)) { $query = "call user_add('$username','$password','$service','$nr_conn'," . " '$expiration_date','$mac','$fullname','$email');";
  • 24. $this->radb->query($query); } //update the username if it is changed if ($username != $old_username) { //make sure that the new username is not already in use if ( $this->username_exists_in_sugar($username) or $this->username_exists_in_radius($username) ) { //use the old username $username = $old_username; $bean->username_c = $old_username; //todo: display a notification message about this problem } else { //change the username $query = "call user_change_username('$old_username', '$username')"; $this->radb->query($query); } } //update the password if it is changed $old_password = $old_bean->password_c; $password = $bean->password_c; if ($password != $old_password) { $query = "call user_set_password('$username', '$password')"; $this->radb->query($query); } //build the field comment of radius manager $comment = $bean->shipping_address_street . ' -- ' . $bean->description; //update the rest of the fields $query = ( "call user_update('$username', '$service', '$nr_conn', " . "'$expiration_date', '$mac', '$fullname', " . "'$email', '$comment');" ); $this->radb->query($query); } /** * Return true if the given username already exists * in the sugar database. Otherwise return false. */ function username_exists_in_sugar($username) { //check in the sugar database $client = new Account(); $client->retrieve_by_string_fields(array('username_c'=>$username)); if ($client->username_c == $username) return true; //not found return false; } /** * Return true if the given username already exists * in the radius database. Otherwise return false. */ function username_exists_in_radius($username) {
  • 25. //check in the radius database $query = "select user_check('$username') as username"; $result = $this->radb->query($query); $record = $result->fetch_object(); if ($record->username == $username) return true; //not found return false; } } ?> SugarCRM/custom/include/MysqlDB.php: <?php /** * This class handles the queries to a mysql database. */ class MysqlDB { var $conn = false; //connection to the database /** constructor */ function __construct($db_host, $db_user, $db_pass, $db_name) { //create a new connection to the database $this->conn = new mysqli($db_host, $db_user, $db_pass, $db_name); //check for errors if (!$this->conn) { $errno = mysqli_connect_errno(); $error = mysqli_connect_error(); throw new Exception("Can't connect to DB: $errno : $error"); } } function __destruct() { $this->conn->close(); } /** run a mysql query and return the result */ function query($query) { //if there is no db connection, cannot run the query if (!$this->conn) return; //debug //$GLOBALS['log']->fatal('MysqlDB::query(): '.$query); //run the query and get the result $result = $this->conn->query($query); //check for errors if (!$result) { $errno = mysqli_errno($this->conn); $error = mysqli_error($this->conn); throw new Exception("Query failed: $errno : $error : $queryn"); }
  • 26. //return the result return $result; } } ?> 6 Almost Automated Configuration of MikroTik After a MikroTik server has been installed, it can be configured quickly by doing copy/paste of the  configuration commands. This can be done easily if we login to MikroTik using a linux terminal.  Note: In order to login to MikroTik through a linux terminal, the network configuration has to be  done manually first, right after the installation. It can be done by adding an IP address. A  gateway may be needed as well. We assume here that the MikroTik servers are named: MikroTik1, MikroTik2, etc. Initially we have some  configuration files in cfg/ , named  MikroTik1.cfg, MikroTik2.cfg, etc. After running the  script generate-mt-setup.sh, the files  MikroTik1.mt, MikroTik2.mt, etc. will be  generated in  setup/. 6.1 generate­mt­setup.sh The script generate-mt-setup.sh is used to (re)generate the setup configuration scripts for all the  MikroTik servers:  #!/bin/bash ### generate the setup configuration scripts for all the MikroTik servers ./mikromik-setup.sh cfg/MikroTik1.cfg > setup/MikroTik1.mt ./mikrotik-setup.sh cfg/MikroTik2.cfg > setup/MikroTik2.mt ./mikrotik-setup.sh cfg/MikroTik3.cfg > setup/MikroTik3.mt ./mikrotik-setup.sh cfg/MikroTik4.cfg > setup/MikroTik4.mt ./mikrotik-setup.sh cfg/MikroTik5.cfg > setup/MikroTik5.mt 6.2 mikrotik­setup.sh The script mikrotik-setup.sh is used to generate and output the MikroTik commands that are used  to configure a MikroTik server with PPPoE service. The commands that are generated can be executed on  the MikroTik server with copy/paste (all of them or many of them at once). The script should get an  argument, which is the name of a file which contains configuration parameters that are specific for each  server (for example IP, gateway, etc.).  #!/bin/bash ### This shell script generates and output the commands that are used ### to configure a MikroTik server with PPPoE service. ### The commands that are generated can be executed on the MikroTik server ### with copy/paste (all of them at once). ### get the configuration file as the first parameter cfgfile=${1:-MikroTik.cfg}
  • 27. ### include the configuration file . $cfgfile ### generate and output the MikroTik commands cat <<EOF ### add an address on the outside (WAN) interface of the MikroTik # / ip address add address=$ADDRESS interface=$WAN_IF ### add a gateway # / ip route add gateway=$GATEWAY ### set a password # / password # # mt-$ID # mt-$ID ## change the name of the server / system identity set name=MikroTik$ID ### set the DNS servers / ip dns set primary-dns=$DNS1 secondary-dns=$DNS2 ### setup NAT on the outside interface of the MikroTik / ip firewall nat add chain=srcnat out-interface=$WAN_IF action=masquerade ### create a PPPoE service on the inner (LAN) interface of the MikroTik / ppp profile add name=pppoe_profile local-address=10.0.0.0 / interface pppoe-server server add service-name=pppoe_service default-profile=pppoe_profile interface=$PPPOE_IF authentication=pap,chap max-sessions=450 one-session-per-host=no disabled=no ### add another address for connecting to the radius server / ip address add address=192.168.25.$ID/24 interface=$RADIUS_IF ### disable masquerading for the radius LAN (192.168.25.0/24) / ip firewall nat add chain=srcnat out-interface=$WAN_IF src-address=192.168.25.0/24 action=return / ip firewall nat move 1 0 ### add radius servers for any PPP service on MikroTik / radius add service=ppp address=192.168.25.101 secret=passw timeout=2000ms called-id=pppoe_service / radius add service=ppp address=192.168.25.102 secret=passw timeout=2000ms called-id=pppoe_service ### tell MikroTik to use the radius servers for authentication and accounting / ppp aaa set use-radius=yes / ppp aaa set accounting=yes / ppp aaa set interim-update=60 / radius incoming set accept=yes ### add some pools of about 500 IPs each / ip pool add name=pool0 ranges=10.0.0.0/23 / ip pool add name=pool1 ranges=10.0.2.0/23 / ip pool add name=pool2 ranges=10.0.4.0/23 / ip pool add name=pool3 ranges=10.0.6.0/23 / ip pool add name=pool4 ranges=10.0.8.0/23 / ip pool add name=pool5 ranges=10.0.10.0/23 / ip pool add name=pool6 ranges=10.0.12.0/23 / ip pool add name=pool7 ranges=10.0.14.0/23 / ip pool add name=pool8 ranges=10.0.16.0/23 / ip pool add name=pool9 ranges=10.0.18.0/23
  • 28. ### logout / quit EOF 6.3 cfg/MikroTik1.cfg The file cfg/MikroTik1.cfg contains some configuration variables for the values that are different  for each MikroTik that is configured.  ### These configuration variables are included by the script ### that generates the MikroTik setup commands. Their values ### should be set according to the server where MikroTik is ### installed. The ID should be a different number from the ### other MikroTiks that connect to the same server radius. ID=1 WAN_IF=ether1 ADDRESS=192.168.25.21/24 GATEWAY=192.168.25.1 DNS1=192.168.25.11 DNS2=4.2.2.2 PPPOE_IF=ether2 RADIUS_IF=ether2 6.4 setup/MikroTik1.mt These are the configuration commands that are generated for a MikroTik server by the script  mikrotik-setup.sh , which takes as input the configuration variables in the file  cfg/MikroTik1.cfg . These commands can be copy/pasted to the MikroTik prompt from a terminal.  The commands that are commented should be given manually right after the installation of the MikroTik.  ### add an address on the outside (WAN) interface of the MikroTik # / ip address add address=192.168.25.21/24 interface=ether1 ### add a gateway # / ip route add gateway=192.168.25.1 ### set a password # / password # # mt-1 # mt-1 ## change the name of the server / system identity set name=MikroTik1 ### set the DNS servers / ip dns set primary-dns=192.168.25.11 secondary-dns=4.2.2.2 ### setup NAT on the outside interface of the MikroTik / ip firewall nat add chain=srcnat out-interface=ether1 action=masquerade
  • 29. ### create a PPPoE service on the inner (LAN) interface of the MikroTik / ppp profile add name=pppoe_profile local-address=10.0.0.0 / interface pppoe-server server add service-name=pppoe_service default-profile=pppoe_profile interface=ether2 authentication=pap,chap max-sessions=450 one-session-per-host=no disabled=no ### add another address for connecting to the radius server / ip address add address=192.168.25.1/24 interface=ether2 ### disable masquerading for the radius LAN (192.168.25.0/24) / ip firewall nat add chain=srcnat out-interface=ether1 src-address=192.168.25.0/24 action=return / ip firewall nat move 1 0 ### add radius servers for any PPP service on MikroTik / radius add service=ppp address=192.168.25.101 secret=radiuspassw timeout=2000ms called-id=pppoe_service / radius add service=ppp address=192.168.25.102 secret=radiuspassw timeout=2000ms called-id=pppoe_service ### tell MikroTik to use the radius servers for authentication and accounting / ppp aaa set use-radius=yes / ppp aaa set accounting=yes / ppp aaa set interim-update=60 / radius incoming set accept=yes ### add some pools of about 500 IPs each / ip pool add name=pool0 ranges=10.0.0.0/23 / ip pool add name=pool1 ranges=10.0.2.0/23 / ip pool add name=pool2 ranges=10.0.4.0/23 / ip pool add name=pool3 ranges=10.0.6.0/23 / ip pool add name=pool4 ranges=10.0.8.0/23 / ip pool add name=pool5 ranges=10.0.10.0/23 / ip pool add name=pool6 ranges=10.0.12.0/23 / ip pool add name=pool7 ranges=10.0.14.0/23 / ip pool add name=pool8 ranges=10.0.16.0/23 / ip pool add name=pool9 ranges=10.0.18.0/23 ### logout / quit 7 Optimize and Test the Performance We need to know (or estimate) how much load can keep the server, how many clients it can serve without  creating problems. For this reason, we simulate a high load by sending too many requests to the server, in  order to see up to what point the server can bear the load.  However, before testing the server we should optimize it so that it can have the best performance with the  resources that are available (RAM etc.).  7.1 Optimize RAM The usage of RAM by the server can be seen at the statistics generated by MRTG. It can be seen also  using linux commands such as top , free, ps and pstree.  ● The command top shows the most active processes. The command pstree displays a graphical  tree of processes and their children. 
  • 30. Using the command ps I can find out the processes that consume the most amount of RAM and  how much uses each of them. I do it like this:   bash# ps -e -o vsize,comm | sort -k1 -n -r | head -n 20 146896 mysqld 123064 radiusd 36412 httpd 36392 httpd 36380 httpd 36380 httpd 36376 httpd 36128 httpd 36128 httpd 36120 httpd 30200 httpd 29600 sort 13072 dbus-daemon 12800 pcscd 11040 snmpd 10720 restorecond 9156 sendmail 8252 sshd 8252 sshd 7996 sendmail ● The command free shows the usage of memory, how much is free etc:  bash# free total used free shared buffers cached Mem: 1026844 449416 577428 0 141572 178628 -/+ buffers/cache: 129216 897628 Swap: 1052248 0 1052248 From the graph of the free memory we can notice that gradually the server uses all of the free  RAM that is available. Initially I thought that maybe there is some problem with memory leaking  from some program (e.g. mysqld). However, after googling for this problem, I found out that this  is something normal for a linux server:    The kernel will attempt to use any available memory for buffering and   cacheing, as this makes things run faster. When applications need more   memory, buffer and cache space is released. So the figure you need to   look at in the "free" output is the one in the row marked "­/+   buffers/cache:", which shows memory usage not in buffers and cache; that   represents how much memory you're "really" using. See also these discussions: http://linux.derkeiler.com/Mailing­Lists/Fedora/2005­02/3149.html  and http://linux.derkeiler.com/Newsgroups/linux.redhat.misc/2004­01/0220.html . ● In order to optimize the usage of RAM, stop any services that use a lot of memory and are not  necessary. I stopped ConsoleKit and yum­updatesd like this:  service ConsoleKit stop service ConsoleKit status chkconfig ConsoleKit off chkconfig --list ConsoleKit service yum-updatesd stop service yum-updatesd status
  • 31. chkconfig yum-updatesd off chkconfig --list yum-updatesd 7.2 Optimize MySQL In order to check the performance of the mysql server and to discover any problems, we can use the tools  mysqlreport and mysqlsla , as described in this guide: Non­technical Guide to Isolating Slow MySQL  Queries .  ● First download mysqlreport and mysqlsla from http://hackmysql.com/ and place them at /usr/ local/bin/ .  ● Then create the cron job /etc/cron.daily/mysqlreport.sh (which will send a daily  report by email):  #!/bin/bash ### send a report to root by email, about the performance of the mysql server /usr/local/bin/mysqlreport --all --flush-status --user root --password sqladmin --email root Alternatively, this command can be executed periodically from the command line:  mysqlreport --all --user root --password ● Add these lines in /etc/my.cnf , in order to log the slow queries:  ### log slow queries log-slow-queries long_query_time=1 Then restart mysql: service mysql restart . ● Finally, wait for the report (which should come daily) and check the values of Read ratio , Slow ,  Waited , etc. (as described in http://hackmysql.com/nontech ). If there are slow queries, find out  which are these queries, using a command like this:  mysqlsla --slow /var/lib/mysql/slow_queries.log > top_10_slow_queries In order to optimize mysql we can do these things:  ● Modify the file /etc/my.cnf similarly to the following lines and then restart mysqld:  [mysqld] . . . . . ### options for increasing performance skip-name-resolve max_connections=500
  • 32. key_buffer=128M table_cache=256 read_buffer_size=1M read_rnd_buffer_size=4M myisam_sort_buffer_size=64M thread_cache_size=8 query_cache_size=16M thread_concurrency=2 . . . . . [mysqldump] quick max_allowed_packet = 16M [isamchk] key_buffer = 128M sort_buffer_size = 128M read_buffer = 2M write_buffer = 2M [myisamchk] key_buffer = 128M sort_buffer_size = 128M read_buffer = 2M write_buffer = 2M These values are mainly copied from the file /usr/share/mysql/my-large.cnf . See  also the articles in http://www.databasejournal.com/features/mysql/article.php/3367871 and http:// www.debianhelp.co.uk/mysqlperformance.htm .  ● As advised in /usr/local/share/doc/freeradius/tuning_guide , we convert to  the engine innodb the table radacct :  mysql> ALTER TABLE radacct ENGINE=INNODB; ● Then, to increase the performance of the innodb engine, we also add these parameters at  /etc/my.cnf :  ### innodb options for incleasing performance innodb-buffer_pool_size=256M innodb_additional_mem_pool_size=20M innodb_log_file_size=64M innodb_log_buffer_size=8M ● We create a multi column index for the (UserName,AcctStopTime) attributes, as advised in  /usr/local/share/doc/freeradius/tuning_guide :  mysql> EXPLAIN SELECT UserName, AcctStopTime FROM radacct; mysql> ALTER TABLE radacct ADD INDEX username_acctsstoptime --> (UserName, AcctStopTime); mysql> EXPLAIN SELECT UserName, AcctStopTime FROM radacct; Read also this article:  http://www.databasejournal.com/features/mysql/article.php/10897_1382791_1
  • 33. 7.3 Optimize FreeRADIUS The optimization of FreeRADIUS is based on the suggestions given in the file  /usr/local/share/doc/freeradius/tuning_guide .  ● First of all we add the noatime attribute to the log files:  chattr -R -A /usr/local/var/log/radius/ lsattr /usr/local/var/log/radius/ This will decrease the number of accesses to the disk because access time of the log files will not  be recorded.  ● Disable at all detailed logs in the configuration file  /usr/local/etc/raddb/radiusd.conf by commenting them out (they can be found by  searching for the word detail ):  # Log authentication requests to the log file. # # allowed values: {no, yes} # #log_auth = yes log_auth = no # Write a detailed log of all accounting records received. # # detail { # detailfile = ${radacctdir}/%{Client-IP-Address}/detail-%Y%m%d # detailperm = 0600 # } # detail auth_log { # detailfile = ${radacctdir}/%{Client-IP-Address}/auth-detail-%Y%m%d # detailperm = 0600 # } # detail reply_log { # detailfile = ${radacctdir}/%{Client-IP-Address}/reply-detail-%Y%m %d # detailperm = 0600 # } # detail pre_proxy_log { # detailfile = ${radacctdir}/%{Client-IP-Address}/pre-proxy-detail- %Y%m%d # detailperm = 0600 # } # detail post_proxy_log { # detailfile = ${radacctdir}/%{Client-IP-Address}/post-proxy-detail- %Y%m% # detailperm = 0600 # } authorize { # If you want to have a log of authentication requests, # un-comment the following line, and the 'detail auth_log' # section, above.
  • 34. # auth_log } accounting { # # Create a 'detail'ed log of the packets. # Note that accounting requests which are proxied # are also logged in the detail file. # detail # daily } post-auth { # # If you want to have a log of authentication replies, # un-comment the following line, and the 'detail reply_log' # section, above. # reply_log } pre-proxy { # If you want to have a log of packets proxied to a home # server, un-comment the following line, and the # 'detail pre_proxy_log' section, above. # pre_proxy_log } post-proxy { # If you want to have a log of replies from a home server, # un-comment the following line, and the 'detail post_proxy_log' # section, above. # post_proxy_log } ● We can also comment the lines related to unix , radwtmp and radutmp in radiusd.conf .  ● Increase the value of cleanup_delay and max_requests :  # cleanup_delay: The time to wait (in seconds) before cleaning up # a reply which was sent to the NAS. # # The RADIUS request is normally cached internally for a short period # of time, after the reply is sent to the NAS. The reply packet may be # lost in the network, and the NAS will not see it. The NAS will then # re-send the request, and the server will respond quickly with the # cached reply. # # If this value is set too low, then duplicate requests from the NAS # MAY NOT be detected, and will instead be handled as seperate requests. # # If this value is set too high, then the server will cache too many # requests, and some new requests may get blocked. (See 'max_requests'.) # # Useful range of values: 2 to 10 # #cleanup_delay = 5 cleanup_delay = 10 # max_requests: The maximum number of requests which the server keeps # track of. This should be 256 multiplied by the number of clients. # e.g. With 4 clients, this number should be 1024.
  • 35. # # If this number is too low, then when the server becomes busy, # it will not respond to any new requests, until the 'cleanup_delay' # time has passed, and it has removed the old requests. # # If this number is set too high, then the server will use a bit more # memory for no real benefit. # # If you aren't sure what it should be set to, it's better to set it # too high than too low. Setting it to 1000 per client is probably # the highest it should be. # # Useful range of values: 256 to infinity # #max_requests = 1024 max_requests = 15000000 ● Turn off proxy requests:  #proxy_requests = yes #$INCLUDE ${confdir}/proxy.conf proxy_requests = no ● Increase some values at thread pool :  thread pool { #max_servers = 32 max_servers = 128 #min_spare_servers = 3 #max_spare_servers = 10 min_spare_servers = 5 max_spare_servers = 16 } ● Modify /usr/local/etc/raddb/sql.conf and increase the number of sql connections. It  should be greater than the number of the radius servers:  # number of sql connections to make to server #num_sql_socks = 5 ### this value is greater than max_servers (=128) num_sql_socks = 130 # number of seconds to dely retrying on a failed database # connection (per_socket) #connect_failure_retry_delay = 60 connect_failure_retry_delay = 10 ● Finally restart radiusd : /usr/local/sbin/rc.radiusd restart
  • 36. 7.4 Testing the Performance When a client tries to connect to internet, MikroTik makes an authentication/authorization request to the  radius server. Then, after the client is connected, MikroTik sends periodically an accounting update to the  radius server, telling how much download/upload traffic the client has done during this interval. We have  set this interval to one minute in MikroTik. So, each minute, the radius server gets an accounting request  for each connected client, and it writes the data to the MySQL database. To simulate a client, we use the script radius­client.sh . It keeps running and sending update requests to  the radius server until the stop time, then it terminates. The interval between the update requests is 5sec,  so, the load on the server that is generated by this client will be approximately equal to the load of 10 real  clients.  Then we use the scrip test­radius.sh which runs many of such clients in parallel, in order to increase the  load. The number of the clients that is generated is configurable. The script stop­test.sh can be used to  interrupt the simulation at any time.  From the tests that we did, we found out that in general the bottleneck of the system is the MySQL  database. One way to increase its performance is to build a MySQL cluster, with two or more MySQL  servers. However this requires additional resources (additional servers with big amounts of RAM). At that  time we could still handle out clients without a MySQL cluster, so we didn't try to build it. ● radius­client.sh #!/bin/bash ### Simulate a radius client. ### It will keep running and sending requests to the radius server ### until the stop time, then it will terminate. ### go to this directory cd $(dirname $0) ### get the parameters if [ "$2" = "" ] then echo "Usage: $0 client_id stop_time" exit fi id=$1 stop_time=$2 ### convert the stop time into seconds stop_time_sec=$(date --date="$stop_time" +%s) ### variables radius_server="192.168.25.101" secret="test" user="testing-radius" passw="test" interval=5 #radclient="/usr/local/bin/radclient -x" radclient="/usr/local/bin/radclient" ### simple packages for authentication and accounting auth_pkg="User-Name=$user,User-Password=$passw" acct_pkg_start="User-Name=$user,Acct-Session-Id=$id,Acct-Status-Type=Start" acct_pkg_stop="User-Name=$user,Acct-Session-Id=$id,Acct-Status-Type=Stop"
  • 37. acct_pkg_update=" User-Name=$user, Acct-Session-Id=$id, Acct-Status-Type=Interim-Update, Service-Type=Framed-User, Framed-Protocol=PPP, User-Name=$user , Calling-Station-Id=00:11:43:9f:90:53, Called-Station-Id=Test, NAS-Port-Id=ether2, Framed-IP-Address="10.2.255.255", Acct-Authentic=RADIUS, Acct-Session-Time=5461, Acct-Input-Octets=974290, Acct-Input-Gigawords=0, Acct-Input-Packets=6458, Acct-Output-Octets=3578820, Acct-Output-Gigawords=0, Acct-Output-Packets=6338, NAS-Identifier=TEST, Acct-Delay-Time=0, Client-IP-Address=192.168.25.22" ### start the connection #echo $acct_pkg_stop | $radclient $radius_server acct $secret echo $auth_pkg | $radclient $radius_server auth $secret echo $acct_pkg_start | $radclient $radius_server acct $secret ### send update account packages periodically until stop time curr_time_sec=$(date +%s) #echo $curr_time_sec ## debug while [ $curr_time_sec -lt $stop_time_sec ] do echo $acct_pkg_update | $radclient $radius_server acct $secret sleep $interval curr_time_sec=$(date +%s) #echo $curr_time_sec ## debug done ### stop the connection echo $acct_pkg_stop | $radclient $radius_server acct $secret ● test­radius.sh #!/bin/bash ### test the resources of the radius server by starting ### many clients and sending a lot of request packages to it ### go to this directory cd $(dirname $0) ### get the parameters of the script if [ "$2" = "" ] then echo "Usage: $0 nr_clients stop_time" exit fi nr_clients=$1 stop_time=$2 for (( i=0 ; i < $nr_clients ; i++ )) do id=$(expr $i + 1) echo ./radius-client.sh $id $stop_time
  • 38. ./radius-client.sh $id $stop_time & sleep 1 done ● stop­test.sh #!/bin/bash ### stop the test by killing the processes pslist=$(ps ax | grep radius-client.sh | gawk '{print $1}') for ps in $pslist do kill -9 $ps done 8 Making Backups Backups are stored in the directory /backup/ on the radius server. Some of them are generated  periodically by cron jobs, some of them manually. They can be downloaded from  http://192.168.25.101/backup/ in order to write them in a CD or DVD. They are made accessible via http  by the configuration file /etc/httpd/conf.d/backup.conf, which has this content:  Alias /backup /backup <Directory "/backup"> Options +Indexes order deny,allow deny from all allow from 127.0.0.1 allow from 192.168.25.0/24 </Directory> 8.1 MikroTik Binary backups of the MikroTik servers can be done by the script  /backup/mikrotik/backup.sh . It takes automatically backups from the MikroTik servers. In  case that a MikroTik server needs to be reinstalled for some reason, its configuration can be restored  easily from the backup. First the backup is uploaded by FTP on the MikroTik server, then it is restored  using the command " / system backup save name=filename.backup " .  #!/bin/bash ### Get binary backups of all the MikroTik servers. ### This script can be executed manually or periodicly (by a cron job). ### However, since the configuration of the MikroTik servers is standard ### and does not change over the time (because all the data of the clients ### are stored in radius), there is no need to make the backup periodically. ### mtexec.exp is a script that can execute a MikroTik command remotely ### it requires expect (install it with `yum install expect`) exec_mt_cmd=/usr/local/bin/mtexec.exp ### Make a backup of the MikroTik router and get it here. ### Gets these parameters: server_ip passwd filename function get_backup { # get the parameters
  • 39. server_ip=$1 passwd=$2 filename=$3 # make a binary backup of the MikroTik server $exec_mt_cmd "$passwd" "$server_ip" "/ system backup save name=MikroTik.backup" # get it here wget "ftp://admin:$passwd@$server_ip/MikroTik.backup" # copy it to the given destination filename mv MikroTik.backup $filename } ### get the backup of all the MikroTik servers get_backup 192.168.25.101 "mt-1" backups/MikroTik1.backup get_backup 192.168.25.102 "mt-2" backups/MikroTik2.backup get_backup 192.168.25.103 "mt-3" backups/MikroTik3.backup #get_backup 192.168.25.104 "mt-4" backups/MikroTik4.backup #get_backup 192.168.25.105 "mt-5" backups/MikroTik5.backup The script /usr/local/bin/mtexec.exp is used to execute a MikroTik command remotely (see  the section Executing MikroTik Commands From a Script  below).  8.2 Database A backup of the database of radius is made each night, however the backups older than a week are deleted  automatically in order to free disk space. They can be downloaded from  http://192.168.25.101/backup/radius_db/backups/ (by a script that uses wget, or manually) and then  optionally can be written to another backup media (such as CD or DVD).  The cron script that does this is /etc/cron.daily/backup-db.cron which is actually a symbolic  link to /backup/radius_db/backup-db.cron . It just calls the script that makes the backup:  #!/bin/bash ### Make a backup of the database daily. ### It should be linked to cron.daily like this: ### ln -s /backup/radius_db/backup-db.cron /etc/cron.daily/ /backup/radius_db/backup-db.sh The script /backup/radius_db/backup-db.sh makes a full backup of the database and cleans  any backups that are older than a week:  #!/bin/bash ### This script makes a full backup of the database. ### It should be called periodically from a cron job. ### ### To restore, first unzip the backup file, and then use the command: ### bash$ mysql -p -u root < radiusdb.2007-10-27.sql ### go to this directory cd $(dirname $0) ### create a filename for the backup, which contains the date filename="backups/radiusdb.$(/bin/date +%Y-%m-%d).sql"
  • 40. ### backup all the databases /usr/bin/mysqldump --user=root --password=sqladmin --all-databases --flush-logs --lock-all-tables --force --master-data=2 > $filename gzip -f $filename ### find and clean any backup files older than a week find backups/ -ctime +7 | xargs rm -f To restore a backup file, first unzip it, and then use the command:  bash$ mysql -p -u root < radiusdb.2007-10-27.sql 8.3 Server A backup of the data and configurations of the radius server can be done by the script  /backup/radius_server/backup.sh . There is no need to do this backup periodically or  automatically, it can be done manually whenever any modifications on the configuration are done. The  files and directories that are backup­ed by the script are those that are listed in the file  /backup/backup_server/backup-list.txt .  The created backup archive can be retrieved remotely from http://192.168.25.101/backup/radius_server/  in order to store it to another backup media (such as CD, DVD, etc.). This backup can be used to build  quickly another radius server that is almost identical as the original one (or to rebuild it in case of failure).  ● backup/radius_server/backup_server.sh The script /backup/radius_server/backup_server.sh is used to backup the data and  configurations of the server:  #!/bin/bash ### Backup the data and configurations in the radius server. ### In case of failure, we should be able to setup the server by just ### installing fedora7 and restoring the backup. ### go to this directory cd $(dirname $0) ### get a current list of the installed packages #rpm -qa | sort > pkglist.txt #rpm -qa | sed 's/-[0-9].*//' | sort > pkglist_1.txt yum list installed > pkglist.txt yum list installed | gawk '{print $1}' | sed 's/.[^.]*$//' > pkglist_1.txt ### create a backup of the config files that are listed in 'backup-list.txt' echo radius-server-config.tgz tar --create --gzip --preserve --files-from="backup-list.txt" --file radius-server-config.tgz ### create a backup of the directories /var/www/html/, /usr/local/, etc. echo radius-server-html.tgz tar cz --preserve --file radius-server-html.tgz /var/www/html/ echo radius-server-local.tgz
  • 41. tar cz --preserve --file radius-server-local.tgz /usr/local/ ● backup/backup_server/backup­list.txt The file /backup/backup_server/backup-list.txt is a list of the files and directories that  should be backup­ed:  /etc/radiusmanager.cfg /var/www/html/radiusmanager/lic.txt /var/www/html/radiusmanager/config/config.php /etc/phpMyAdmin/config.inc.php /etc/my.cnf /etc/crontab /etc/cron.daily/backup-db.cron /etc/cron.daily/check-files.sh /etc/cron.monthly/backup-server.sh /etc/sysconfig/iptables /etc/resolv.conf /etc/aliases /home/dhoxha/.procmailrc /etc/rc.d/rc.local /etc/php.ini /etc/mail/sendmail.mc /etc/mail/sendmail.cf /etc/ntp.conf /etc/httpd/conf/httpd.conf /etc/httpd/conf.d/backup.conf /etc/httpd/conf.d/phpMyAdmin.conf /etc/httpd/conf.d/radiusmanager.conf /backup/mikrotik/backup.sh /backup/radius_db/backup-db.sh /backup/radius_db/backup-db.cron /backup/radius_server/backup-list.txt /backup/radius_server/pkglist.txt /backup/radius_server/pkglist_1.txt /backup/radius_server/backup-server.sh /usr/local/config/ /usr/local/etc/raddb/ /usr/local/bin/mtexec.exp /usr/local/scripts/ 8.4 Rebuild the Server From Backup The backup radius_server.tgz can be used to build quickly another radius server that is almost  identical as the original one. It can be done like this:  1. Initially install Fedora7 from CD/DVD. Install a system that is as small as possible (a minimal  system, no need for GUI etc., just the base system).  2. Transfer the backup file on the new system, using scp , wget or anything else.  3. Make sure that in this system are installed almost the same packages as in the original server. It  can be done like this:  a. First update the packages that are already installed: 
  • 42. bash# yum update b. Then extract the pkglist_1.txt from the backup archive:  bash# tar xfz radius-server.tgz backup/radius_server/pkglist_1.txt c. Next, make sure that all packages in pkglist_1.txt are installed in the server:  bash# cat backup/radius_server/pkglist_1.txt | xargs yum install d. If you want, you can also make sure that no extra packages (more than those on the  original server) are installed. It can be done by generating a list of the installed packages  (using yum list installed ), comparing it to the file pkglist_1.txt (using diff ),  finding out which are the extra packages and removing them:  bash# yum list installed | gawk '{print $1}' | sed 's/. [^.] *$//' >package_list.txt bash# diff -u package_list.txt pkglist_1.txt > pkg.diff bash# yum remove ... However, extra packages in general should not harm anything.  4. Now restore the backup file:  bash# cd / bash# tar xvz --preserve --file radius_server.tgz 5. Don't forget to restore the database of radius manager.  6. Also make sure that the needed services are ON (like httpd, mysqld, etc.), and those that are not  needed are OFF. This can be done using the commands /sbin/chkconfig and /sbin/service .  9 Misc 9.1 Keeping the Time Correct It is important that the time of the server be correct, since radius manager checks the expiration times of  the clients. In order to make sure that the time of the server is correct, we can use ntp. It can be installed  and configured like this:  1. Install the package ntp :  bash# yum install ntp 2. Modify /etc/ntp.conf by adding time servers: