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 will gets DENY because of Single user limit. We increased it to 2 by using SIM-USE=2 directive in user properties,It allows second session to login, but both sessions can use the bandwidth, therefore we want that once second session is established its old first live session should get kicked. If it was single Hotspot we could have used the script on LOGIN, but there are several NAS spreaded across various location using single radius.”
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
1 | mysql -uroot -pXXXX |
and switch to radius database
1 | use radius; |
Now create new table that will hold duplicate users record
MYSQL Table to hold duplicate users list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | -- -- 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
1 2 3 4 5 6 7 8 9 10 11 12 | 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
1 2 3 4 | mkdir /temp touch /temp/kickdupuser.sh chmod +x /temp/kickdupuser.sh nano /temp/kickdupuser.sh |
& paste following, make sure to modify credentials
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | #!/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 # $CMD "use $DB; 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
1 | crontab -e |
& add following entry
1 | * * * * * /temp/kickdupuser.sh >/dev/null 2>&1 |
Testing …
Using same credentials, Login to first device, and then on second ,
& run this script,
1 2 3 4 5 6 7 8 9 10 11 12 | 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 !
TIPS:
Command to view duplicate users session in freeradius using CLI
1 | mysql -uroot -pMYPASS --skip-column-names -e 'use radius; SELECT username FROM radacct WHERE acctstoptime IS NULL;' > 1.txt && sort 1.txt | uniq |