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.
Regard’s
Syed Jahanzaib~
Scenario:
- We have DMASOFTLAB radius manager installed as a billing system in Ubuntu 12.04 server
- Mikrotik version 6.4x.x is acting as Hotspot NAS and connected with radius for AAA
Requirement: [A Weird one really]
As operator demanded
“We are running Hotspot on mikrotik, & client login to hotspot using his mobile/laptop. If logged-in client leaves his primary location without logout, & move to another location, & if he try to login from another device, his request should be ACCEPTED (by SIM-USE=2 directive), and his existing old session should be dropped and only new login session should be online.”
if the user uses same device then we could have used
if (User-Name){
if("%{sql:UPDATE radacct set AcctStopTime=ADDDATE(AcctStartTime,INTERVAL AcctSessionTime SECOND), AcctTerminateCause='Clear-Stale Session' WHERE UserName='%{User-Name}' and CallingStationId='%{Calling-Station-Id}' and AcctStopTime is NULL}"){
}
}
but things are different in hotspot as I have observed, if devices are different then it will give us already logged-in error, if we use sim-use=2
then second device can be logged-in but old session will also be alive and both ids will suck the bandwidth at a time.
Also using idle-timeout or keep-alive timeout is the simplest way to achieve this , but for some weird reasons and to avoid long arguments dueto accent issues, I made one customized solution for the operator.
Solution:
Login to mysql with root
mysql -uroot -pXXXX
and switch to radius database
use radius;
Now create new table that will hold duplicate users record
MYSQL Table to hold duplicate users list
-- -- Table structure for table `rm_dupusers` -- DROP TABLE IF EXISTS `rm_dupusers`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `rm_dupusers` ( `dupid` int(9) NOT NULL AUTO_INCREMENT, `datetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `username` varchar(64) NOT NULL, `ip` varchar(16) NOT NULL, `nas` varchar(16) NOT NULL, `comments` varchar(64) DEFAULT NULL, KEY `dupid` (`dupid`) ) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `rm_dupusers` --
MYSQL TRIGGER to check duplicate users sessions
Now we will create a new Trigger that will be executed when any record is inserted in radacct, it will check for existing duplicate session of user and if it found , it will add its entry in the mysql table of rm_dupusers
drop trigger chk_dup_user; DELIMITER ;; /*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `chk_dup_user` BEFORE INSERT ON `radacct` FOR EACH ROW BEGIN SET @dupuserchk = (SELECT count(*) from radacct where username=New.username and acctstoptime is NULL); IF (@dupuserchk = 1) THEN SET @dupusername = (SELECT username from radacct where username=New.username and acctstoptime is NULL); SET @dupuserip = (SELECT framedipaddress from radacct where username=New.username and acctstoptime is NULL); SET @dupusernas = (SELECT nasipaddress from radacct where username=New.username and acctstoptime is NULL); INSERT into rm_dupusers (dupid,username,ip,nas,comments) values ('',@dupusername,@dupuserip,@dupusernas,'Duplicate User'); END IF; END */;; DELIMITER ;
Mysql Part is Done.
Now we will create a BASH script that will scheduled to run every minute.
BASH script !
Create bash script in desired folder, in this example I am using /temp folder as default
mkdir /temp touch /temp/kickdupuser.sh chmod +x /temp/kickdupuser.sh nano /temp/kickdupuser.sh
& paste following, make sure to modify credentials
#!/bin/bash #set -x # Following script is made specifically for Dmasoftlab radius manager 4.1.x # When any new user will login, it will simply check if exists session of same user found, it will kick previous session # it requires custom trigger on radacct table, this script will be schedule to run every minute # Created: 25-MARCH-2019 # Tested on Ubuntu OS Only PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ################# # CHANGE these HOSTNAME=`hostname` SQLID="root" SQLPASS="XXXXXX" NAS_COA_PORT="1700" DB="radius" SRV="mysql" DUP_TABLE="rm_dupusers" INT="1" RADCLIENT="/usr/local/bin/radclient" ################# #DATE TIME FUNCTIONS currenttime=$(date +%H:%M:%S) # Add Script start execution entry in the /var/log/syslog to see if the script got executed or not logger "Duplicate User poller script Started @ $currenttime by the CRON scheduler ... Powered by SYED.JAHANZAIB" echo "- Script Start Time - $currenttime" echo "- Checking Duplicate Users in $DUP_TABLE table ..." export MYSQL_PWD=$SQLPASS CMD="mysql -u$SQLID --skip-column-names -s -e" #Table which contain main users information TMPUSRINFO=/tmp/userpass.txt TEMP="/temp" # Checking if /temp folder is previously present or not . . . { if [ ! -d "$TEMP" ]; then echo echo "- INFO: $TEMP folder not found, Creating it now to store logs ..." mkdir $TEMP else echo -e "- INFO: $TEMP folder is already present to store logs." echo fi } DUP_LIST_FILE=$TEMP/duplicate_users_list.txt SYSLOG="/var/log/syslog" > $TMPUSRINFO # KANNEL DETAILS KHOST="127.0.0.1:13013" KID="kannel" KPASS="KANNEL_PASSWORD" IPADD=`ip route get 1 | awk '{print $NF;exit}'` SRVSTATUS=`service $SRV status |grep running |wc -l` if [ "$SRVSTATUS" -ne 1 ]; #if [ -z "$SRVSTATUS" ]; then echo "- ALERT: $HOSTNAME - $IPADD - $SRV NOT RESPONDING CHECK - $DATE $DT .Exiting ..." echo "- ALERT: $HOSTNAME - $IPADD - $SRV NOT RESPONDING CHECK - $DATE $DT .Exiting ..." >> $SYSLOG echo "- ALERT: - $HOSTNAME - $IPADD - $SRV not responding *** - $currenttime Exiting ..." exit 1 else echo "- INFO: $SRV service is accessible. Proceeding further ... OK" fi # Check if table exists if [ $($CMD \ "select count(*) from information_schema.tables where \ table_schema='$DB' and table_name='$DUP_TABLE';") -eq 1 ]; then echo "- INFO: $DUP_TABLE Table exists ..." else echo "- WARNING: $DUP_TABLE Table does not exists ..." fi ########## # Enable following line so that it will update all users simultanous-use to '2' so that two sessions can be established # UPDATE radius.radcheck SET value = '2' where Attribute = 'Simultaneous-Use'; ########## # pull user record $CMD "use $DB; select username,ip,nas from $DUP_TABLE WHERE datetime >= NOW() - INTERVAL $INT MINUTE;" >> $TMPUSRINFO if [ ! -s $TMPUSRINFO ] then endtime=$(date +%H:%M:%S) echo " - INFO: No Duplicate User found in DMA RADIUS MANAGER TABLE '$DUP_TABLE' , Sending EXIT signals ... - Script Ends Here... - EXITING peacefully... - Script End Time - $endtime " exit 1 fi # Apply Count Loop Formula while deleting first line which have junk text num=0 cat $TMPUSRINFO | while read users do num=$[$num+1] username=`echo $users | awk '{print $1}'` USER_IP=`echo $users | awk '{print $2}'` ACCTSESID=`$CMD "use $DB; select acctsessionid from radacct where framedipaddress ='$USER_IP' AND acctstoptime is NULL;"` NAS_IP=`echo $users | awk '{print $3}'` NAS_SECRET=`$CMD "use $DB; select secret from nas where nasname = '$NAS_IP' ;"` # Print Info on screen echo "Duplicate User Found: USER: $username , IP: $USER_IP, ID: $ACCTSESID, $NAS: $NAS+IP @ $currenttime ... KICKING him now ..." echo "Duplicate User Found: USER: $username , IP: $USER_IP, ID: $ACCTSESID, $NAS: $NAS+IP @ $currenttime ... KICKING him now ..." >> $DUP_LIST_FILE #echo User-Name=$USERNAME,Acct-Session-Id=$ACCTSESID,Framed-IP-Address=$USER_IP,Mikrotik-Rate-Limit=\"$DN_BWPKG\" | $RADCLIENT -q -c 1 $NAS_IP:$NAS_COA_PORT coa $NAS_SECRET #for hotspot, enable following line echo Framed-IP-Address=$USER_IP | radclient -x -c 1 $NAS_IP:$NAS_COA_PORT disconnect $NAS_SECRET done # once done, we should delete the tmp files to clear the garbage rm $TMPUSRINFO
CRON scheduler to run the above script every minute. Edit crontab by
crontab -e
& add following entry
* * * * * /temp/kickdupuser.sh >/dev/null 2>&1
Testing …
Using same credentials, Login to first device, and then on second ,
& run this script,
root@radius:/temp# /temp/kickdupuser.sh - Script Start Time - 10:52:03 - Checking Duplicate Users in rm_dupusers table ... - INFO: /temp folder is already present to store logs. - INFO: mysql service is accessible. Proceeding further ... OK - INFO: rm_dupusers Table exists ... Duplicate User Found: USER: test , IP: 172.16.0.253, ID: 81d00057, : +IP @ 10:52:03 ... KICKING him now ... Sending Disconnect-Request of id 58 to 10.0.0.1 port 1700 Framed-IP-Address = 172.16.0.253 rad_recv: Disconnect-ACK packet from host 10.0.0.1 port 1700, id=58, length=32 NAS-Identifier = "ZAIB_CCR_GW" root@radius:/temp#
older session will be removed
Weirdo …. but its fun to learn !
Regard’s
Syed Jahanzaib