Syed Jahanzaib Personal Blog to Share Knowledge !

June 27, 2018

FREERADIUS WITH MIKROTIK – Part #14 – Dynamic Bandwidth Change on the FLY using COA with radclient

Filed under: freeradius — Tags: , , , , , , — Syed Jahanzaib / Pinochio~:) @ 1:33 PM

fre

bandwidth

FREERADIUS WITH MIKROTIK – Part #1 – General Tip’s Click here to read more on FR tutorials …


Disclaimer! This is important!

Every Network is different , so one solution cannot be applied to all. Therefore try to understand logic & create your own solution as per your network scenario. Just dont follow copy paste.

If anybody here thinks I am an expert on this stuff, I am NOT certified in anything Mikrotik/Cisco/Linux or Windows. However I have worked with some core networks and I read , research & try stuff all of the time. So I am not speaking/posting about stuff I am formerly trained in, I pretty much go with experience and what I have learned on my own. And , If I don’t know something then I read & learn all about it.

So , please don’t hold me/my-postings to be always 100 percent correct. I make mistakes just like everybody else. However – I do my best, learn from my mistakes and always try to help others.

This particular script was tested in Virtual environment only, therefore consider this posting as an reference only, donot use it in production environment.

Regard's
Syed Jahanzaib~

Scenario:

We have a generic FreeRADIUS Version 2.2.8 based billing system in Ubuntu 16.04.3 LTS Server. Users are authenticating to NAS (Mikrotik) which is using Freeradius as its AAA Server.


Requirement:

Currently users packages are flat 1mb,  2mb and so on. We would like to introduce different bandwidth for day and night for specific services. Upgrade/Downgrade of user package should be done by dynamically with COA, so that package changes must be done on the fly without disconnecting user.

I made  a simple bash script to perform the job, but that required different cron jobs to start/end the packages, also if the server was powered off on that particular time then schedule will be missed till next run. This was major PITA. Therefore I added few more checks and balances so that if the job misses its schedule time, it will execute it self BUT avoiding re-running of same action.

Its a bit complicated piece of BASH scripting , but so far doing its job.


Software / Hardware Components Used:

  • NAS: Mikrotik CCR1036 / Firmware: 6.42.1
  • Radius: Ubuntu 16.04.3 LTS Server Edition / 64bit
  • FreeRADIUS Version: 2.2.8 using apt-get installation

SERVICES Table!

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;

