Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • smradius/smradius
  • centiva-shail/smradius
  • nkukard/smradius
3 results
Show changes
Commits on Source (535)
stages:
- quality
- tests
- install
code-quality:
stage: quality
script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get dist-upgrade -y
- apt-get install -y perl-modules
- apt-get install -y make
- apt-get install -y libperl-critic-perl
- perlcritic --gentle "$CI_PROJECT_DIR"
make-test:
stage: tests
script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get dist-upgrade -y
- apt-get install -y git make
- apt-get install -y libdevel-cover-perl libpod-coverage-perl libtest-most-perl
- apt-get install -y libnet-server-perl libconfig-inifiles-perl libdatetime-perl libcache-fastmmap-perl libtimedate-perl
libcrypt-des-perl libcrypt-rc4-perl libdigest-sha-perl libdigest-md4-perl libmime-lite-perl
- apt-get install -y mariadb-server
# Start services and create dirs we need
- service mysql start
- mkdir /var/run/smradius
# Update our dependencies
- cd "$CI_PROJECT_DIR"
- ./update-git-modules
- perl -MCPAN -e 'install Math::Expression' < /dev/null
# Build Makefile and make
- perl Makefile.PL
- make
# Convert DB into MySQL
- blib/script/convert-tsql MySQL database/core.tsql > database/core.mysql
- blib/script/convert-tsql MySQL database/users-accounting-summary.tsql > database/users-accounting-summary.mysql
- blib/script/convert-tsql MySQL database/wisp.tsql > database/wisp.mysql
# Load SQL into DB
- echo "CREATE DATABASE smradiustest;" | mysql -u root
- mysql -u root smradiustest < database/core.mysql
- mysql -u root smradiustest < database/users-accounting-summary.mysql
- mysql -u root smradiustest < database/wisp.mysql
# Sort out config file
- cp smradiusd.conf smradiusd.conf.test
- perl -pi -e 's/database=smradius/database=smradiustest/' smradiusd.conf.test
# Run tests, exclude all but smradius
- DBTESTS=1 cover -test -ignore_re '.*' -select_re '^blib\/lib\/smradius\/' | tee devel-coverage.txt
- grep "^Total" devel-coverage.txt | awk '{ print "(" $8 "%) covered" }'
make-install:
stage: install
script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get update
- apt-get dist-upgrade -y
- apt-get install -y git make
- apt-get install -y mariadb-server
- cd "$CI_PROJECT_DIR"
- ./update-git-modules
- perl Makefile.PL
- make install
[submodule "webgui/awitef"]
path = webgui/awitef
url = https://gitlab.devlabs.linuxassist.net/awit-frameworks/awit-extjs-framework.git
[submodule "awitpt"]
path = 3rdparty/awitpt
url = https://gitlab.devlabs.linuxassist.net/awit-frameworks/awit-perl-toolkit.git
branch = v1.0.x
# Makefile
# Copyright (C) 2014-2016, 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.
use strict;
use warnings;
use ExtUtils::MakeMaker;
use File::Find;
my @additionalDirs = ();
find(
{
wanted => sub {
if (/^Makefile.PL$/) {
my $dirname = $File::Find::dir;
if ($dirname ne ".") {
$dirname =~ s,\./,,;
push(@additionalDirs,$dirname);
}
};
},
bydepth => 1,
},
".",
);
WriteMakefile(
'NAME' => 'SMRadius-3rdParty',
'DIR' => \@additionalDirs,
);
# vim: ts=4
awitpt @ 6944c201
Subproject commit 6944c2017372a13a334f902fdd8461e171393949
Project Lead:
=============
Nigel Kukard <nkukard@lbsd.net>
Contributors:
=============
Robert Anderson
Wembu Pongo
......@@ -27,7 +27,7 @@ Enhanced features:
* Plugin: Topups
* Plugin: Auto-topups
* Plugin: Usage/Time caps
* Plugin: Prepaid accounting based on usage/time
* Plugin: Prepaid accounting based on usage/time
* Plugin: Creation of accounting START records when no START record has been received but an interim update has - helps on slow/lossly links
* Plugin: Notifications, % based or approximate time based
* Plugin: User blacklists
......
......@@ -6,6 +6,13 @@ Installing SMRadius.
- Net::Server >= 0.96
- Config::IniFiles (Debian based: libconfig-inifiles-perl, RPM based: perl-Config-IniFiles)
- Cache::FastMmap (Debian based: libcache-fastmmap-perl, RPM based: perl-Cache-FastMmap)
- DateTime (requires: perl-Class-Singleton)
- TimeDate
- Crypt::DES (Debian based: libcrypt-des-perl)
- Crytpt::RC4 (Debian based: libcrypt-rc4-perl)
- Digest::SHA1 (Debian based: libdigest-sha-perl)
- Digest::MD4 (Debian based: libdigest-md4-perl)
- Math::Expression
* Requirements for webui
- PHP v5+
......@@ -13,3 +20,13 @@ Installing SMRadius.
1. Setup system dirs
mkdir /var/log/smradius
mkdir /var/run/smradius
If you are running smradius as an unpriv user, then ...
chown smradius.smradius /var/log/smradius /var/run/smradius
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
......@@ -56,7 +56,7 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
......@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
......@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
......
# SMRadius Utility Functions
# Copyright (C) 2007-2009, AllWorldIT
# Makefile
# Copyright (C) 2014-2016, 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
......@@ -16,70 +16,34 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
## @class smradius::util
# Utility functions
package smradius::util;
use strict;
use warnings;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(
niceUndef
templateReplace
);
## @fn niceUndef($string)
# If string defined return 'string', or if undefined return -undef-
#
# @param string String to check
#
# @return Return 'string' if defined, or -undef- otherwise
sub niceUndef
{
my $string = shift;
return defined($string) ? "'$string'" : '-undef-';
}
use ExtUtils::MakeMaker;
use File::Find;
## @fn templateReplace($string,$hashref)
# Template string replacer function
#
# @param string String to replace template items in
# @param hashref Hashref containing the hash of tempalte items & values
#
# @return String with replaced items
sub templateReplace
{
my ($string,$hashref) = @_;
WriteMakefile(
'NAME' => 'SMRadius',
my @valueArray = ();
'VENDORPREFIX' => '/opt/smradius',
'INSTALLDIRS' => 'vendor',
# Replace blanks
while (my ($entireMacro,$section,$item,$default) = ($string =~ /(\%{([a-z]+)\.([a-z0-9\-]+)(?:=([^}]+))?})/i )) {
# Replace macro with ?
$string =~ s/$entireMacro/\?/;
'VERSION_FROM' => "lib/smradius/version.pm",
# Get value to substitute
my $value = defined($hashref->{$section}->{$item}) ? $hashref->{$section}->{$item} : $default;
'DIR' => ["3rdparty"],
# Add value onto our array
push(@valueArray,$value);
}
return ($string, @valueArray);
}
'EXE_FILES' => [qw(
bin/smradiusd
bin/smadmin
bin/smradclient
)],
);
1;
# vim: ts=4
# Copyright (C) 2007-2016, AllWorldIT
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The FreeRADIUS Server Project
#
# The following attribute examples and ideas is a derivative work of
# the files, documentation and operators used in the FreeRADIUS Server
# Project, which is licensed GPLv2. This file therefore is also licensed
# under the terms of the GNU Public License, verison 2
#
# 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.
Op Example and documentation
------------------------------------
......
[![build status](https://gitlab.devlabs.linuxassist.net/smradius/smradius/badges/master/build.svg)](https://gitlab.devlabs.linuxassist.net/smradius/smradius/commits/master)
[![coverage report](https://gitlab.devlabs.linuxassist.net/smradius/smradius/badges/master/coverage.svg)](https://gitlab.devlabs.linuxassist.net/smradius/smradius/commits/master)
\ No newline at end of file
smradiusd:
* Eval around actual code in process_request
* Eval around actual code in process_request?
* Strip off whitespaces around attributes
* Create a raddbpath config option which is prepended to dict paths
WebUI:
WiSP -> User List -> Edit: Must be able to clear fields (MAC Address)
WiSP User Add -> Check if only certain fields are blank. Might want to add user without MAC Address.
wisp-user-edit.php - proper sql error handling, use 1 query to pull in all attribs and check them in a hash, nigel to then cleanup
wisp-multiuser-add: only generating two users with error - needs fix
Realm config
- config global defaults and defaults for realms
<DEFAULT>
@realm
* Configurable 'use defaults for POD/CoA' we may not want to send these
smadmin:
* Ability to run smadmin before the end of current month and updating the records as necessary at a later stage
2011-11-08:
/* change accounting index accounting_idx4 and accounting_idx5 */
DROP INDEX accounting_idx4 ON accounting;
DROP INDEX accounting_idx5 ON accounting;
CREATE INDEX accounting_idx5 ON accounting (Username,AcctSessionID,NASIPAddress,NASPort,PeriodKey);
CREATE INDEX accounting_idx4 ON accounting (Username,AcctSessionID,NASIPAddress,NASPort);
2011-11-04:
/* accounting_stop_status_query */
CREATE INDEX accounting_idx4 ON accounting (Username,AcctSessionID,NASIPAddress,NASPortID);
/* accounting_update_query */
CREATE INDEX accounting_idx5 ON accounting (Username,AcctSessionID,NASIPAddress,NASPortID,PeriodKey);
2011-10-14:
ALTER TABLE accounting_summary ADD COLUMN PeriodKey2 VARCHAR(255) AFTER PeriodKey;
UPDATE accounting_summary set PeriodKey2 = DATE_FORMAT(PeriodKey,'%Y-%m');
ALTER TABLE accounting_summary DROP COLUMN PeriodKey;
ALTER TABLE accounting_summary CHANGE COLUMN PeriodKey2 PeriodKey VARCHAR(255);
2011-03-22:
Add users_data table for various functions
CREATE TABLE @PREFIX@users_data (
ID @SERIAL_TYPE@,
UserID @INT_UNSIGNED@,
LastUpdated DATETIME,
Name VARCHAR(255),
Value VARCHAR(255),
UNIQUE (UserID,Name)
);
2011-01-11:
Move configuration file item "use_packet_timestamp=" to "[radius]" section
mkdir /var/log/smradius
mkdir /var/run/smradius
If you are running smradius as an unpriv user, then ...
chown smradius.smradius /var/log/smradius /var/run/smradius
---
r574:
# Database
ALTER TABLE topups ADD COLUMN SMAdminDepletedOn DATETIME;
ALTER TABLE topups_summary ADD COLUMN SMAdminDepletedOn DATETIME;
r538:
# Database
ALTER TABLE accounting_summary DROP COLUMN AcctSessionTime;
ALTER TABLE accounting_summary DROP COLUMN AcctInputOctets;
ALTER TABLE accounting_summary DROP COLUMN AcctInputGigawords;
ALTER TABLE accounting_summary DROP COLUMN AcctOutputOctets;
ALTER TABLE accounting_summary DROP COLUMN AcctOutputGigawords;
ALTER TABLE accounting_summary ADD COLUMN TotalSessionTime INT UNSIGNED;
ALTER TABLE accounting_summary ADD COLUMN TotalInput INT UNSIGNED;
ALTER TABLE accounting_summary ADD COLUMN TotalOutput INT UNSIGNED;
r509:
# Database
ALTER TABLE accounting ADD PeriodKey VARCHAR(7);
ALTER TABLE accounting_summary DROP COLUMN AcctInputPackets;
# smradiusd.conf
Modified:
accounting_start_query=<<EOT
INSERT INTO
@TP@accounting
(
Username,
ServiceType,
FramedProtocol,
NASPort,
NASPortType,
CallingStationID,
CalledStationID,
NASPortID,
AcctSessionID,
FramedIPAddress,
AcctAuthentic,
EventTimestamp,
AcctStatusType,
NASIdentifier,
NASIPAddress,
AcctDelayTime,
PeriodKey
)
VALUES
(
%{request.User-Name},
%{request.Service-Type},
%{request.Framed-Protocol},
%{request.NAS-Port},
%{request.NAS-Port-Type},
%{request.Calling-Station-Id},
%{request.Called-Station-Id},
%{request.NAS-Port-Id},
%{request.Acct-Session-Id},
%{request.Framed-IP-Address},
%{request.Acct-Authentic},
%{request.Timestamp},
%{request.Acct-Status-Type},
%{request.NAS-Identifier},
%{request.NAS-IP-Address},
%{request.Acct-Delay-Time},
%{query.PeriodKey}
)
EOT
accounting_update_query=<<EOT
UPDATE
@TP@accounting
SET
AcctSessionTime = %{query.AcctSessionTime},
AcctInputOctets = %{query.AcctInputOctets},
AcctInputGigawords = %{query.AcctInputGigawords},
AcctInputPackets = %{query.AcctInputPackets},
AcctOutputOctets = %{query.AcctOutputOctets},
AcctOutputGigawords = %{query.AcctOutputGigawords},
AcctOutputPackets = %{query.AcctOutputPackets},
AcctStatusType = %{request.Acct-Status-Type}
WHERE
Username = %{request.User-Name}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
AND PeriodKey = %{query.PeriodKey}
EOT
accounting_stop_query=<<EOT
UPDATE
@TP@accounting
SET
AcctSessionTime = %{query.Acct-Session-Time},
AcctInputOctets = %{query.Acct-Input-Octets},
AcctInputGigawords = %{query.Acct-Input-Gigawords},
AcctInputPackets = %{query.Acct-Input-Packets},
AcctOutputOctets = %{query.Acct-Output-Octets},
AcctOutputGigawords = %{query.Acct-Output-Gigawords},
AcctOutputPackets = %{query.Acct-Output-Packets}
WHERE
Username = %{request.User-Name}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
AND PeriodKey = %{query.PeriodKey}
EOT
Added:
accounting_update_get_records_query=<<EOT
SELECT
SUM(AcctInputOctets) AS InputOctets,
SUM(AcctInputPackets) AS InputPackets,
SUM(AcctOutputOctets) AS OutputOctets,
SUM(AcctOutputPackets) AS OutputPackets,
SUM(AcctInputGigawords) AS InputGigawords,
SUM(AcctOutputGigawords) AS OutputGigawords,
SUM(AcctSessionTime) AS SessionTime,
PeriodKey
FROM
@TP@accounting
WHERE
Username = %{request.User-Name}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
GROUP BY
PeriodKey
ORDER BY
ID ASC
EOT
accounting_stop_status_query=<<EOT
UPDATE
@TP@accounting
SET
AcctStatusType = %{request.Acct-Status-Type},
AcctTerminateCause = %{request.Acct-Terminate-Cause}
WHERE
Username = %{request.User-Name}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
EOT
#!/usr/bin/perl -w
# Author: Nigel Kukard <nkukard@lbsd.net>
# Date: 17/04/2007
# Desc: Accounting filter for GNU Radius
# License: GPL
use strict;
use Benchmark;
use Getopt::Long;
use DateTime;
use Time::HiRes qw( gettimeofday tv_interval );
use MIME::Lite;
# Set library directory
use lib qw(../../);
use sm::config;
use sm::dblayer;
# Radius stuff
use Authen::Radius;
# Common stuff
require("common.pm");
use Data::Dumper;
# Notify constants
use constant {
NOTIFY_CHECK => 1,
NOTIFY_RESET => 2,
};
my %optctl = ();
GetOptions(\%optctl, "help");
# Check if user wants usage
if (defined($optctl{'help'})) {
displayUsage();
}
# Open up logfile
my $logfile = "/var/log/radius/acct-filter";
open(FH,">> $logfile") or die "Failed to open '$logfile': $!";
# Load radius dictionaries
Authen::Radius->load_dictionary("raddb/dictionary");
# Databases
my $dbh; # Authentication
my $dbh_log; # Logs
# Get db handle
$dbh = sm::dbilayer->new($cfg_db_DSN, $cfg_db_Username, $cfg_db_Password);
if (!$dbh) {
print(STDERR "Error creating database object: ".sm::dbilayer->internalErr());
exit 1;
}
# Connect to database
if ($dbh->connect() != 0) {
print(STDERR "Error connecting to database: ".$dbh->err);
exit 1;
}
# Check if we must use split db's
if (defined($cfg_radiuslog_db_DSN)) {
# Get log db handle
$dbh_log = sm::dbilayer->new($cfg_radiuslog_db_DSN, $cfg_radiuslog_db_Username, $cfg_radiuslog_db_Password);
if (!$dbh_log) {
print(STDERR "Error creating database object: ".sm::dbilayer->internalErr());
exit 1;
}
# Connect to database
if ($dbh_log->connect() != 0) {
print(STDERR "Error connecting to database: ".$dbh_log->err);
exit 1;
}
# If not use the main DB
} else {
$dbh_log = $dbh;
}
# Signal handler for dead children
use POSIX "WNOHANG";
sub REAPER {
my $child;
# If a second child dies while in the signal handler caused by the
# first death, we won't get another signal. So must loop here else
# we will leave the unreaped child as a zombie. And the next time
# two children die we get another zombie. And so on.
while (($child = waitpid(-1,WNOHANG)) > 0) {
# $Kid_Status{$child} = $?;
1;
}
$SIG{CHLD} = \&REAPER; # still loathe sysV
}
$SIG{CHLD} = \&REAPER;
# No buffering
select((select(FH), $| = 1)[0]);
select((select(STDOUT), $| = 1)[0]);
# Handled requests
my $requests = 0;
# Loop with input
while (my $line = <STDIN>) {
my %acct;
my @acct;
# Inc number of requests
$requests++;
# Munch off \n
chomp($line);
# Check number of results
if ((@acct = split /:/, $line) != 21) {
print(FH "ERROR: Number of params from radiusd: ".(@acct)."/$line\n");
goto END;
}
# Pull in request
($acct{'User-Name'},$acct{'Status-Type'},$acct{'Acct-Session-Id'},$acct{'NAS-IP-Address'},$acct{'NAS-Port-Type'},$acct{'NAS-Port'},
$acct{'Called-Station-Id'},$acct{'Calling-Station-Id'},$acct{'Delay'},$acct{'Acct-Session-Time'},
$acct{'Acct-Input-Octets'},$acct{'Acct-Output-Octets'},$acct{'Acct-Input-Gigawords'},$acct{'Acct-Output-Gigawords'},
$acct{'Ascend-Xmit-Rate'},$acct{'Ascend-Data-Rate'},$acct{'Framed-IP-Address'},$acct{'Connect-Info'},$acct{'Service-Type'},
$acct{'Class'},$acct{'Acct-Terminate-Cause'}) = @acct;
# Pull timestamp
my $dt = DateTime->from_epoch( epoch => time() );
$acct{'Timestamp'} = $dt->strftime('%Y-%m-%d %H:%M:%S');
my $timer0 = [gettimeofday];
# Grab user details
my $userData = getUser($dbh,$acct{'User-Name'});
if (ref $userData ne "HASH") {
print(FH "ERROR: $userData\n");
$userData->{'AgentID'} = 0;
$userData->{'RadiusClassID'} = 0;
$userData->{'UsageCap'} = 0;
}
printf(FH 'ACCT - AgentID: %s, ClassID: %s, User-Name: %s, Status-Type: %s, Timestamp: %s, Acct-Session-Id: %s, NAS-IP-Address: %s, NAS-Port-Type: %s, NAS-Port: %s, Called-Station-Id: %s, Calling-Station-Id: %s, Delay: %s, Acct-Session-Time: %s, Acct-Input-Octets: %s, Acct-Output-Octets: %s, Acct-Input-Gigawords: %s, Acct-Output-Gigawords: %s, Ascend-Xmit-Rate: %s, Ascend-Data-Rate: %s, Framed-IP-Address: %s, Connect-Info: %s, Service-Type: %s, Class: %s, Acct-Terminate-Cause: %s'."\n",$userData->{'AgentID'},$userData->{'RadiusClassID'},$acct{'User-Name'},$acct{'Status-Type'},$acct{'Timestamp'},$acct{'Acct-Session-Id'},$acct{'NAS-IP-Address'},$acct{'NAS-Port-Type'},$acct{'NAS-Port'},$acct{'Called-Station-Id'},$acct{'Calling-Station-Id'},$acct{'Delay'},$acct{'Acct-Session-Time'},$acct{'Acct-Input-Octets'},$acct{'Acct-Output-Octets'},$acct{'Acct-Input-Gigawords'},$acct{'Acct-Output-Gigawords'},$acct{'Ascend-Xmit-Rate'},$acct{'Ascend-Data-Rate'},$acct{'Framed-IP-Address'},$acct{'Connect-Info'},$acct{'Service-Type'},$acct{'Class'},$acct{'Acct-Terminate-Cause'});
# IF ADSL (ADSL specific stuff)
if ($acct{'NAS-Port-Type'} eq "5") {
# Calculate dates
my $date = DateTime->from_epoch( epoch => time() );
my $today = $date->ymd();
$date->set_day(1);
my $thismonth = $date->ymd();
$date->add( months => 1);
my $nextmonth = $date->ymd();
my $extraQuery = "";
# NULL - uncapped, no limits
if (!defined($userData->{'UsageCap'})) {
$extraQuery = "AND Timestamp > ".$dbh->quote($thismonth)." AND Timestamp < ".$dbh->quote($nextmonth);
# > 0 - normal cap, check usage for this month, check topups for this month
# Calculate cap user has (acctinputoctets + (2^32 * gigawords)) /1024 / 1024
# Calculate usage user has
} elsif ($userData->{'UsageCap'} > 0) {
$extraQuery = "AND Timestamp > ".$dbh->quote($thismonth)." AND Timestamp < ".$dbh->quote($nextmonth);
# 0 - topup account
} elsif ($userData->{'UsageCap'} == 0) {
}
# Grab users usage
my $usageData = getUsage($dbh_log,$acct{'User-Name'},$extraQuery);
if (ref $usageData ne "HASH") {
print(FH "ERROR: $usageData\n");
# print(STDOUT "1\n");
goto END;
}
$userData->{'TotalUsage'} = $usageData->{'Total'};
# Only total up this month
$extraQuery = "";
if (defined($userData->{'UsageCap'}) && $userData->{'UsageCap'} > 0) {
$extraQuery = "AND ValidFrom <= ".$dbh->quote($today)." AND ValidTo > ".$dbh->quote($today);
}
# Get how much we've been topped up
my $topupData = getTopups($dbh,$acct{'User-Name'},$extraQuery);
if (ref $topupData ne "HASH") {
print(FH "ERROR: $topupData\n");
# print(STDOUT "1\n");
goto END;
}
$userData->{'Topups'} = $topupData->{'Total'};
$userData->{'SessUsage'} = 0;
# If we updating or disconnecting, calculate the session usage so far
if ($acct{'Status-Type'} == 2 || $acct{'Status-Type'} == 3) {
# Check how much data we used in this session
my $sessUsage = 0;
if (defined($acct{'Acct-Input-Octets'}) && $acct{'Acct-Input-Octets'} > 0) {
$sessUsage += $acct{'Acct-Input-Octets'} / 1024 / 1024;
}
if (defined($acct{'Acct-Input-Gigawords'}) && $acct{'Acct-Input-Gigawords'} > 0) {
$sessUsage += $acct{'Acct-Input-Gigawords'} * 4096;
}
# Add up output
if (defined($acct{'Acct-Output-Octets'}) && $acct{'Acct-Output-Octets'} > 0) {
$sessUsage += $acct{'Acct-Output-Octets'} / 1024 / 1024;
}
if (defined($acct{'Acct-Output-Gigawords'}) && $acct{'Acct-Output-Gigawords'} > 0) {
$sessUsage += $acct{'Acct-Output-Gigawords'} * 4096;
}
$userData->{'SessUsage'} = ceil($sessUsage);
}
# Print usage stats
printf(FH ' - Usage => Total: %s, Session: %s, Cap: %s, Topups: %s'."\n",
$userData->{'TotalUsage'},
$userData->{'SessUsage'},
defined($userData->{'UsageCap'}) ? $userData->{'UsageCap'} : "uncapped",
$userData->{'Topups'},
);
# Capping & usage predictions
my $totalCap = !defined($userData->{'UsageCap'}) ? 0 : $userData->{'UsageCap'} + $userData->{'Topups'};
if (defined($userData->{'UsageCap'}) && $userData->{'UsageCap'} >= 0) {
my $exceeded = 0;
# Checking if we updating or stopping the session, if we are, check capping
if ($totalCap <= $userData->{'TotalUsage'} && ($acct{'Status-Type'} == 2 || $acct{'Status-Type'} == 3)) {
print(FH " - TEST: User has exceeded cap by ".($userData->{'TotalUsage'} - $totalCap)."Mbyte\n");
# If this is an update, user is still logged in, so disconnect them
if ($userData->{'CappingType'} == 1 && $acct{'Status-Type'} == 3) {
print(FH " - TEST: User is still logged in, disconnecting\n");
disconnectUser($dbh,\*FH,$userData,\%acct);
# If user just got kicked off, notify them
} elsif ($userData->{'CappingType'} == 1 && $acct{'Status-Type'} == 2) {
print(FH " - TEST: Notifying user\n");
# We reset notifications, cause the user MUST get this and we dont mind updating him in future
notifyUser($dbh,$dbh_log,\*FH,$userData,NOTIFY_RESET,
sprintf('Username %s has been capped, please contact your ISP should you need a topup',$userData->{'Username'}),
);
}
$exceeded = 1;
}
# Check if we may exceed the cap in the next hour, we need at least 1hrs of data for accuracy
if (!$exceeded && $acct{'Acct-Session-Time'} > 3600) {
my $perSecUsage = $userData->{'SessUsage'} / $acct{'Acct-Session-Time'};
my $hrPredict = sprintf('%.2f',($perSecUsage * 3600));
print(FH " - User is predicted to use ${hrPredict}Mb in the next hour\n");
# If user is infact predicted to exceed, notify them
if ($totalCap <= $userData->{'TotalUsage'} + $hrPredict) {
print(FH " - This will exceed users cap, notifying user\n");
notifyUser($dbh,$dbh_log,\*FH,$userData,NOTIFY_CHECK,
sprintf('Username %s may be capped in the next hour based on current usage stats, please contact your ISP should you need a topup',
$userData->{'Username'}),
);
}
}
}
}
# START
if ($acct{'Status-Type'} == 1) {
# Start accounting
my $res = $dbh_log->do("
INSERT INTO radiusLogs
(
AgentID,
Username,
RadiusClassID,
UsageCap,
Topups,
Timestamp,
AcctDelayTime,
AcctSessionID,
NASIPAddress,
NASPortType,
NASPort,
CalledStationID,
CallingStationID,
ConnectInfo,
ServiceType,
Class,
FramedIPAddress,
Status
)
VALUES
(
".$dbh_log->quote($userData->{'AgentID'}).",
".$dbh_log->quote($acct{'User-Name'}).",
".$dbh_log->quote($userData->{'RadiusClassID'}).",
".$dbh_log->quote($userData->{'UsageCap'}).",
".$dbh_log->quote($userData->{'Topups'}).",
".$dbh_log->quote($acct{'Timestamp'}).",
".$dbh_log->quote($acct{'Delay'}).",
".$dbh_log->quote($acct{'Acct-Session-Id'}).",
".$dbh_log->quote($acct{'NAS-IP-Address'}).",
".$dbh_log->quote($acct{'NAS-Port-Type'}).",
".$dbh_log->quote($acct{'NAS-Port'}).",
".$dbh_log->quote($acct{'Called-Station-Id'}).",
".$dbh_log->quote($acct{'Calling-Station-Id'}).",
".$dbh_log->quote($acct{'Connect-Info'}).",
".$dbh_log->quote($acct{'Service-Type'}).",
".$dbh_log->quote($acct{'Class'}).",
".$dbh_log->quote($acct{'Framed-IP-Address'}).",
".$dbh_log->quote(1)."
)
");
if (!$res) {
print(FH "ERROR: Failed to insert radius auth fail data: ".$dbh_log->err."\n");
# print(STDOUT "1\n");
goto END;
}
# STOP
} elsif ($acct{'Status-Type'} == 2) {
# Stop accounting
my $res = $dbh_log->do("
UPDATE radiusLogs
SET
Status = ".$dbh_log->quote(3).",
NASTransmitRate = ".$dbh_log->quote($acct{'Ascend-Xmit-Rate'}).",
NASReceiveRate = ".$dbh_log->quote($acct{'Ascend-Data-Rate'}).",
AcctSessionTime = ".$dbh_log->quote($acct{'Acct-Session-Time'}).",
AcctInputOctets = ".$dbh_log->quote($acct{'Acct-Input-Octets'}).",
AcctOutputOctets = ".$dbh_log->quote($acct{'Acct-Output-Octets'}).",
AcctInputGigawords = ".$dbh_log->quote($acct{'Acct-Input-Gigawords'}).",
AcctOutputGigawords = ".$dbh_log->quote($acct{'Acct-Output-Gigawords'}).",
ConnectTermReason = ".$dbh_log->quote($acct{'Acct-Terminate-Cause'}).",
LastAccUpdate = ".$dbh_log->quote($acct{'Timestamp'})."
WHERE
Username = ".$dbh_log->quote($acct{'User-Name'})."
AND AcctSessionID = ".$dbh_log->quote($acct{'Acct-Session-Id'})."
AND NASIPAddress = ".$dbh_log->quote($acct{'NAS-IP-Address'})."
");
if (!$res) {
print(FH "ERROR: Failed to update stop accounting data: ".$dbh_log->err."\n");
# print(STDOUT "1\n");
goto END;
}
$res = 0 if ($res eq "0E0");
print(FH " - Rows updated: $res\n");
# Check if we updated duplicates, if we did, fix them
if ($res > 1) {
fixDuplicates(\%acct);
}
# UPDATE
} elsif ($acct{'Status-Type'} == 3) {
# Update accounting
my $res = $dbh_log->do("
UPDATE radiusLogs
SET
Status = ".$dbh_log->quote(2).",
NASTransmitRate = ".$dbh_log->quote($acct{'Ascend-Xmit-Rate'}).",
NASReceiveRate = ".$dbh_log->quote($acct{'Ascend-Data-Rate'}).",
AcctSessionTime = ".$dbh_log->quote($acct{'Acct-Session-Time'}).",
AcctInputOctets = ".$dbh_log->quote($acct{'Acct-Input-Octets'}).",
AcctOutputOctets = ".$dbh_log->quote($acct{'Acct-Output-Octets'}).",
AcctInputGigawords = ".$dbh_log->quote($acct{'Acct-Input-Gigawords'}).",
AcctOutputGigawords = ".$dbh_log->quote($acct{'Acct-Output-Gigawords'}).",
ConnectTermReason = ".$dbh_log->quote($acct{'Acct-Terminate-Cause'}).",
LastAccUpdate = ".$dbh_log->quote($acct{'Timestamp'})."
WHERE
Username = ".$dbh_log->quote($acct{'User-Name'})."
AND AcctSessionID = ".$dbh_log->quote($acct{'Acct-Session-Id'})."
AND NASIPAddress = ".$dbh_log->quote($acct{'NAS-IP-Address'})."
");
if (!$res) {
print(FH "ERROR: Failed to update accounting data: ".$dbh_log->err."\n");
# print(STDOUT "1\n");
goto END;
}
$res = 0 if ($res eq "0E0");
print(FH " - Rows updated: $res\n");
# Create record as it doesn't exist!
if ($res == 0) {
# Start accounting
my $res = $dbh_log->do("
INSERT INTO radiusLogs
(
AgentID,
Username,
RadiusClassID,
UsageCap,
Topups,
Timestamp,
AcctDelayTime,
AcctSessionID,
NASIPAddress,
NASPortType,
NASPort,
CalledStationID,
CallingStationID,
ConnectInfo,
ServiceType,
Class,
FramedIPAddress,
NASTransmitRate,
NASReceiveRate,
AcctSessionTime,
AcctInputOctets,
AcctOutputOctets,
AcctInputGigawords,
AcctOutputGigawords,
ConnectTermReason,
Status
)
VALUES
(
".$dbh_log->quote($userData->{'AgentID'}).",
".$dbh_log->quote($acct{'User-Name'}).",
".$dbh_log->quote($userData->{'RadiusClassID'}).",
".$dbh_log->quote($userData->{'UsageCap'}).",
".$dbh_log->quote($userData->{'Topups'}).",
".$dbh_log->quote(
$acct{'Timestamp'} - defined($acct{'Acct-Session-Time'}) ? $acct{'Acct-Session-Time'} : 0
).",
".$dbh_log->quote($acct{'Delay'}).",
".$dbh_log->quote($acct{'Acct-Session-Id'}).",
".$dbh_log->quote($acct{'NAS-IP-Address'}).",
".$dbh_log->quote($acct{'NAS-Port-Type'}).",
".$dbh_log->quote($acct{'NAS-Port'}).",
".$dbh_log->quote($acct{'Called-Station-Id'}).",
".$dbh_log->quote($acct{'Calling-Station-Id'}).",
".$dbh_log->quote($acct{'Connect-Info'}).",
".$dbh_log->quote($acct{'Service-Type'}).",
".$dbh_log->quote($acct{'Class'}).",
".$dbh_log->quote($acct{'Framed-IP-Address'}).",
".$dbh_log->quote($acct{'Ascend-Xmit-Rate'}).",
".$dbh_log->quote($acct{'Ascend-Data-Rate'}).",
".$dbh_log->quote($acct{'Acct-Session-Time'}).",
".$dbh_log->quote($acct{'Acct-Input-Octets'}).",
".$dbh_log->quote($acct{'Acct-Output-Octets'}).",
".$dbh_log->quote($acct{'Acct-Input-Gigawords'}).",
".$dbh_log->quote($acct{'Acct-Output-Gigawords'}).",
".$dbh_log->quote(0).",
".$dbh_log->quote(1)."
)
");
if (!$res) {
print(FH "ERROR: Failed to insert radius auth fail data: ".$dbh_log->err."\n");
# print(STDOUT "1\n");
goto END;
}
print(FH " - Lost accounting record created\n");
}
# Check if we updated duplicates, if we did, fix them
if ($res > 1) {
fixDuplicates(\%acct);
}
}
my $timer1 = [gettimeofday];
my $timediff = tv_interval($timer0,$timer1);
print(FH "Code execution took: ${timediff}s\n");
END:
# Check if we've handled enough requests
if ($requests >= 1000) {
print(FH "Handled enough request, terminating\n");
last;
}
# print(STDOUT "0\n");
}
close(FH);
# Function to resolve duplicates
sub fixDuplicates
{
my ($acct) = @_;
# Select duplicates
my $sth = $dbh_log->select("
SELECT ID
FROM
radiusLogs
WHERE
Username = ".$dbh_log->quote($acct->{'User-Name'})."
AND NASIPAddress = ".$dbh_log->quote($acct->{'NAS-IP-Address'})."
AND AcctSessionID = ".$dbh_log->quote($acct->{'Acct-Session-Id'})."
ORDER BY ID
LIMIT 99 OFFSET 1
");
if (!$sth) {
print(FH "ERROR: Selecting duplicates: ".$dbh_log->err."\n");
print(STDOUT "1\n");
return;
}
# Return if no rows returned
return if ($sth->rows < 1);
my @IDs = ();
# Pull in duplicates
while (my $dup = $sth->fetchrow_hashref()) {
push(@IDs,$dup->{'ID'});
}
$sth->finish();
# Remove duplicates
my $res = $dbh_log->do("
DELETE FROM radiusLogs
WHERE
ID IN (".join(',',@IDs).")
");
if (!$res) {
print(FH "ERROR: Failed to remove duplicates: ".$dbh_log->err."\n");
} else {
$res = 0 if ($res eq "0E0");
print(FH " - Duplicates removed: $res\n");
}
}
# Disconnect user
sub disconnectUser
{
my ($dbh,$fh,$userData,$acct) = @_;
# If radius classID == 0, means we don't know about this user, just return, nothing we can do
if ($userData->{'RadiusClassID'} == 0) {
print($fh " - (D) Radius class ID is zero, cannot disconnect\n");
return;
}
# Grab class data
my $classData = getClass($dbh,$userData->{'RadiusClassID'});
# Check if we got a hash, if not just return, error already reported in common.pm
if (ref $classData ne "HASH") {
print($fh " - (D) No class data, cannot disconnect: $classData\n");
return;
}
# Check if we have POD server list, if not ... return
if (!defined($classData->{'PODServers'}) || $classData->{'PODServers'} eq "") {
print($fh " - (D) No POD servers, cannot disconnect\n");
return;
}
# Loop with POD servers and add to list
my @podServers;
foreach my $i (split(/,/,$classData->{'PODServers'})) {
my %server;
# Pull out data we need
if ($i =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/([^:]+):(\d+)$/) {
($server{'IP'},$server{'Secret'},$server{'Port'}) = ($1,$2,$3);
# If we didn't understand it, bitch
} else {
print($fh " - (D) Did not understand POD server definition '$i'\n");
return;
}
# Add to list
push(@podServers,\%server);
}
# Fork off notification handler
my $child_pid;
if (!defined($child_pid = fork())) {
print($fh " - (D) ERROR: Cannot fork: $!\n");
} elsif ($child_pid) {
# I'm the parent
return;
} else {
# I'm the child
sleep(5);
# Grab a server
foreach my $server (@podServers)
{
# Fire up radius
my $r = new Authen::Radius(Host => $server->{'IP'}.':'.$server->{'Port'}, Secret => $server->{'Secret'}, Debug => 1);
if (!$r) {
print($fh "FORK - (D) ".$userData->{'Username'}.", failed to create radius object\n");
exit;
}
# Set attributes
$r->add_attributes(
{ Name => 'User-Name', Value => $userData->{'Username'} },
{ Name => 'Framed-IP-Address', Value => $acct->{'Framed-IP-Address'} },
{ Name => 'NAS-IP-Address', Value => $acct->{'NAS-IP-Address'} },
);
# Send packet
my $res = $r->send_packet(DISCONNECT_REQUEST);
print($fh "FORK - (D) ".$userData->{'Username'}.", Result1: ".Dumper($res)."\n");
# Clear & grab result
$r->clear_attributes();
$res = $r->recv_packet();
# Disect packet and see whats going on
print($fh "FORK - (D) ".$userData->{'Username'}.", Result2: ".Dumper($res)."\n");
my $value = $r->{'attributes'};
if (defined($value)) {
my ($v1,$v2,$v3,$v4) = unpack('C C N',$value);
printf($fh "FORK - (D) ".$userData->{'Username'}.", Got reply: v1=%s, v2=%s, v3=%s, v4=%s\n",
defined($v1) ? $v1 : '<undef>',
defined($v2) ? $v2 : '<undef>',
defined($v3) ? $v3 : '<undef>',
defined($v4) ? $v4 : '<undef>'
);
# Last it, as we got a reply
last;
# This would mean a timeout?
} else {
print($fh "FORK - (D) ".$userData->{'Username'}.", Got reply: undefined\n");
}
}
}
}
# Notify user
sub notifyUser
{
my ($dbh,$dbh_log,$fh,$userData,$action,$message) = @_;
# If no notification method is specified, just return
return if (!defined($userData->{'NotifyMethod'}) || $userData->{'NotifyMethod'} eq "");
# Get current notification status
my $notifyStatus = getNotifyStatus($dbh_log,$userData);
if (ref $notifyStatus ne "HASH") {
print($fh " - (N) No notify status: $notifyStatus\n");
return;
}
printf($fh ' - (N) Notify status, id = %s, lastupdate = %s, updateinterval = %s'."\n",
defined($notifyStatus->{'ID'}) ? $notifyStatus->{'ID'} : 'new',
$notifyStatus->{'LastUpdate'},
$notifyStatus->{'UpdateInterval'});
# Time now
my $now = time();
# Check if we should reset
if ($action & NOTIFY_RESET == NOTIFY_RESET) {
$notifyStatus->{'UpdateInterval'} = 86400;
# If we not resetting, check if we should do a check
} else {
# Calculate delta
my $delta = (($now - $notifyStatus->{'LastUpdate'}) + $notifyStatus->{'UpdateInterval'}) / 2;
$delta = 86400 if ($delta > 86400); # 1 day window only
print($fh " - (N) Notify delta = $delta\n");
# Check actions
if ($action & NOTIFY_CHECK == NOTIFY_CHECK) {
# Return if delta is less than half a day
return if ($delta < 43200);
print($fh " - (N) Notify check, succeeded\n");
}
$notifyStatus->{'UpdateInterval'} = $delta;
}
$notifyStatus->{'LastUpdate'} = $now;
my $res = updateNotifyStatus($dbh_log,$userData,$notifyStatus);
if (!$res) {
print($fh " - (N) Notify data updated\n");
} else {
print($fh " - (N) Notify update error: $res\n");
}
# Get agent data
my $agentData = getAgent($dbh,$userData->{'AgentID'});
# Check if we got a hash, if not just return, error already reported in common.pm
if (ref $agentData ne "HASH") {
print($fh " - (N) No agent data, cannot notify: $agentData\n");
return;
}
# Fork off notification handler
my $child_pid;
if (!defined($child_pid = fork())) {
print($fh " - (N) ERROR: Cannot fork: $!\n");
} elsif ($child_pid) {
# I'm the parent
return;
} else {
# I'm the child
sleep(5);
# Pull out notification email addy
my @methods = split /,/, $userData->{'NotifyMethod'};
print($fh "FORK - (N) Resume for: ".$userData->{'Username'}."\n");
# Loop with notification methods
foreach my $method (@methods) {
# Check for email addy
if ($method =~ /^\S+@\S+$/) {
print($fh "FORK - (N) Its an email addy: $method\n");
# Create message
my $msg = MIME::Lite->new(
From => $agentData->{'ContactEmail'},
To => $method,
Bcc => $agentData->{'ContactEmail'},
Subject => "ADSL user ".$userData->{'Username'},
Type => 'multipart/mixed'
);
# Attach body
$msg->attach(
Type => 'TEXT',
Encoding => 'quoted-printable',
Data => $message,
);
# Send
if (!(my $res = $msg->send())) {
print($fh "FORK - (N) Failed to send email\n");
}
# First character can only be 1-9, next char is second part of country code, then 9 digit phone number (without 0)
} elsif ($method =~ /^\+([1-9][0-9]{0,1})([1-9][0-9]{8})$/) {
my $country = $1;
my $number = $2;
use URI::Escape;
my $cmd = "echo '".uri_escape($message,"\x00-\x1f\x7f-\xff\"\'")."' | /usr/local/sitemanager-trunk/scripts/sms/sendsms --send-to='$country$number'";
if ($country eq "27") {
print($fh "FORK - (N) Its a cellphone in allowed country $country: $method\n");
open(LOG,">> /var/log/radius/sms.log");
printf(LOG '%s:SENT:%s:%s:%s%s', $userData->{'AgentID'}, $country . $number, $userData->{'Username'}, $cmd, "\n");
close(LOG);
print($fh "FORK - (N) Going to run command: $cmd\n");
system($cmd);
if ($? == -1) {
print($fh "FORK - (N) Failed to execute: $!\n");
} elsif ($? & 127) {
printf($fh "FORK - (N) Child died with signal %d\n",($? & 127));
} else {
printf($fh "FORK - (N) Child exited with value %d\n", $? >> 8);
}
} else {
print($fh "FORK - (N) Its a cellphone in dis-allowed country $country: $method\n");
open(LOG,">> /var/log/radius/sms.log");
printf(LOG '%s:REJECT:%s:%s:%s%s', $userData->{'AgentID'}, $country . $number, $userData->{'Username'}, $cmd, "\n");
close(LOG);
}
# Not understood
} else {
print($fh "FORK - (N) I DO NOT UNDERSTAND NOTIFY METHOD => '$method'\n");
}
}
exit;
}
}
# Function to get a notification status
sub getNotifyStatus
{
my ($dbh,$userData) = @_;
my %notifyStatus;
# Select tracking information
my $sth = $dbh->select("
SELECT
ID, Username, LastUpdate, UpdateInterval, LastValue
FROM
radiusNotifyTrack
WHERE
Username = ".$dbh->quote($userData->{'Username'})."
");
if (!$sth) {
return "Database error: ".$dbh->err;
}
# Tracking info exists
if ($sth->rows == 1) {
# Pull data
my $data = $sth->fetchrow_hashref();
$sth->finish();
# Sanity check
return "Undefined data!" if (ref $data ne "HASH");
$notifyStatus{'ID'} = $data->{'ID'};
$notifyStatus{'LastUpdate'} = $data->{'LastUpdate'};
$notifyStatus{'UpdateInterval'} = $data->{'UpdateInterval'};
$notifyStatus{'LastValue'} = $data->{'LastValue'};
# No tracking info
} elsif ($sth->rows == 0) {
$sth->finish();
$notifyStatus{'LastUpdate'} = 0;
$notifyStatus{'UpdateInterval'} = 86400;
# Insert record seeing as it doesn't exist
my $res = $dbh->do("
INSERT INTO radiusNotifyTrack
(
Username,
LastUpdate,
UpdateInterval
)
VALUES
(
".$dbh->quote($userData->{'Username'}).",
".$dbh->quote($notifyStatus{'LastUpdate'}).",
".$dbh->quote($notifyStatus{'UpdateInterval'})."
)
");
return "Failed to insert radius tracking data: ".$dbh->err if (!$res);
# Wth happened here?
} else {
my $msg = "Unknown number of rows returned for radius tracking: ".$sth->rows;
$sth->finish();
return $msg;
}
return \%notifyStatus;
}
# Function to update users notify status
sub updateNotifyStatus
{
my ($dbh,$userData,$notifyStatus) = @_;
# Update accounting
my $res = $dbh->do("
UPDATE radiusNotifyTrack
SET
LastUpdate = ".$dbh->quote($notifyStatus->{'LastUpdate'}).",
UpdateInterval = ".$dbh->quote($notifyStatus->{'UpdateInterval'})."
WHERE
Username = ".$dbh->quote($userData->{'Username'})."
");
if (!$res) {
return "Failed to update notify tracking data: ".$dbh->err;
}
return;
}
# Display usage
sub displayUsage {
print("Usage: $0 [--quiet]\n");
exit 0;
}
# vim: ts=4
#!/usr/bin/perl -w
# Author: Nigel Kukard <nkukard@lbsd.net>
# Date: 12/04/2007
# Desc: Authentication filter for GNU Radius
# License: GPL
use strict;
use Benchmark;
use Getopt::Long;
use DateTime;
use Time::HiRes qw( gettimeofday tv_interval );
# Set library directory
use lib qw(../../);
use sm::config;
use sm::dblayer;
require("common.pm");
my %optctl = ();
GetOptions(\%optctl, "help");
# Check if user wants usage
if (defined($optctl{'help'})) {
displayUsage();
}
# Open up logfile
my $logfile = "/var/log/radius/auth-filter";
open(FH,">> $logfile") or die "Failed to open '$logfile': $!";
# Databases
my $dbh; # Authentication
my $dbh_log; # Logs
# Get db handle
$dbh = sm::dbilayer->new($cfg_db_DSN, $cfg_db_Username, $cfg_db_Password);
if (!$dbh) {
print(STDERR "Error creating database object: ".sm::dbilayer->internalErr());
exit 1;
}
# Connect to database
if ($dbh->connect() != 0) {
print(STDERR "Error connecting to database: ".$dbh->err);
exit 1;
}
# Check if we must use split db's
if (defined($cfg_radiuslog_db_DSN)) {
# Get log db handle
$dbh_log = sm::dbilayer->new($cfg_radiuslog_db_DSN, $cfg_radiuslog_db_Username, $cfg_radiuslog_db_Password);
if (!$dbh_log) {
print(STDERR "Error creating database object: ".sm::dbilayer->internalErr());
exit 1;
}
# Connect to database
if ($dbh_log->connect() != 0) {
print(STDERR "Error connecting to database: ".$dbh_log->err);
exit 1;
}
# If not use the main DB
} else {
$dbh_log = $dbh;
}
# No buffering
select((select(FH), $| = 1)[0]);
select((select(STDOUT), $| = 1)[0]);
# Loop with input
while (my $line = <STDIN>) {
my %request;
my @request;
my %accessAttribs;
my %replyAttribs;
# Munch off \n
chomp($line);
# Check number of results
if ((@request = split /:/, $line) < 6) {
print(FH "ERROR: Number of params from radiusd: ".(@request)."/$line\n");
print(STDOUT "1\n");
next;
}
# Pull in request
($request{'User-Name'},$request{'NAS-IP-Address'},$request{'NAS-Port-Type'},$request{'NAS-Port'},$request{'Connect-Info'},
$request{'Service-Type'}) = @request;
my $dt = DateTime->from_epoch( epoch => time() );
$request{'Timestamp'} = $dt->strftime('%Y-%m-%d %H:%M:%S');
# If this is a auth mechanism that called us, allow
if ($request{'NAS-Port-Type'} eq "0" && $request{'NAS-Port'} eq "0" && $request{'Service-Type'} eq "0") {
print(STDOUT "0\n");
next;
}
my $timer0 = [gettimeofday];
# Grab user details
my $userData = getUser($dbh,$request{'User-Name'});
if (ref $userData ne "HASH") {
print(FH "ERROR: $userData\n");
print(STDOUT "1\n");
next;
}
printf(FH 'INFO: User-Name: %s, Timestamp: %s, NAS-IP-Address: %s, NAS-Port-Type: %s, NAS-Port: %s, Connect-Info: %s, Service-Type: %s, CappingType: %s, UsageCap: %s, AgentDisabled: %s'."\n",
$request{'User-Name'},
$request{'Timestamp'},
$request{'NAS-IP-Address'},
$request{'NAS-Port-Type'},
$request{'NAS-Port'},
$request{'Connect-Info'},
$request{'Service-Type'},
$userData->{'CappingType'},
defined($userData->{'UsageCap'}) ? $userData->{'UsageCap'} : "uncapped",
$userData->{'AgentDisabled'}
);
# Check user active, else insert into auth fail
if ($userData->{'AgentDisabled'} eq "1") {
print(FH " - Agent disabled\n");
print(STDOUT "1 Reply-Message = \"Your account is currently deactivated. Please, contact your ISP.\"\n");
authFail(\%request,1);
next;
}
# Pull in class attribs & check
my $sth = $dbh->select("
SELECT
Attr, OP, Value
FROM
radiusClassAttribs
WHERE
RadiusClassID = ".$dbh->quote($userData->{'RadiusClassID'})."
AND OP IS NOT NULL
");
if (!$sth) {
print(FH "ERROR: Selecting class attributes: ".$dbh->err."\n");
print(STDOUT "1\n");
next;
}
# Loop with class attribs and push
while (my $item = $sth->fetchrow_hashref()) {
push(@{$accessAttribs{$item->{'Attr'}}{$item->{'OP'}}},$item->{'Value'});
}
$sth->finish();
# Pull in user attribs & check
$sth = $dbh->select("
SELECT
Attr, OP, Value
FROM
radiusAttribs
WHERE
RadiusUserID = ".$dbh->quote($userData->{'ID'})."
AND OP IS NOT NULL
");
if (!$sth) {
print(FH "ERROR: Selecting class attributes: ".$dbh->err."\n");
print(STDOUT "1\n");
next;
}
# Loop with class attribs and push
while (my $item = $sth->fetchrow_hashref()) {
push(@{$accessAttribs{$item->{'Attr'}}{$item->{'OP'}}},$item->{'Value'});
}
$sth->finish();
# Loop with access attribs and push
my $rejectAttrs = 0;
foreach my $attr (keys %accessAttribs) {
my $ok = 0;
# Check if we missing something in the request
if (!defined($request{$attr})) {
printf(FH " - WARNING: Attribute '$attr' was in accessAttribs, but not request\n");
next;
}
# Loop with attrib op's and check them out
foreach my $op (keys %{$accessAttribs{$attr}}) {
printf(FH ' - Checking %s, request="%s", op="%s", attr="%s": ',
$attr,
$request{$attr},
$op,
join(',',@{$accessAttribs{$attr}{$op}})
);
# Check value against operator
foreach my $val (@{$accessAttribs{$attr}{$op}}) {
# Equal
if ($op eq "=") {
if ($request{$attr} eq $val) {
print(FH "matched '$val'\n");
$ok = 1;
last;
}
}
}
# Check if we ok, if not continue
if ($ok == 0) {
print(FH "no match\n");
} else {
last
}
}
# Check if we ok, if not we've been violated
if ($ok == 0) {
print(FH " - Class attribute violation: '$attr'\n");
$rejectAttrs = 1;
last;
}
}
# Check if something didn't match up
if ($rejectAttrs == 1) {
print(STDOUT "1 Reply-Message = \"Connection attribute mismatch. Please, contact your ISP.\"\n");
authFail(\%request,5);
next;
}
# Check user type vs. adsl & analogue/isdn
# IF ADSL
if ($request{'NAS-Port-Type'} eq "5") {
# Calculate dates
my $date = DateTime->from_epoch( epoch => time() );
my $today = $date->ymd();
$date->set_day(1);
my $thismonth = $date->ymd();
$date->add( months => 1);
my $nextmonth = $date->ymd();
# Extra query for selects
my $extraQuery = "";
# Check port locking, else insert into auth fail
$sth = $dbh->select("
SELECT
NASPort
FROM
radiusPortLocks
WHERE
RadiusUserID = ".$dbh->quote($userData->{'ID'})."
AND AgentDisabled = 0
");
if (!$sth) {
print(FH "ERROR: Selecting NAS ports: ".$dbh->err."\n");
print(STDOUT "1\n");
next;
}
# Check rows
if ($sth->rows > 0) {
my $found = 0;
# Loop with port locks
while (my $portLock = $sth->fetchrow_hashref()) {
# Check if we found port locking
if ($request{'NAS-Port'} eq $portLock->{'NASPort'}) {
$found = 1;
last;
}
}
# Check if we found it
if ($found == 0) {
print(FH " - Port locked\n");
print(STDOUT "1 Reply-Message = \"Connection from unauthorized port. Please, contact your ISP.\"\n");
authFail(\%request,4);
$sth->finish();
next;
}
}
$sth->finish();
# NULL - uncapped, no limits
if (!defined($userData->{'UsageCap'})) {
$extraQuery = "AND Timestamp > ".$dbh->quote($thismonth)." AND Timestamp < ".$dbh->quote($nextmonth);
# > 0 - normal cap, check usage for this month, check topups for this month
# Calculate cap user has (acctinputoctets + (2^32 * gigawords)) /1024 / 1024
# Calculate usage user has
} elsif ($userData->{'UsageCap'} > 0) {
$extraQuery = "AND Timestamp > ".$dbh->quote($thismonth)." AND Timestamp < ".$dbh->quote($nextmonth);
# 0 - topup account
} elsif ($userData->{'UsageCap'} == 0) {
}
# Grab users usage
my $usageData = getUsage($dbh_log,$request{'User-Name'},$extraQuery);
if (ref $usageData ne "HASH") {
print(FH "ERROR: $usageData\n");
print(STDOUT "1\n");
next;
}
my $totalUsage = $usageData->{'Total'};
# If we a normal or topup, check topups
if (defined($userData->{'UsageCap'}) && ($userData->{'UsageCap'} > 0 || $userData->{'UsageCap'} == 0)) {
# Prepare
$extraQuery = "";
# Only total up this month
if ($userData->{'UsageCap'} > 0) {
$extraQuery = "AND ValidFrom <= ".$dbh->quote($today)." AND ValidTo >= ".$dbh->quote($today);
}
# Get how much we've been topped up
my $topupData = getTopups($dbh,$request{'User-Name'},$extraQuery);
if (ref $topupData ne "HASH") {
print(FH "ERROR: $topupData\n");
print(STDOUT "1\n");
next;
}
my $topupBw = $topupData->{'Total'};
# Check capping, else insert into auth fail
print(FH " - Usage $totalUsage (Cap:".$userData->{'UsageCap'}."+Topup:$topupBw)\n");
# Check capping
if ($userData->{'CappingType'} == 1 && ($userData->{'UsageCap'} + $topupBw) <= $totalUsage) {
print(FH " - User capped\n");
print(STDOUT "1 Reply-Message = \"Your account is has been capped. Please, contact your ISP.\"\n");
authFail(\%request,2);
next;
}
}
# IF ANALOGUE
} elsif ($request{'NAS-Port-Type'} eq "0") {
# IF ISDN
} elsif ($request{'NAS-Port-Type'} eq "2") {
} else {
print(FH "ERROR: Unknown NAS-Port-Type: ".$request{'NAS-Port-Type'}."\n");
}
# Pull in class attribs
$sth = $dbh->select("
SELECT
Attr, Value
FROM
radiusClassAttribs
WHERE
RadiusClassID = ".$dbh->quote($userData->{'RadiusClassID'})."
AND OP IS NULL
");
if (!$sth) {
print(FH "ERROR: Selecting class attributes: ".$dbh->err."\n");
print(STDOUT "1\n");
next;
}
# Loop with class attribs
while (my $item = $sth->fetchrow_hashref()) {
$replyAttribs{$item->{'Attr'}} = $item->{'Value'};
}
$sth->finish();
# Pull in user attribs
$sth = $dbh->select("
SELECT
Attr, Value
FROM
radiusAttribs
WHERE
RadiusUserID = ".$dbh->quote($userData->{'ID'})."
AND OP IS NULL
");
if (!$sth) {
print(FH "ERROR: Selecting user attributes: ".$dbh->err."\n");
print(STDOUT "1\n");
next;
}
# Loop with user attribs
while (my $item = $sth->fetchrow_hashref()) {
$replyAttribs{$item->{'Attr'}} = $item->{'Value'};
}
$sth->finish();
# Build up our attrib pairs
my @replyAttribs;
foreach my $key (keys %replyAttribs) {
push(@replyAttribs,sprintf('%s = %s',$key,$replyAttribs{$key}));
}
my $timer1 = [gettimeofday];
my $timediff = tv_interval($timer0,$timer1);
printf(FH ' - Attributes => %s'."\n",join(", ",@replyAttribs));
print(FH "Code execution took: ${timediff}s\n");
# Reply with positive status plus , separated list of attribs
printf(STDOUT '0 %s'."\n",join(", ",@replyAttribs));
}
close(FH);
# Log failed authentication
sub authFail
{
my ($request,$reason) = @_;
# Insert entry into auth fail table
my $sth = $dbh_log->do("
INSERT INTO radiusAuthFail
(
Username,
Timestamp,
NASIPAddress,
NASPortType,
NASPort,
ConnectInfo,
ServiceType,
Reason
)
VALUES
(
".$dbh->quote($request->{'User-Name'}).",
".$dbh->quote($request->{'Timestamp'}).",
".$dbh->quote($request->{'NAS-IP-Address'}).",
".$dbh->quote($request->{'NAS-Port-Type'}).",
".$dbh->quote($request->{'NAS-Port'}).",
".$dbh->quote($request->{'Connect-Info'}).",
".$dbh->quote($request->{'Service-Type'}).",
".$dbh->quote($request->{'Reason'})."
)
");
if (!$sth) {
print(FH "ERROR: Failed to insert radius auth fail data: ".$dbh_log->err."\n");
}
}
# Display usage
sub displayUsage {
print("Usage: $0 [--quiet]\n");
exit 0;
}
# vim: ts=4
#!/usr/bin/perl
# SMRadius administration tool
# Copyright (C) 2009, AllWorldIT
#
# Copyright (C) 2009-2016, 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.
use strict;
use warnings;
# Set library directory
use lib qw(
../ ./
smradius/modules/authentication
smradius/modules/userdb
smradius/modules/accounting
smradius/modules/features
smradius/modules/config
);
use Config;
use FindBin;
use lib ("$FindBin::Bin/../lib", "$FindBin::Bin/../share/perl5", "$FindBin::Bin/../share/perl/$Config{'version'}");
# Check Config::IniFiles is instaslled
if (!eval {require Config::IniFiles; 1;}) {
print STDERR "You're missing Config::IniFiles, try 'apt-get install libconfig-inifiles-perl'\n";
exit 1;
}
# Check DateTime is installed
if (!eval {require DateTime; 1;}) {
print STDERR "You're missing DateTime, try 'apt-get install libdatetime-perl'\n";
exit 1;
}
use Config::IniFiles;
use Getopt::Long;
use smradius::logging;
use smradius::version;
print("SMRadius Admin Tool v".VERSION." - Copyright (c) 2008-2009 AllWorldIT\n");
print("SMRadius Admin Tool v".VERSION." - Copyright (c) 2008-2016 AllWorldIT\n");
# Fire up commandline processing...
my %opts;
......@@ -47,7 +55,9 @@ GetOptions(
\%opts,
"help",
"config:s",
"cleanup-date:s",
"cleanup",
"reset-userdata",
"debug",
);
......@@ -59,7 +69,7 @@ if ($opts{'help'}) {
# Set defaults
my $cfg;
$cfg->{'config_file'} = "smradiusd.conf.testing";
$cfg->{'config_file'} = "smradiusd.conf";
# Check if we must override
if (defined($opts{'config'}) && $opts{'config'} ne "") {
......@@ -80,7 +90,6 @@ tie my %inifile, 'Config::IniFiles', (
) or die "Failed to open config file '".$cfg->{'config_file'}."': $!";
# Copy config
my %config = %inifile;
# FIXME: This now generates a warning as Config::Inifiles doesn't implement UNTIE
# untie(%inifile);
......@@ -94,15 +103,35 @@ my $server;
# We must cleanup
if ($opts{'cleanup'}) {
# Check if we need to reset user data
my $reset_userdata = defined($opts{'reset-userdata'}) ? 1 : 0;
loadModules();
# Pull where we are now and default to that, if we truncate the day we mess up the timezone calculations
my $cleanupMonth = DateTime->now()->set_time_zone($server->{'smradius'}->{'event_timezone'});
# If so the ndo it
if (defined($opts{'cleanup-date'}) && $opts{'cleanup-date'} ne "") {
# Split up and setup
my ($year,$month,$day) = split(/-/,$opts{'cleanup-date'});
$cleanupMonth = DateTime->new(year => $year, month => (defined($month) ? $month : 1), day => (defined($day) ? $day : 1));
}
$server->log(LOG_INFO,"Running cleanup for '%s'",$cleanupMonth->ymd());
# Loop with modules
foreach my $module ( @{$server->{'modules'}} ) {
$server->log(LOG_INFO,"Module: " . $module->{'Name'});
my @sortedModules = sort {
(defined($a->{'CleanupOrder'}) ? $a->{'CleanupOrder'} : 50) cmp (defined($b->{'CleanupOrder'}) ? $b->{'CleanupOrder'} : 50)
} @{$server->{'modules_list'}};
foreach my $module ( @sortedModules ) {
my $prio = defined($module->{'CleanupOrder'}) ? $module->{'CleanupOrder'} : 50;
$server->log(LOG_INFO,"Module: ".$module->{'Name'}." (prio: ".($module->{'CleanupOrder'} ? $prio : "default 50").")");
# If we have a cleanup module, run it
if (defined($module->{'Cleanup'})) {
$server->log(LOG_INFO," -> running cleanup...");
$module->{'Cleanup'}($server);
$module->{'Cleanup'}($server,$cleanupMonth->epoch(),$reset_userdata);
}
}
......@@ -116,10 +145,10 @@ if ($opts{'cleanup'}) {
#
# Misc functions
#
#
# Register plugin info
# Register module info
sub plugin_register {
my ($self,$module,$info) = @_;
......@@ -132,7 +161,7 @@ sub plugin_register {
# Set real module name & save
$info->{'Module'} = $module;
push(@{$self->{'modules'}},$info);
push(@{$self->{'modules_list'}},$info);
# If we should, init the module
if (defined($info->{'init'})) {
......@@ -147,98 +176,135 @@ sub plugin_register {
# Function to load our modules
sub loadModules
{
# FIXME HERE - START
#
# System plugins
# System modules
#
my @system_params = (
'plugins',
);
my $system;
foreach my $param (@system_params) {
$system->{$param} = $config{'system'}{$param} if (defined($config{'system'}{$param}));
}
if (!defined($system->{'plugins'})) {
$server->log(LOG_ERR,"[SMRADIUS] System configuration error: System plugins not found");
exit 1;
if (ref($config{'system'}{'modules'}) eq "ARRAY") {
foreach my $module (@{$config{'system'}{'modules'}}) {
$module =~ s/\s+//g;
# Skip comments
next if ($module =~ /^#/);
$module = "system/$module";
push(@{$cfg->{'module_list'}},$module);
}
} else {
my @moduleList = split(/\s+/,$config{'system'}{'modules'});
foreach my $module (@moduleList) {
# Skip comments
next if ($module =~ /^#/);
$module = "system/$module";
push(@{$cfg->{'module_list'}},$module);
}
}
#
# Authentication plugins
# Authentication modules
#
my @auth_params = (
'mechanisms',
'users',
);
my $auth;
foreach my $param (@auth_params) {
$auth->{$param} = $config{'authentication'}{$param} if (defined($config{'authentication'}{$param}));
if (ref($config{'authentication'}{'mechanisms'}) eq "ARRAY") {
foreach my $module (@{$config{'authentication'}{'mechanisms'}}) {
$module =~ s/\s+//g;
# Skip comments
next if ($module =~ /^#/);
$module = "authentication/$module";
push(@{$cfg->{'module_list'}},$module);
}
} else {
my @moduleList = split(/\s+/,$config{'authentication'}{'mechanisms'});
foreach my $module (@moduleList) {
# Skip comments
next if ($module =~ /^#/);
$module = "authentication/$module";
push(@{$cfg->{'module_list'}},$module);
}
}
if (!defined($auth->{'users'})) {
$server->log(LOG_ERR,"[SMRADIUS] Authentication configuration error: Userdb plugins not found");
exit 1;
if (ref($config{'authentication'}{'users'}) eq "ARRAY") {
foreach my $module (@{$config{'authentication'}{'users'}}) {
$module =~ s/\s+//g;
# Skip comments
next if ($module =~ /^#/);
$module = "userdb/$module";
push(@{$cfg->{'module_list'}},$module);
}
} else {
my @moduleList = split(/\s+/,$config{'authentication'}{'users'});
foreach my $module (@moduleList) {
# Skip comments
next if ($module =~ /^#/);
$module = "userdb/$module";
push(@{$cfg->{'module_list'}},$module);
}
}
#
# Accounting plugins
# Accounting modules
#
my @acct_params = (
'plugins',
);
my $acct;
foreach my $param (@acct_params) {
$acct->{$param} = $config{'accounting'}{$param} if (defined($config{'accounting'}{$param}));
}
if (!defined($acct->{'plugins'})) {
$server->log(LOG_ERR,"[SMRADIUS] Accounting configuration error: Plugins not found");
exit 1;
if (ref($config{'accounting'}{'modules'}) eq "ARRAY") {
foreach my $module (@{$config{'accounting'}{'modules'}}) {
$module =~ s/\s+//g;
# Skip comments
next if ($module =~ /^#/);
$module = "accounting/$module";
push(@{$cfg->{'module_list'}},$module);
}
} else {
my @moduleList = split(/\s+/,$config{'accounting'}{'modules'});
foreach my $module (@moduleList) {
# Skip comments
next if ($module =~ /^#/);
$module = "accounting/$module";
push(@{$cfg->{'module_list'}},$module);
}
}
#
# Feature plugins
# Feature modules
#
my $features;
$features->{'plugins'} = [ ];
$features->{'plugins'} = $config{'features'}{'plugins'} if (defined($config{'features'}{'plugins'}));
# FIXME HERE = END
my $pluginlist = [
@{$auth->{'mechanisms'}},
@{$auth->{'users'}},
@{$acct->{'plugins'}},
@{$features->{'plugins'}},
@{$system->{'plugins'}}
];
# use Data::Dumper; print STDERR Dumper($pluginlist);
if (ref($config{'features'}{'modules'}) eq "ARRAY") {
foreach my $module (@{$config{'features'}{'modules'}}) {
$module =~ s/\s+//g;
# Skip comments
next if ($module =~ /^#/);
$module = "features/$module";
push(@{$cfg->{'module_list'}},$module);
}
} else {
my @moduleList = split(/\s+/,$config{'features'}{'modules'});
foreach my $module (@moduleList) {
# Skip comments
next if ($module =~ /^#/);
$module = "features/$module";
push(@{$cfg->{'module_list'}},$module);
}
}
# Emulate server
$server = new smserver;
$server->{'inifile'} = \%config;
$server = smserver->new();
$server->{'inifile'} = \%config;
# Init everything
$server->init();
# Load plugins
foreach my $plugin (@{$pluginlist}) {
# Load plugin
my $res = eval("
use $plugin;
plugin_register(\$server,\"$plugin\",\$${plugin}::pluginInfo);
");
# Load modules
foreach my $module (@{$cfg->{'module_list'}}) {
# Split off dir and mod name
$module =~ /^(\w+)\/(\w+)$/;
my ($mod_dir,$mod_name) = ($1,$2);
# Load module
## no critic (BuiltinFunctions::ProhibitStringyEval)
my $res = eval qq{
use smradius::modules::${mod_dir}::${mod_name};
plugin_register(\$server,\"${mod_name}\",\$smradius::modules::${mod_dir}::${mod_name}::pluginInfo);
};
## use critic
if ($@ || (defined($res) && $res != 0)) {
$server->log(LOG_ERR,"WARNING: Error loading plugin $plugin ($@)");
$server->log(LOG_ERR,"WARNING: Error loading module $module ($@)");
} else {
$server->log(LOG_DEBUG,"[SMRADIUS] Plugin '$module' loaded.");
}
}
return;
}
......@@ -248,14 +314,17 @@ sub loadModules
# Display help
sub displayHelp {
print(<<EOF);
print(<<"EOF");
Usage: $0 [args]
--config=<file> Configuration file
--debug Put into debug mode
--cleanup Cleanup database records
--reset-userdata Reset user data counters in addition to --cleanup
EOF
return;
}
......@@ -269,8 +338,8 @@ use warnings;
use smradius::logging;
use smradius::config;
use smradius::dbilayer;
use smradius::dblayer;
use AWITPT::DB::DBILayer;
use AWITPT::DB::DBLayer;
# Return oursevles
sub new
......@@ -279,11 +348,12 @@ sub new
my $self = {
};
bless $self, $class;
return $self;
};
sub init
{
my $self = shift;
......@@ -292,9 +362,9 @@ sub init
smradius::config::Init($self);
# Init system stuff
$self->{'client'}->{'dbh'} = smradius::dbilayer::Init($self);
$self->{'client'}->{'dbh'} = AWITPT::DB::DBILayer::Init($self,'smradius');
if (!defined($self->{'client'}->{'dbh'})) {
$self->log(LOG_WARN,"Failed to Initialize: ".smradius::dbilayer::internalErr()." ($$)");
$self->log(LOG_WARN,"Failed to Initialize: ".AWITPT::DB::DBILayer::internalError()." ($$)");
die;
}
if ($self->{'client'}->{'dbh'}->connect()) {
......@@ -302,15 +372,56 @@ sub init
die;
}
# Setup database handle
smradius::dblayer::setHandle($self->{'client'}->{'dbh'});
AWITPT::DB::DBLayer::setHandle($self->{'client'}->{'dbh'});
return;
}
# Same format as Net::Server
sub log_time {
my ($sec,$min,$hour,$day,$mon,$year) = localtime;
return sprintf("%04d/%02d/%02d-%02d:%02d:%02d",
$year+1900, $mon+1, $day, $hour, $min, $sec);
}
sub log
sub log ## no critic (Subroutines::ProhibitBuiltinHomonyms)
{
my ($self,$level,@msg) = @_;
my ($self,$level,$msg,@args) = @_;
# Check log level and set text
my $logtxt = "UNKNOWN";
if ($level == LOG_DEBUG) {
$logtxt = "DEBUG";
} elsif ($level == LOG_INFO) {
$logtxt = "INFO";
} elsif ($level == LOG_NOTICE) {
$logtxt = "NOTICE";
} elsif ($level == LOG_WARN) {
$logtxt = "WARNING";
} elsif ($level == LOG_ERR) {
$logtxt = "ERROR";
}
# Parse message nicely
if ($msg =~ /^(\[[^\]]+\]) (.*)/s) {
$msg = "$1 $logtxt: $2";
} else {
$msg = "[CORE] $logtxt: $msg";
}
# If we have args, this is more than likely a format string & args
if (@args > 0) {
$msg = sprintf($msg,@args);
}
# FIXME: we shouldn't ignore $level
print(@msg, "\n");
print(STDERR "[".log_time()." - $$] $msg\n");
return;
}
......
#!/usr/bin/perl
# Radius client
# Copyright (C) 2007-2016, 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.
use strict;
use warnings;
use Config;
use FindBin;
use lib ("$FindBin::Bin/../lib", "$FindBin::Bin/../share/perl5", "$FindBin::Bin/../share/perl/$Config{'version'}");
use smradius::client;
# Grab and exit with result received
my $res = smradius::client->run();
exit($res);
# vim: ts=4
#!/usr/bin/perl
# Radius daemon
# Copyright (C) 2007-2016, 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.
use strict;
use warnings;
use Config;
use FindBin;
use lib ("$FindBin::Bin/../lib", "$FindBin::Bin/../share/perl5", "$FindBin::Bin/../share/perl/$Config{'version'}");
use smradius::daemon;
smradius::daemon->run();
1;
# vim: ts=4
#!/bin/bash
# Database translation/creation script
# Copyright (C) 2008, LinuxRulz
#
# 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.
database="$1"
file="$2"
prefix="$3"
# Display usage info
display_usage() {
echo "Usage: $0 <database type> <file> [prefix]"
echo
echo "Valid database types:"
echo " mysql4 - For MySQL v4"
echo " mysql - For MySQL v5"
echo " pgsql - For PostgreSQL"
echo " sqlite - For SQLite v3"
echo
exit
}
# Check we have our params
if [ -z "$database" -o -z "$file" ]
then
display_usage
fi
# Check file exists
if [ ! -f "$file" ]
then
echo "ERROR: Cannot open file '$file'"
exit 1
fi
# Check what we converting for
case "$database" in
"mysql4")
sed \
-e "s/@PREFIX@/$prefix/g" \
-e 's/@PRELOAD@/SET FOREIGN_KEY_CHECKS=0;/' \
-e 's/@POSTLOAD@/SET FOREIGN_KEY_CHECKS=1;/' \
-e 's/@CREATE_TABLE_SUFFIX@/TYPE=InnoDB CHARACTER SET latin1 COLLATE latin1_bin/' \
-e 's/@SERIAL_TYPE@/SERIAL/' \
-e 's/@BIGINT_UNSIGNED@/BIGINT UNSIGNED/' \
-e 's/@INT_UNSIGNED@/INT UNSIGNED/' \
-e 's/@TRACK_KEY_LEN@/255/' \
-e 's/@SERIAL_REF_TYPE@/BIGINT UNSIGNED/' < "$file"
;;
"mysql")
sed \
-e "s/@PREFIX@/$prefix/g" \
-e 's/@PRELOAD@/SET FOREIGN_KEY_CHECKS=0;/' \
-e 's/@POSTLOAD@/SET FOREIGN_KEY_CHECKS=1;/' \
-e 's/@CREATE_TABLE_SUFFIX@/TYPE=InnoDB CHARACTER SET latin1 COLLATE latin1_bin/' \
-e 's/@SERIAL_TYPE@/SERIAL/' \
-e 's/@BIGINT_UNSIGNED@/BIGINT UNSIGNED/' \
-e 's/@INT_UNSIGNED@/INT UNSIGNED/' \
-e 's/@TRACK_KEY_LEN@/512/' \
-e 's/@SERIAL_REF_TYPE@/BIGINT UNSIGNED/' < "$file"
;;
"pgsql")
sed \
-e "s/@PREFIX@/$prefix/g" \
-e 's/@PRELOAD@/SET CONSTRAINTS ALL DEFERRED;/' \
-e 's/@POSTLOAD@//' \
-e 's/@CREATE_TABLE_SUFFIX@//' \
-e 's/@SERIAL_TYPE@/SERIAL PRIMARY KEY/' \
-e 's/@BIGINT_UNSIGNED@/INT8/' \
-e 's/@INT_UNSIGNED@/INT8/' \
-e 's/@TRACK_KEY_LEN@/512/' \
-e 's/@SERIAL_REF_TYPE@/INT8/' < "$file"
;;
"sqlite")
sed \
-e "s/@PREFIX@/$prefix/g" \
-e 's/@PRELOAD@//' \
-e 's/@POSTLOAD@//' \
-e 's/@CREATE_TABLE_SUFFIX@//' \
-e 's/@SERIAL_TYPE@/INTEGER PRIMARY KEY AUTOINCREMENT/' \
-e 's/@BIGINT_UNSIGNED@/INT8/' \
-e 's/@INT_UNSIGNED@/INT8/' \
-e 's/@TRACK_KEY_LEN@/512/' \
-e 's/@SERIAL_REF_TYPE@/INT8/' < "$file"
;;
*)
echo "ERROR: Invalid database type '$database'"
exit 1
;;
esac
......@@ -14,8 +14,6 @@ CREATE TABLE @PREFIX@users (
) @CREATE_TABLE_SUFFIX@;
CREATE INDEX @PREFIX@users_idx1 ON @PREFIX@users (Username);
/* User attributes */
CREATE TABLE @PREFIX@user_attributes (
ID @SERIAL_TYPE@,
......@@ -48,8 +46,6 @@ CREATE TABLE @PREFIX@groups (
) @CREATE_TABLE_SUFFIX@;
CREATE INDEX @PREFIX@groups_idx1 ON @PREFIX@groups (Name);
/* Group attributes */
CREATE TABLE @PREFIX@group_attributes (
ID @SERIAL_TYPE@,
......@@ -65,8 +61,6 @@ CREATE TABLE @PREFIX@group_attributes (
FOREIGN KEY (GroupID) REFERENCES @PREFIX@groups(ID)
) @CREATE_TABLE_SUFFIX@;
/* User to group mapping */
CREATE TABLE @PREFIX@users_to_groups (
ID @SERIAL_TYPE@,
......@@ -84,27 +78,6 @@ CREATE TABLE @PREFIX@users_to_groups (
CREATE INDEX @PREFIX@users_to_groups_idx1 ON @PREFIX@users_to_groups (UserID,GroupID);
/* Topups */
CREATE TABLE @PREFIX@topups (
ID @SERIAL_TYPE@,
UserID @SERIAL_REF_TYPE@ NOT NULL,
Timestamp DATETIME,
/* 1 = traffic topup, 2 = uptime topup */
Type @INT_UNSIGNED@,
ValidFrom DATETIME,
ValidTo DATETIME,
Value @INT_UNSIGNED@,
Depleted SMALLINT NOT NULL DEFAULT '0',
FOREIGN KEY (UserID) REFERENCES @PREFIX@users(ID)
) @CREATE_TABLE_SUFFIX@;
/* Realms */
CREATE TABLE @PREFIX@realms (
......@@ -117,8 +90,6 @@ CREATE TABLE @PREFIX@realms (
) @CREATE_TABLE_SUFFIX@;
CREATE INDEX @PREFIX@realms_idx1 ON @PREFIX@realms (Name);
/* Realm attributes */
CREATE TABLE @PREFIX@realm_attributes (
ID @SERIAL_TYPE@,
......@@ -134,43 +105,88 @@ CREATE TABLE @PREFIX@realm_attributes (
FOREIGN KEY (RealmID) REFERENCES @PREFIX@realms(ID)
) @CREATE_TABLE_SUFFIX@;
/* Client to realm mapping */
CREATE TABLE @PREFIX@clients_to_realms (
ID @SERIAL_TYPE@,
ClientID @SERIAL_REF_TYPE@ NOT NULL,
RealmID @SERIAL_REF_TYPE@ NOT NULL,
/* Topups Summary */
CREATE TABLE @PREFIX@topups_summary (
Disabled SMALLINT NOT NULL DEFAULT '0',
Comment VARCHAR(1024),
UNIQUE (ClientID,RealmID),
FOREIGN KEY (ClientID) REFERENCES @PREFIX@clients(ID),
FOREIGN KEY (RealmID) REFERENCES @PREFIX@realms(ID)
) @CREATE_TABLE_SUFFIX@;
/* Clients */
CREATE TABLE @PREFIX@clients (
ID @SERIAL_TYPE@,
TopupID @SERIAL_REF_TYPE@ NOT NULL,
Name VARCHAR(255) NOT NULL,
AccessList VARCHAR(255),
PeriodKey VARCHAR(255) NOT NULL,
Disabled SMALLINT NOT NULL DEFAULT '0',
Balance @INT_UNSIGNED@,
UNIQUE (Name)
) @CREATE_TABLE_SUFFIX@;
Depleted SMALLINT NOT NULL DEFAULT '0',
/* Client attributes */
CREATE TABLE @PREFIX@client_attributes (
ID @SERIAL_TYPE@,
FOREIGN KEY (TopupID) REFERENCES @PREFIX@topups(ID)
ClientID @SERIAL_REF_TYPE@ NOT NULL,
Name VARCHAR(255) NOT NULL,
Operator VARCHAR(4) NOT NULL,
Value VARCHAR(255),
Disabled SMALLINT NOT NULL DEFAULT '0',
FOREIGN KEY (ClientID) REFERENCES @PREFIX@clients(ID)
) @CREATE_TABLE_SUFFIX@;
/* Accounting Summary */
CREATE TABLE @PREFIX@accounting_summary (
/* Topups */
CREATE TABLE @PREFIX@topups (
ID @SERIAL_TYPE@,
Username VARCHAR(255),
UserID @SERIAL_REF_TYPE@ NOT NULL,
PeriodKey DATETIME,
Timestamp DATETIME,
AcctSessionTime @INT_UNSIGNED@,
/* 1 = traffic topup, 2 = uptime topup, 4 = auto-topup */
Type @INT_UNSIGNED@,
AcctInputOctets @INT_UNSIGNED@,
ValidFrom DATETIME,
ValidTo DATETIME,
AcctInputGigawords @INT_UNSIGNED@,
Value @INT_UNSIGNED@,
AcctInputPackets @INT_UNSIGNED@,
Depleted SMALLINT NOT NULL DEFAULT '0',
SMAdminDepletedOn DATETIME,
AcctOutputOctets @INT_UNSIGNED@,
FOREIGN KEY (UserID) REFERENCES @PREFIX@users(ID)
) @CREATE_TABLE_SUFFIX@;
/* Topups Summary */
CREATE TABLE @PREFIX@topups_summary (
ID @SERIAL_TYPE@,
TopupID @SERIAL_REF_TYPE@ NOT NULL,
PeriodKey VARCHAR(255) NOT NULL,
Balance @INT_UNSIGNED@,
AcctOutputGigawords @INT_UNSIGNED@
Depleted SMALLINT NOT NULL DEFAULT '0',
SMAdminDepletedOn DATETIME,
FOREIGN KEY (TopupID) REFERENCES @PREFIX@topups(ID)
) @CREATE_TABLE_SUFFIX@;
......@@ -183,13 +199,13 @@ CREATE TABLE @PREFIX@accounting (
ServiceType @INT_UNSIGNED@,
FramedProtocol @INT_UNSIGNED@,
FramedProtocol @INT_UNSIGNED@,
NASPort VARCHAR(255),
NASPortType @INT_UNSIGNED@,
CallingStationID VARCHAR(255),
CallingStationID VARCHAR(255),
CalledStationID VARCHAR(255),
......@@ -225,6 +241,53 @@ CREATE TABLE @PREFIX@accounting (
AcctStatusType @INT_UNSIGNED@,
AcctTerminateCause @INT_UNSIGNED@
AcctTerminateCause @INT_UNSIGNED@,
PeriodKey VARCHAR(255)
) @CREATE_TABLE_SUFFIX@;
CREATE INDEX @PREFIX@accounting_idx1 ON @PREFIX@accounting (Username);
CREATE INDEX @PREFIX@accounting_idx2 ON @PREFIX@accounting (PeriodKey);
/* accounting_stop_status_query */
CREATE INDEX @PREFIX@accounting_idx4 ON @PREFIX@accounting (Username,AcctSessionID,NASIPAddress,NASPort);
/* accounting_update_query */
CREATE INDEX @PREFIX@accounting_idx5 ON @PREFIX@accounting (Username,AcctSessionID,NASIPAddress,NASPort,PeriodKey);
/* Index for the EventTimestamp */
CREATE INDEX @PREFIX@accounting_idx7 ON @PREFIX@accounting (EventTimestamp);
CREATE INDEX @PREFIX@accounting_idx8 ON @PREFIX@accounting (Username,EventTimestamp);
/* Accounting Summary */
CREATE TABLE @PREFIX@accounting_summary (
ID @SERIAL_TYPE@,
Username VARCHAR(255),
PeriodKey VARCHAR(255),
TotalSessionTime @INT_UNSIGNED@,
TotalInput @INT_UNSIGNED@,
TotalOutput @INT_UNSIGNED@
) @CREATE_TABLE_SUFFIX@;
CREATE INDEX @PREFIX@accounting_summary_idx1 ON @PREFIX@accounting_summary (Username);
CREATE INDEX @PREFIX@accounting_summary_idx2 ON @PREFIX@accounting_summary (PeriodKey);
CREATE INDEX @PREFIX@accounting_summary_idx3 ON @PREFIX@accounting_summary (Username,PeriodKey);
/* Users data */
CREATE TABLE @PREFIX@users_data (
ID @SERIAL_TYPE@,
UserID @INT_UNSIGNED@,
LastUpdated DATETIME,
Name VARCHAR(255),
Value VARCHAR(255),
UNIQUE (UserID,Name)
) @CREATE_TABLE_SUFFIX@;