Monday, March 5, 2007

Transparently Disconnect WiFi Stale Sessions (Chillispot, FreeRadius, MySQL)

I've been running this hotspot for months already, and yet I see stale client sessions from time to time preventing them access when they try to login again. By the way, the access controller is Chillispot and Simultaneous-Use is set to 1 for all clients.

Thanks to a few pointers I've read from the net, I finally got it ;)

The solution is to execute a script (and I prefer php for this) from radiusd every time a user logs in successfully. The script will check if an unterminated session is already present for the current user. If none, then the script will just return cleanly, otherwise, the session is "terminated."

Terminating a session is done by writing a non-zero value to the radacct table's AcctStopTime field. This can be done by sending a disconnect request to the Chillispot server for this username using "radclient". If the client is successfully disconnected by radclient, Chillispot will send the appropriate Accounting Stop packet to the radius server. If radclient fails to disconnect the user, which often indicate that the session is really "stale", the script will access the database and write the correct value to the radacct table's AcctStopTime field.

Here's what I did:

1. Create this php script in /usr/local/etc/raddb and name it mpp_disconnect.php and make it "executable".

#!/usr/local/bin/php -q

$db_host = "radius.guihulngan.net";
$db_name = "radius";
$db_user = "radiususer";
$db_pass = "radiususerpassword";

if (($db = mysql_connect($db_host, $db_user, $db_pass))==NULL) {
exit(0);
}

mysql_select_db("radius");

$mpp_max = 1;

//count how many online sessions for this user
$sql = "SELECT COUNT(*) FROM radacct WHERE Username='".$argv[1]."' AND AcctStopTime=0";

if (($result = mysql_query($sql, $db))==NULL) {
exit(0);
}

if (($row = mysql_fetch_row($result))==NULL) {
exit(0);
}

$mpp = $row[0];
if ($mpp < $mpp_max) {
exit(0);
}

//prepare to call radclient to send disconnect request to NAS$nas_type = get_connection_type($argv[2]);
$nas_target = "10.9.8.200:3799"; //this is the Chillispot ip address and COA port
$nas_secret = "testing123";

if ($mpp > 0){
//disconnect the user now and exit denying him login for now.
$rad_result = shell_exec("/root/test/radclient -c 1 -r 1 -t 3 " . $argv[1] ." $nas_target $nas_secret");

if (substr_count($rad_result, "Code 41,")>0) {
exit(0);
}
else {
//Radclient wasn't able to disconnect the user,
//so remove the stale sessionsfrom database to avoid MPP errors
$sql = "SELECT RadacctId, AcctSessionId, Username, NasPortType, AcctStopTime,AcctSessionTime FROM radacct WHERE Username='".$argv[1]."' AND AcctStopTime=0 LIMIT 1";

if (($result = mysql_query($sql, $db)) == NULL) {
exit(0);
}

if (mysql_num_rows($result)==0) {
exit(0);
}

if (($row = mysql_fetch_row($result))==NULL) {
exit(0);
}

if ($row[4]==0){ //No AcctStopTime, Fix or End using AcctStartTime and AcctSessionTime
$sql = "UPDATE radacct SET AcctStopTime=ADDDATE(AcctStartTime, INTERVALAcctSessionTime SECOND),AcctTerminateCause='User-Reset' WHERE RadAcctId=".$row[0]." LIMIT 1";
} else { //No AcctSessionTime, Fix or End using AcctStartTime and AcctStopTime
$sql = "UPDATE radacct SET AcctSessionTime = UNIX_TIMESTAMP(AcctStopTime) - UNIX_TIMESTAMP(AcctStartTime), AcctTerminateCause = 'User-Reset' WHERE RadAcctId = ".$row[0]."LIMIT 1";
}

if (($result = mysql_query($sql, $db))==NULL) {
exit(0);
}
}
}
exit(0);
?>



2.) Edit radiusd.conf to include these lines in modules section:


modules {

...

exec mpp_disconnect {
wait = yes
program = "${confdir}/mpp_disconnect.php %{User-Name} %{Nas-Port-Type}
%{Acct-Session-Id:-NOSESSIONID}"
packet_type = Access-Accept
}

...

}


and in post-auth section:

post-auth {

...

mpp_disconnect

...

}


3. Remove any "Simultaneous-Use" attributes from your users. Your script won't be executed when the user is rejected when Simultaneous-Use restriction is violated.

4. Restart radiusd.

I modified my script to log debugging information to a file to simplify troubleshooting. By the way, this example is for FreeBSD but it should work in other platforms too.

This way, the stale session resolution is done transparently... and no more client calls at night.

Cheers!

2 comments:

LAYONGDAPIT said...

Congratulations koverlord for sharing and offering your talent to your hometown. A lot of opportunities are waving for you with a considerable purse but you chose your sleeping town over the rest. More power to you and your family!!!!!

koverlord said...

Thanks!