-- Database: `radius`
-- Table structure for table `services`
CREATE TABLE `services` (
`srvid` int(10) NOT NULL,
`srvname` varchar(128) NOT NULL,
`descr` varchar(128) DEFAULT NULL,
`enabled` varchar(1) NOT NULL DEFAULT '1',
`expdays` int(4) NOT NULL DEFAULT '0',
`dlimit` varchar(32) NOT NULL DEFAULT '0',
`ulimit` varchar(32) NOT NULL DEFAULT '0',
`qt_enabled` tinyint(1) NOT NULL,
`tot_qt` int(32) NOT NULL DEFAULT '0',
`free_quota_enabled` tinyint(1) NOT NULL DEFAULT '0',
`free_qt_start_time` time NOT NULL,
`free_qt_end_time` time NOT NULL,
`dyn_bw_change` int(1) NOT NULL DEFAULT '0',
`dn_bwpkg` varchar(16) NOT NULL DEFAULT '0',
`dn_st_time` time NOT NULL,
`dn_et_time` time NOT NULL,
`dn_st_exec` int(1) NOT NULL DEFAULT '0',
`dn_et_exec` int(1) NOT NULL DEFAULT '0',
`ippool` varchar(64) NOT NULL,
`createdon` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- Dumping data for table `services`

INSERT INTO `services` (`srvid`, `srvname`, `descr`, `enabled`, `expdays`, `dlimit`, `ulimit`, `qt_enabled`, `tot_qt`, `free_quota_enabled`, `free_qt_start_time`, `free_qt_end_time`, `dyn_bw_change`, `dn_bwpkg`, `dn_st_time`, `dn_et_time`, `dn_st_exec`, `dn_et_exec`, `ippool`, `createdon`) VALUES
(9, '1mb', '1mb 30 days expity', '1', 30, '1024k', '1024k', 1, 1024, 1, '00:00:00', '00:00:00', 1, '2048k/2048k', '13:00:00', '23:00:00', 0, 1, 'public-pool', '2018-06-27 12:13:04');

-- Indexes for dumped tables
-- Indexes for table `services`
ALTER TABLE `services`
ADD PRIMARY KEY (`srvid`),
ADD KEY `nasname` (`srvname`);

-- AUTO_INCREMENT for dumped tables
-- AUTO_INCREMENT for table `services`
ALTER TABLE `services`
MODIFY `srvid` int(10) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;


the Script !

Following is bash script which will query different tables and take action according to the results. Scheduled to run hourly (or decide as per network load)


#!/bin/bash
# Following script will check specific table, and if found any servic entry (ID),
# It will query that service and make list of users attached to this service,
# Then it will query next pacakge , start/end time, and will perform actions accordingly
# It will also check if package change is already done, then ignore as per condition
# Syed Jahanzaib
# Created: 25-Jun=2018
# Last Modified: 27-Jun-2018
# set -x
SQLID="SQLID"
SQLPASS="SQLPASSWORD"
export MYSQL_PWD=$SQLPASS
CMD="mysql -u$SQLID --skip-column-names -s -e"
DB="radius"
#Table which contain main users information
USER_TABLE="users"
#Table which contains users name which will be scanned for quota
SRV_BW_DB="services"
#Log table in which few actions will be logged
USER_TABLE_LOG="log"
#Rad-user group in which we will update user profile like from 1mb to different pacake as per service
GROUP="radgroupreply"
# Temp file where user list will be saved
TMP1="/tmp/bwsch_srv.txt"
TMP2="/tmp/bwsch_users.txt"

#DATE TIME FUNCTIONS
currenttime=$(date +%H:%M:%S)

# Look for services that have Dynamic bandwidth change
$CMD "use $DB; select srvid from $SRV_BW_DB where dyn_bw_change ='1';" > $TMP1
if [ ! -s $TMP1 ]
then
echo "No SERVICES found to check for bandwdith changing in $SRV_BW_DB , exit"
exit 1
fi

# If required service found then look for Users
num=0
cat $TMP1 | while read srvid
do
num=$[$num+1]
SRVID=`echo $srvid |awk '{print $1}'`
$CMD "use $DB; select username from $USER_TABLE where srvid ='$SRVID';" > $TMP2
done
if [ ! -s $TMP2 ]
then
echo "No User found for bandwidth upgrade $SRV_BW_DB , exit"
exit 1
fi

# Run loop forumla to run CMD for single or multi usernames
num=0
cat $TMP2 | while read users
do
num=$[$num+1]
USERNAME=`echo $users |awk '{print $1}'`
SRVID=`$CMD "use $DB; select srvid from $USER_TABLE where username ='$USERNAME';"`
SRVNAME=`$CMD "use $DB; select srvname from services where srvid = '$SRVID';"`
DLIMIT=`$CMD "use $DB; select dlimit from services where srvid ='$SRVID';"`
ULIMIT=`$CMD "use $DB; select ulimit from services where srvid ='$SRVID';"`
DN_BWPKG=`$CMD "use $DB; select dn_bwpkg from services where srvid ='$SRVID';"`
DN_ST=`$CMD "use $DB; select dn_st_time from services where srvid ='$SRVID';"`
DN_ET=`$CMD "use $DB; select dn_et_time from services where srvid ='$SRVID';"`
DN_ST_EXEC=`$CMD "use $DB; select dn_st_exec from services where srvid ='$SRVID';"`
DN_ET_EXEC=`$CMD "use $DB; select dn_et_exec from services where srvid ='$SRVID';"`
GRPNAME=`$CMD "use $DB; select groupname from radgroupreply where groupname ='$SRVNAME';"`

# If package UPgrade time is matched in services & packages have not changed already, then do it now - zaib
echo "* Checking for Dynamic Bandwidth Changing. Match Start/End time, Check if upgrade/downgrade is already done etc,"
if [[ "$currenttime" > "$DN_ST" ]] && [[ "$currenttime" < "$DN_ET" ]]; then
# Avoid recurrence for same
if [ "$DN_ST_EXEC" -eq 0 ]; then
# Update User group for bandwidth change
$CMD "use $DB; update $GROUP set value= '$DN_BWPKG' where groupname ='$GRPNAME';"
NAS_IP=`$CMD "use $DB; select nasipaddress from radacct where username ='$USERNAME' AND acctstoptime is NULL;"`
NAS_SECRET=`$CMD "use $DB; select secret from nas where nasname = '$NAS_IP' ;"`
NAS_COA_PORT=`$CMD "use $DB; select nas_coa_port from nas where nasname = '$NAS_IP';"`
ACCTSESID=`$CMD "use $DB; select acctsessionid from radacct where username ='$USERNAME' AND acctstoptime is NULL;"`
$CMD "use $DB; update services set dn_st_exec= '1' where srvid ='$SRVID';"
$CMD "use $DB; update services set dn_et_exec= '0' where srvid ='$SRVID';"
echo "************** Bandwidht Packages UPgraded to New as per time **************"
fi
# If user is Online
if [ ! -z "$ACCTSESID" ]; then
echo User-Name=zaib,Mikrotik-Rate-Limit=\"$DN_BWPKG\" | radclient -x $NAS_IP:$NAS_COA_PORT coa $NAS_SECRET
fi
else
# If package DOWNgrade time is matched in services & packages have not changed already, then do it now - zaib
# BUT Avoid recurrence for same
if [ "$DN_ET_EXEC" -eq 0 ]; then
NAS_IP=`$CMD "use $DB; select nasipaddress from radacct where username ='$USERNAME' AND acctstoptime is NULL;"`
NAS_SECRET=`$CMD "use $DB; select secret from nas where nasname = '$NAS_IP' ;"`
NAS_COA_PORT=`$CMD "use $DB; select nas_coa_port from nas where nasname = '$NAS_IP';"`
ACCTSESID=`$CMD "use $DB; select acctsessionid from radacct where username ='$USERNAME' AND acctstoptime is NULL;"`
$CMD "use $DB; update $GROUP set value= '$DLIMIT/$ULIMIT' where groupname ='$GRPNAME';"
$CMD "use $DB; update services set dn_st_exec= '0' where srvid ='$SRVID';"
$CMD "use $DB; update services set dn_et_exec= '1' where srvid ='$SRVID';"
echo "************** Bandwidht Packages Downgraded to original packages **************"
fi
if [ ! -z "$ACCTSESID" ]; then
echo User-Name=zaib,Mikrotik-Rate-Limit=\"$DLIMIT/$ULIMIT\" | radclient -x $NAS_IP:$NAS_COA_PORT coa $NAS_SECRET
fi
fi
done

Result:

When bandwidth time change starts …

root@radius:/temp# ./bwsch.sh
* Checking for Dynamic Bandwidth Changing. Match Start/End time, Check if upgrade/downgrade is already done etc,
************** Bandwidht Packages UPgraded to New as per time **************
Sending CoA-Request of id 94 to 10.0.0.6 port 3799
User-Name = "zaib"
Mikrotik-Rate-Limit = "2048k/2048k"
rad_recv: CoA-ACK packet from host 10.0.0.6 port 3799, id=94, length=38
NAS-Identifier = "_CCR_GW"
NAS-IP-Address = 10.0.0.6
root@radius:/temp#
root@radius:/temp# ./bwsch.sh
* Checking for Dynamic Bandwidth Changing. Match Start/End time, Check if upgrade/downgrade is already done etc,
root@radius:/temp#

**********************************

root@radius:/temp# ./bwsch.sh
* Checking for Dynamic Bandwidth Changing. Match Start/End time, Check if upgrade/downgrade is already done etc,
************** Bandwidht Packages Downgraded to original packages **************
Sending CoA-Request of id 54 to 10.0.0.6 port 3799
User-Name = "zaib"
Mikrotik-Rate-Limit = "1024k/1024k"
rad_recv: CoA-ACK packet from host 10.0.0.6 port 3799, id=54, length=38
NAS-Identifier = "_CCR_GW"
NAS-IP-Address = 10.0.0.6
root@radius:/temp# ./bwsch.sh
* Checking for Dynamic Bandwidth Changing. Match Start/End time, Check if upgrade/downgrade is already done etc,
root@radius:/temp#

BEFORE

1- before update

AFTER

2- after update

 


When bandwidth time change ends … return pkg to normal

3- after tim ends.JPG


 

2 Comments »

  1. […] FREERADIUS WITH MIKROTIK – Part #14 – Dynamic Bandwidth Change on the FLY using COA with radclie… […]

    Like

    Pingback by Mikrotik with Freeradius/mySQL # Part-1 | Syed Jahanzaib Personal Blog to Share Knowledge ! — June 27, 2018 @ 1:37 PM


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: