Skip to content

Commit 423ce46

Browse files
Merge pull request pfsense#3453 from plumbeo/traffic-quota
2 parents 9f85da7 + 066335a commit 423ce46

File tree

6 files changed

+158
-43
lines changed

6 files changed

+158
-43
lines changed

src/etc/inc/captiveportal.inc

+63-30
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,7 @@ function captiveportal_delete_rules($pipes_to_remove = array()) {
731731
/*
732732
* Remove clients that have been around for longer than the specified amount of time
733733
* db file structure:
734-
* timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time,interim_interval
734+
* timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time,interim_interval,traffic_quota,radiusctx
735735
* (password is in Base64 and only saved when reauthentication is enabled)
736736
*/
737737
function captiveportal_prune_old() {
@@ -755,9 +755,16 @@ function captiveportal_prune_old() {
755755
$idletimeout = $cpcfg['idletimeout'] * 60;
756756
}
757757

758+
/* check for entries exceeding their traffic quota */
759+
$trafficquota = 0;
760+
if (!empty($cpcfg['trafficquota']) && is_numeric($cpcfg['trafficquota'])) {
761+
$trafficquota = $cpcfg['trafficquota'] * 1048576;
762+
}
763+
758764
/* Is there any job to do? */
759-
if (!$timeout && !$idletimeout && !isset($cpcfg['reauthenticate']) &&
760-
!isset($cpcfg['radiussession_timeout']) && !isset($vcpcfg['enable'])) {
765+
if (!$timeout && !$idletimeout && !$trafficquota && !isset($cpcfg['reauthenticate']) &&
766+
!isset($cpcfg['radiussession_timeout']) && !isset($cpcfg['radiustraffic_quota']) &&
767+
!isset($vcpcfg['enable'])) {
761768
return;
762769
}
763770

@@ -779,16 +786,21 @@ function captiveportal_prune_old() {
779786

780787
$timedout = false;
781788
$term_cause = 1;
782-
if (empty($cpentry[11])) {
783-
$cpentry[11] = 'first';
789+
$logout_cause = 'TIMEOUT';
790+
if (empty($cpentry[12])) {
791+
$cpentry[12] = 'first';
784792
}
785-
$radiusservers = $radiussrvs[$cpentry[11]];
793+
$radiusservers = $radiussrvs[$cpentry[12]];
786794

787-
/* hard timeout? */
795+
/* hard timeout or session_timeout from radius if enabled */
796+
if (isset($cpcfg['radiussession_timeout'])) {
797+
$timeout = (is_numeric($cpentry[7])) ? $cpentry[7] : $timeout;
798+
}
788799
if ($timeout) {
789800
if (($pruning_time - $cpentry[0]) >= $timeout) {
790801
$timedout = true;
791802
$term_cause = 5; // Session-Timeout
803+
$logout_cause = 'SESSION TIMEOUT';
792804
}
793805
}
794806

@@ -797,6 +809,7 @@ function captiveportal_prune_old() {
797809
if ($pruning_time >= $cpentry[9]) {
798810
$timedout = true;
799811
$term_cause = 5; // Session-Timeout
812+
$logout_cause = 'SESSION TIMEOUT';
800813
}
801814
}
802815

@@ -812,6 +825,7 @@ function captiveportal_prune_old() {
812825
if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) {
813826
$timedout = true;
814827
$term_cause = 4; // Idle-Timeout
828+
$logout_cause = 'IDLE TIMEOUT';
815829
if (!isset($config['captiveportal'][$cpzone]['includeidletime'])) {
816830
$stop_time = $lastact;
817831
}
@@ -823,21 +837,27 @@ function captiveportal_prune_old() {
823837
if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
824838
$timedout = true;
825839
$term_cause = 5; // Session-Timeout
840+
$logout_cause = 'SESSION TIMEOUT';
826841
$voucher_needs_sync = true;
827842
}
828843
}
829844

830-
/* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
831-
if (!$timedout && isset($cpcfg['radiussession_timeout']) && !empty($cpentry[7])) {
832-
if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
845+
/* traffic quota, value retrieved from the radius attribute if the option is enabled */
846+
if (isset($cpcfg['radiustraffic_quota'])) {
847+
$trafficquota = (is_numeric($cpentry[11])) ? $cpentry[11] : $trafficquota;
848+
}
849+
if (!$timedout && $trafficquota > 0) {
850+
$volume = getVolume($cpentry[2], $cpentry[3]);
851+
if (($volume['input_bytes'] + $volume['output_bytes']) > $trafficquota) {
833852
$timedout = true;
834-
$term_cause = 5; // Session-Timeout
853+
$term_cause = 10; // NAS-Request
854+
$logout_cause = 'QUOTA EXCEEDED';
835855
}
836856
}
837857

838858
if ($timedout) {
839859
captiveportal_disconnect($cpentry, $radiusservers, $term_cause, $stop_time);
840-
captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "TIMEOUT");
860+
captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], $logout_cause);
841861
$unsetindexes[] = $cpentry[5];
842862
}
843863

@@ -1098,10 +1118,10 @@ function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutRea
10981118
captiveportal_write_db("DELETE FROM captiveportal WHERE sessionid = '{$sessionid}'");
10991119

11001120
foreach ($result as $cpentry) {
1101-
if (empty($cpentry[11])) {
1102-
$cpentry[11] = 'first';
1121+
if (empty($cpentry[12])) {
1122+
$cpentry[12] = 'first';
11031123
}
1104-
captiveportal_disconnect($cpentry, $radiusservers[$cpentry[11]], $term_cause);
1124+
captiveportal_disconnect($cpentry, $radiusservers[$cpentry[12]], $term_cause);
11051125
captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");
11061126
}
11071127
unset($result);
@@ -1155,15 +1175,15 @@ function captiveportal_radius_stop_all($term_cause = 6, $logoutReason = "DISCONN
11551175
foreach ($cpdb as $cpentry) {
11561176
if ($radacct) {
11571177
if (!empty($radiusservers)) {
1158-
if (empty($cpentry[11])) {
1159-
$cpentry[11] = 'first';
1178+
if (empty($cpentry[12])) {
1179+
$cpentry[12] = 'first';
11601180
}
1161-
if (!empty($radiusservers[$cpentry[11]])) {
1181+
if (!empty($radiusservers[$cpentry[12]])) {
11621182
RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
11631183
$cpentry[4], // username
11641184
$cpentry[5], // sessionid
11651185
$cpentry[0], // start time
1166-
$radiusservers[$cpentry[11]],
1186+
$radiusservers[$cpentry[12]],
11671187
$cpentry[2], // clientip
11681188
$cpentry[3], // clientmac
11691189
$term_cause);
@@ -1613,7 +1633,8 @@ function captiveportal_opendb() {
16131633
$createquery = "CREATE TABLE IF NOT EXISTS captiveportal (" .
16141634
"allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, " .
16151635
"sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, " .
1616-
"session_terminate_time INTEGER, interim_interval INTEGER, radiusctx TEXT); " .
1636+
"session_terminate_time INTEGER, interim_interval INTEGER, traffic_quota INTEGER, " .
1637+
"radiusctx TEXT); " .
16171638
"CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " .
16181639
"CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " .
16191640
"CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " .
@@ -2147,8 +2168,14 @@ function captiveportal_reapply_attributes($cpentry, $attributes) {
21472168
$dwfaultbw_up = $dwfaultbw_down = 0;
21482169
}
21492170
/* pipe throughputs must always be an integer, enforce that restriction again here. */
2150-
$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2151-
$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2171+
if (isset($config['captiveportal'][$cpzone]['radiusperuserbw'])) {
2172+
$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2173+
$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2174+
} else {
2175+
$bw_up = round($dwfaultbw_up,0);
2176+
$bw_down = round($dwfaultbw_down,0);
2177+
}
2178+
21522179
$bw_up_pipeno = $cpentry[1];
21532180
$bw_down_pipeno = $cpentry[1]+1;
21542181

@@ -2236,8 +2263,8 @@ function portal_allow($clientip, $clientmac, $username, $password = null, $attri
22362263
}
22372264

22382265
foreach ($cpdb as $cpentry) {
2239-
if (empty($cpentry[11])) {
2240-
$cpentry[11] = 'first';
2266+
if (empty($cpentry[12])) {
2267+
$cpentry[12] = 'first';
22412268
}
22422269
/* on the same ip */
22432270
if ($cpentry[2] == $clientip) {
@@ -2257,15 +2284,15 @@ function portal_allow($clientip, $clientmac, $username, $password = null, $attri
22572284
}
22582285

22592286
/* This user was already logged in so we disconnect the old one */
2260-
captiveportal_disconnect($cpentry, $radiusservers[$cpentry[11]], 13);
2287+
captiveportal_disconnect($cpentry, $radiusservers[$cpentry[12]], 13);
22612288
captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION");
22622289
$unsetindexes[] = $cpentry[5];
22632290
break;
22642291
} elseif ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
22652292
/* on the same username */
22662293
if (strcasecmp($cpentry[4], $username) == 0) {
22672294
/* This user was already logged in so we disconnect the old one */
2268-
captiveportal_disconnect($cpentry, $radiusservers[$cpentry[11]], 13);
2295+
captiveportal_disconnect($cpentry, $radiusservers[$cpentry[12]], 13);
22692296
captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION");
22702297
$unsetindexes[] = $cpentry[5];
22712298
break;
@@ -2294,8 +2321,13 @@ function portal_allow($clientip, $clientmac, $username, $password = null, $attri
22942321
$dwfaultbw_up = $dwfaultbw_down = 0;
22952322
}
22962323
/* pipe throughputs must always be an integer, enforce that restriction again here. */
2297-
$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2298-
$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2324+
if (isset($config['captiveportal'][$cpzone]['radiusperuserbw'])) {
2325+
$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2326+
$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2327+
} else {
2328+
$bw_up = round($dwfaultbw_up,0);
2329+
$bw_down = round($dwfaultbw_down,0);
2330+
}
22992331

23002332
if ($passthrumac) {
23012333

@@ -2374,15 +2406,16 @@ function portal_allow($clientip, $clientmac, $username, $password = null, $attri
23742406
$idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL';
23752407
$session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL';
23762408
$interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL';
2409+
$traffic_quota = (!empty($attributes['maxbytes'])) ? $attributes['maxbytes'] : 'NULL';
23772410

23782411
/* escape username */
23792412
$safe_username = SQLite3::escapeString($username);
23802413

23812414
/* encode password in Base64 just in case it contains commas */
23822415
$bpassword = (isset($config['captiveportal'][$cpzone]['reauthenticate'])) ? base64_encode($password) : '';
2383-
$insertquery = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, radiusctx) ";
2416+
$insertquery = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, traffic_quota, radiusctx) ";
23842417
$insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', ";
2385-
$insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, '{$radiusctx}')";
2418+
$insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, {$traffic_quota}, '{$radiusctx}')";
23862419

23872420
/* store information to database */
23882421
captiveportal_write_db($insertquery);

src/etc/inc/globals.inc

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ $g = array(
7474
"disablecrashreporter" => false,
7575
"crashreporterurl" => "https://crashreporter.pfsense.org/crash_reporter.php",
7676
"debug" => false,
77-
"latest_config" => "18.0",
77+
"latest_config" => "18.1",
7878
"minimum_ram_warning" => "101",
7979
"minimum_ram_warning_text" => "128 MB",
8080
"wan_interface_name" => "wan",

src/etc/inc/radius.inc

+15
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* Adding of VENDOR Nomadix
4545
* Adding of VENDOR WISPr (Wi-Fi Alliance)
4646
* Adding of VENDOR ChilliSpot (bandwidth-attributes only)
47+
* Adding of VENDOR pfSense (Netgate)
4748
4849
*/
4950

@@ -696,6 +697,20 @@ class Auth_RADIUS extends PEAR {
696697
}
697698
}
698699

700+
elseif ($vendor == 13644) { /* Netgate */
701+
switch ($attrv) {
702+
case 1: /* pfSense-Bandwidth-Max-Up */
703+
$this->attributes['bw_up'] = radius_cvt_int($datav);
704+
break;
705+
case 2: /* pfSense-Bandwidth-Max-Down */
706+
$this->attributes['bw_down'] = radius_cvt_int($datav);
707+
break;
708+
case 3: /* pfSense-Max-Total-Octets */
709+
$this->attributes['maxbytes'] = radius_cvt_int($datav);
710+
break;
711+
}
712+
}
713+
699714
break;
700715

701716
case 85: /* Acct-Interim-Interval: RFC 2869 */

src/etc/inc/upgrade_config.inc

+27-12
Original file line numberDiff line numberDiff line change
@@ -5478,18 +5478,6 @@ function upgrade_173_to_174() {
54785478
}
54795479
}
54805480

5481-
/*
5482-
* Special function that is called independent of current config version. It's
5483-
* a workaround to have config_upgrade running on older versions after next
5484-
* config version was already taken by newer pfSense.
5485-
*
5486-
* XXX Change the way we handle config version to make it based on product
5487-
* version
5488-
*/
5489-
function additional_config_upgrade() {
5490-
global $config;
5491-
}
5492-
54935481
/* IPsec Phase1 now supports multiple authentication ciphers to be specified from the webgui.
54945482
* This is usefull for mobile users using different OS's supporting different ciphers.
54955483
*/
@@ -5554,4 +5542,31 @@ function upgrade_179_to_180() {
55545542
}
55555543
}
55565544

5545+
/*
5546+
* Automatically enable retrieving captive portal bandwidth limits from RADIUS for each captive portal
5547+
*/
5548+
function upgrade_180_to_181() {
5549+
global $config;
5550+
5551+
if (is_array($config['captiveportal'])) {
5552+
foreach ($config['captiveportal'] as $cpzone => $cpcfg) {
5553+
if ($cpcfg['auth_method'] == "radius") {
5554+
$config['captiveportal'][$cpzone]['radiusperuserbw'] = true;
5555+
}
5556+
}
5557+
}
5558+
}
5559+
5560+
/*
5561+
* Special function that is called independent of current config version. It's
5562+
* a workaround to have config_upgrade running on older versions after next
5563+
* config version was already taken by newer pfSense.
5564+
*
5565+
* XXX Change the way we handle config version to make it based on product
5566+
* version
5567+
*/
5568+
function additional_config_upgrade() {
5569+
global $config;
5570+
}
5571+
55575572
?>

0 commit comments

Comments
 (0)