From f5ca42bf2f983ba36753d32d662f1bd17bfbe88b Mon Sep 17 00:00:00 2001 From: Robert Anderson <randerson@lbsd.net> Date: Wed, 27 May 2009 11:58:54 +0000 Subject: [PATCH] Added topup support --- .../modules/config/mod_config_sql_topups.pm | 138 ++++++++++++++++++ .../modules/features/mod_feature_capping.pm | 126 ++++++++++++---- smradiusd.conf.testing | 1 + 3 files changed, 240 insertions(+), 25 deletions(-) create mode 100644 smradius/modules/config/mod_config_sql_topups.pm diff --git a/smradius/modules/config/mod_config_sql_topups.pm b/smradius/modules/config/mod_config_sql_topups.pm new file mode 100644 index 00000000..6494da0b --- /dev/null +++ b/smradius/modules/config/mod_config_sql_topups.pm @@ -0,0 +1,138 @@ +# SQL config database support +# Copyright (C) 2007-2009, AllWorldIT +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +package mod_config_sql_topups; + +use strict; +use warnings; + +# Modules we need +use smradius::constants; +use smradius::logging; +use smradius::dblayer; +use smradius::util; +use smradius::attributes; +use Data::Dumper; + +# Exporter stuff +require Exporter; +our (@ISA,@EXPORT,@EXPORT_OK); +@ISA = qw(Exporter); +@EXPORT = qw( +); +@EXPORT_OK = qw( +); + + + +# Plugin info +our $pluginInfo = { + Name => "SQL Topups Database", + Init => \&init, + + # User database + Config_get => \&getTopups +}; + +# Module config +my $config; + +## @internal +# Initialize module +sub init +{ + my $server = shift; + my $scfg = $server->{'inifile'}; + + + # Enable support for database + if (!$server->{'smradius'}->{'database'}->{'enabled'}) { + $server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Enabling database support."); + $server->{'smradius'}->{'database'}->{'enabled'} = 1; + } + + # Default configs... + $config->{'get_topups_query'} = ' + SELECT + @TP@topups.ValidFrom, + @TP@topups.ValidTo, + @TP@topups.Value + FROM + @TP@topups, + @TP@users + WHERE + @TP@topups.UserID = @TP@users.ID + AND + @TP@users.Username = ? + '; + + + # Setup SQL queries + if (defined($scfg->{'mod_topups_sql'})) { + # Pull in queries + if (defined($scfg->{'mod_topups_sql'}->{'get_config_query'}) && + $scfg->{'mod_topups_sql'}->{'get_config_query'} ne "") { + if (ref($scfg->{'mod_topups_sql'}->{'get_config_query'}) eq "ARRAY") { + $config->{'get_config_query'} = join(' ',@{$scfg->{'mod_config_sql'}->{'get_config_query'}}); + } else { + $config->{'get_config_query'} = $scfg->{'mod_config_sql'}->{'get_config_query'}; + } + + } + } +} + + +## @getTopups +# Try to get topup information +# +# @param server Server object +# @param user User +# @param packet Radius packet +# +# @return Result +sub getTopups +{ + my ($server,$user,$packet) = @_; + + # Set up dbDoParams + my @dbDoParams = ($config->{'get_topups_query'},$packet->attr('User-Name')); + + # Query database + my $sth = DBSelect(@dbDoParams); + if (!$sth) { + $server->log(LOG_ERR,"Failed to get topup information: ".smradius::dblayer::Error()); + return MOD_RES_NACK; + } + + # Fetch items + my $topupTotal = 0; + while (my $row = $sth->fetchrow_hashref()) { + $topupTotal += $row->{'value'}; + } + + # Add to ConfigAttributes + processConfigAttribute($server,$user->{'ConfigAttributes'},{ 'Name' => 'SMRadius-Capping-Traffic-Topup', 'Operator' => ':=', 'Value' => $topupTotal }); + + DBFreeRes($sth); + + return MOD_RES_ACK; +} + + +1; +# vim: ts=4 diff --git a/smradius/modules/features/mod_feature_capping.pm b/smradius/modules/features/mod_feature_capping.pm index 84ef4447..4557d9ec 100644 --- a/smradius/modules/features/mod_feature_capping.pm +++ b/smradius/modules/features/mod_feature_capping.pm @@ -24,6 +24,7 @@ use warnings; use smradius::constants; use smradius::logging; use smradius::util; +use Date::Parse; # Exporter stuff require Exporter; @@ -40,7 +41,7 @@ our (@ISA,@EXPORT,@EXPORT_OK); our $pluginInfo = { Name => "User Capping Feature", Init => \&init, - + # Authentication hook 'Feature_Post-Authentication_hook' => \&post_auth_hook, @@ -52,7 +53,7 @@ our $pluginInfo = { # Some constants my $TRAFFIC_LIMIT_KEY = 'SMRadius-Capping-Traffic-Limit'; my $UPTIME_LIMIT_KEY = 'SMRadius-Capping-UpTime-Limit'; - +my $TRAFFIC_TOPUPS_KEY = 'SMRadius-Capping-Traffic-Topup'; ## @internal # Initialize module @@ -75,9 +76,9 @@ sub post_auth_hook my ($server,$user,$packet) = @_; $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] POST AUTH HOOK"); - + my ($trafficLimit,$timeLimit); - + # Compare uptime limit if (defined($user->{'Attributes'}->{$UPTIME_LIMIT_KEY})) { $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$UPTIME_LIMIT_KEY."' is defined"); @@ -87,15 +88,17 @@ sub post_auth_hook if ($user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) { $timeLimit = $user->{'Attributes'}->{$UPTIME_LIMIT_KEY}; } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'}."' is NOT a numeric value"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'}. + "' is NOT a numeric value"); } } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}."'"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '". + $user->{'Attributes'}->{$UPTIME_LIMIT_KEY}."'"); } } - # Compare SMRadius-Capping-Traffic-Limit + # Compare traffic limit if (defined($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY})) { $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_LIMIT_KEY."' is defined"); # Operator: += @@ -104,14 +107,16 @@ sub post_auth_hook if ($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) { $trafficLimit = $user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}; } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'}."' is NOT a numeric value"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'}. + "' is NOT a numeric value"); } } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}."'"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '". + $user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}."'"); } } - # Check if we need to get the users' usage + # Check if we need to get the users' usage my $accountingUsage; if (defined($timeLimit) || defined($trafficLimit)) { # Loop with plugins to find anyting supporting getting of usage @@ -134,15 +139,48 @@ sub post_auth_hook # Check values against limits if (defined($timeLimit)) { if ($accountingUsage->{'TotalTimeUsage'} >= $timeLimit->{':='}->{'Value'}) { - $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage exceeds ".$timeLimit->{':='}->{'Value'}.", returning [NACK]"); + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage exceeds ".$timeLimit->{':='}->{'Value'}.", rejecting"); # Exceeding maximum, must be disconnected return MOD_RES_NACK; } } + + my $topupAmount = 0; if (defined($trafficLimit)) { - if ($accountingUsage->{'TotalDataUsage'} >= $trafficLimit->{':='}->{'Value'}) { - $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage exceeds ".$trafficLimit->{':='}->{'Value'}.", returning [NACK]"); - # Exceeding maximum, must be disconnected + + # Get topups + if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY})) { + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' is defined"); + # Operator: += + if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0])) { + # Is it a number? + if ($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0] =~ /^[0-9]+$/) { + $topupAmount = $user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0]; + } else { + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0]. + "' is NOT a numeric value"); + } + } else { + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' has no value"); + } + } + + # Set allowed traffic usage + my $alteredTrafficLimit; + if ($topupAmount > 0) { + $alteredTrafficLimit = $trafficLimit->{':='}->{'Value'} + $topupAmount; + } else { + $alteredTrafficLimit = $trafficLimit->{':='}->{'Value'}; + } + + # Bandwidth usage + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}. + "Mb (Cap: ".$trafficLimit->{':='}->{'Value'}."Mb, Topups: ".$topupAmount."Mb)"); + + # If bandwidth limit exceeded, cap user + if ($accountingUsage->{'TotalDataUsage'} >= $alteredTrafficLimit) { + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage of ".$accountingUsage->{'TotalDataUsage'}. + "Mb exceeds allowed limit of ".$alteredTrafficLimit."Mb. Capped."); return MOD_RES_NACK; } } @@ -163,6 +201,10 @@ sub post_acct_hook { my ($server,$user,$packet) = @_; + + # Exceeding maximum, must be disconnected + return MOD_RES_SKIP if ($packet->attr('Acct-Status-Type') ne "Alive"); + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] POST ACCT HOOK"); my ($trafficLimit,$timeLimit); @@ -176,10 +218,12 @@ sub post_acct_hook if ($user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) { $timeLimit = $user->{'Attributes'}->{$UPTIME_LIMIT_KEY}; } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'}."' is NOT a numeric value"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'}. + "' is NOT a numeric value"); } } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}."'"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '". + $user->{'Attributes'}->{$UPTIME_LIMIT_KEY}."'"); } } @@ -193,14 +237,16 @@ sub post_acct_hook if ($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) { $trafficLimit = $user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}; } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'}."' is NOT a numeric value"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'}. + "' is NOT a numeric value"); } } else { - $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}."'"); + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '". + $user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}."'"); } } - # Check if we need to get the users' usage + # Check if we need to get the users' usage my $accountingUsage; if (defined($timeLimit) || defined($trafficLimit)) { # Loop with plugins to find anyting supporting getting of usage @@ -223,19 +269,49 @@ sub post_acct_hook # Check values against limits if (defined($timeLimit)) { if ($accountingUsage->{'TotalTimeUsage'} >= $timeLimit->{':='}->{'Value'}) { - $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage exceeds ".$timeLimit->{':='}->{'Value'}.", returning [NACK]"); + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage exceeds ".$timeLimit->{':='}->{'Value'}.", rejecting"); # Exceeding maximum, must be disconnected return MOD_RES_NACK; } } + my $topupAmount = 0; if (defined($trafficLimit)) { - if ($accountingUsage->{'TotalDataUsage'} >= $trafficLimit->{':='}->{'Value'}) { - $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage exceeds ".$trafficLimit->{':='}->{'Value'}.", returning [NACK]"); - # Exceeding maximum, must be disconnected - if ($packet->attr('Acct-Status-Type') eq "Alive") { - return MOD_RES_NACK; + + # Get topups + if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY})) { + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' is defined"); + # Operator: += + if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0])) { + # Is it a number? + if ($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0] =~ /^[0-9]+$/) { + $topupAmount = $user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0]; + } else { + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0]. + "' is NOT a numeric value"); + } + } else { + $server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' has no value"); } } + + # Set allowed traffic usage + my $alteredTrafficLimit; + if ($topupAmount > 0) { + $alteredTrafficLimit = $trafficLimit->{':='}->{'Value'} + $topupAmount; + } else { + $alteredTrafficLimit = $trafficLimit->{':='}->{'Value'}; + } + + # Bandwidth usage + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}. + "Mb (Cap: ".$trafficLimit->{':='}->{'Value'}."Mb, Topups: ".$topupAmount."Mb)"); + + # If bandwidth limit exceeded, cap user + if ($accountingUsage->{'TotalDataUsage'} >= $alteredTrafficLimit) { + $server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage of ".$accountingUsage->{'TotalDataUsage'}. + "Mb exceeds allowed limit of ".$alteredTrafficLimit."Mb. Capped."); + return MOD_RES_NACK; + } } return MOD_RES_ACK; diff --git a/smradiusd.conf.testing b/smradiusd.conf.testing index 496bbdc9..c534f525 100644 --- a/smradiusd.conf.testing +++ b/smradiusd.conf.testing @@ -110,6 +110,7 @@ EOT [system] plugins=<<EOT mod_config_sql +mod_config_sql_topups EOT -- GitLab