From 3014cd817666026103f5d7ef9dd79efedcf657b6 Mon Sep 17 00:00:00 2001 From: jdegraeve Date: Wed, 29 Mar 2006 09:46:48 +0000 Subject: [PATCH] Introduction of rule pooling git-svn-id: https://svn.m0n0.ch/wall/trunk@114 e36fee2c-cc09-0410-a7cc-ebac5c6737de --- CHANGELOG | 4 +- captiveportal/index.php | 60 ++++++++--------------- phpconf/inc/captiveportal.inc | 92 ++++++++++++++++++++++------------- 3 files changed, 82 insertions(+), 74 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b6a23fe..f93422b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,8 +17,8 @@ $Id$ - Cleanup and code added to allow FUTURE stuff like volume limits etc. to be implemented - Added User Volume Stats in captive portal status page - RADIUS mac authentication now works on local subnet even if "Disable MAC filtering" is activated - - Firewall ruleno should now wrap more cleanly when having lots of sessions, REBOOT after you change CP Allowed-ip-addresses - - fixed bug in RADIUS Session-Timeout handling so it'll also work even if reauthentication is disabled + - Firewall ruleno now uses a more intelligent pool, this fixes a bug where a ruleno could be used even if it is already been assigned + - Fixed bug in RADIUS Session-Timeout handling so it'll also work even if reauthentication is disabled - added "disable port mapping" option to advanced outbound NAT (helps with certain IPsec VPN gateways that insist on the IKE source port being 500) (mkasper) diff --git a/captiveportal/index.php b/captiveportal/index.php index 0611ca1..5c533d2 100755 --- a/captiveportal/index.php +++ b/captiveportal/index.php @@ -74,7 +74,6 @@ $clientmac = arp_get_mac_by_ip($clientip); if (!$clientmac && $macfilter) { /* unable to find MAC address - shouldn't happen! - bail out */ captiveportal_logportalauth("unauthenticated","noclientmac",$clientip,"ERROR"); - /* We should return an error page to the client explaining what went wrong instead of exiting */ exit; } @@ -147,7 +146,7 @@ EOD; $moddb = true; } } - if ($moddb) + if ($moddb)portal_reply_page($redirurl, "error", $auth_list['error']); write_config(); $userdb = &$config['captiveportal']['user']; @@ -232,7 +231,7 @@ function portal_mac_radius($clientmac,$clientip) { return FALSE; } -function portal_allow($clientip,$clientmac,$clientuser,$password = null, $attributes = null) { +function portal_allow($clientip,$clientmac,$clientuser,$password = null, $attributes = null, $ruleno = null) { global $redirurl, $g, $config; @@ -244,27 +243,35 @@ function portal_allow($clientip,$clientmac,$clientuser,$password = null, $attrib kick_concurrent_logins($clientuser); captiveportal_lock(); - - $ruleno = get_next_ipfw_ruleno(); + + /* if no ruleno is passed to this function we will look one up in the pool */ + $ruleno = (!is_null($ruleno)) ? $ruleno : captiveportal_get_next_ipfw_ruleno(); + + /* if the pool is empty, return appropriate message and exit */ + if (is_null($ruleno)) { + portal_reply_page($redirurl, "error", "System reached maximum login capacity"); + captiveportal_unlock(); + exit; + } /* generate unique session ID */ $tod = gettimeofday(); $sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16); - + /* add ipfw rules for layer 3 */ exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from $clientip to any in"); exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to $clientip out"); - + /* add ipfw rules for layer 2 */ if (!isset($config['captiveportal']['nomacfilter'])) { $l2ruleno = $ruleno + 10000; exec("/sbin/ipfw add $l2ruleno set 3 deny all from $clientip to any not MAC any $clientmac layer2 in"); exec("/sbin/ipfw add $l2ruleno set 3 deny all from any to $clientip not MAC $clientmac any layer2 out"); } - + /* read in client database */ $cpdb = captiveportal_read_db(); - + $radiusservers = captiveportal_get_radius_servers(); /* find an existing entry and delete it */ @@ -286,7 +293,7 @@ function portal_allow($clientip,$clientmac,$clientuser,$password = null, $attrib unset($cpdb[$i]); break; } - } + } /* encode password in Base64 just in case it contains commas */ $bpassword = base64_encode($password); @@ -298,21 +305,8 @@ function portal_allow($clientip,$clientmac,$clientuser,$password = null, $attrib /* rewrite information to database */ captiveportal_write_db($cpdb); - /* Builtin wrap safety for allowedip */ - $allowedipno = (is_array($config['captiveportal']['allowedip'])) ? count($config['captiveportal']['allowedip']) : 0; - - /* write next rule number */ - $fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w"); - if ($fd) { - $ruleno++; - if ($ruleno > 19899) - $ruleno = 10000 + $allowedipno; /* wrap around */ - fwrite($fd, $ruleno); - fclose($fd); - } - captiveportal_unlock(); - + /* redirect user to desired destination */ if ($url_redirection) $my_redirurl = $url_redirection; @@ -320,14 +314,14 @@ function portal_allow($clientip,$clientmac,$clientuser,$password = null, $attrib $my_redirurl = $config['captiveportal']['redirurl']; else $my_redirurl = $redirurl; - + if(isset($config['captiveportal']['logoutwin_enable'])) { - + if (isset($config['captiveportal']['httpslogin'])) $logouturl = "https://{$config['captiveportal']['httpsname']}:8001/"; else $logouturl = "http://{$config['interfaces'][$config['captiveportal']['interface']]['ipaddr']}:8000/"; - + echo << Redirecting... @@ -433,17 +427,5 @@ function disconnect_client($sessionid, $logoutReason = "LOGOUT", $term_cause = 1 captiveportal_unlock(); } -function get_next_ipfw_ruleno() { - - global $g; - - /* get next ipfw rule number */ - if (file_exists("{$g['vardb_path']}/captiveportal.nextrule")) - $ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule")); - if (!$ruleno) - $ruleno = 10000; /* first rule number */ - - return $ruleno; -} ?> diff --git a/phpconf/inc/captiveportal.inc b/phpconf/inc/captiveportal.inc index 18fa767..3fab3da 100644 --- a/phpconf/inc/captiveportal.inc +++ b/phpconf/inc/captiveportal.inc @@ -73,7 +73,6 @@ function captiveportal_configure() { if ((!is_numeric($croninterval)) || ($croninterval < 10)) { $croninterval = 60; } /* remove old information */ - unlink_if_exists("{$g['vardb_path']}/captiveportal.nextrule"); unlink_if_exists("{$g['vardb_path']}/captiveportal.db"); unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db"); unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db"); @@ -626,7 +625,7 @@ function captiveportal_passthrumac_configure() { function captiveportal_allowedip_configure() { global $config, $g; - + captiveportal_lock(); /* clear out existing allowed ips, if necessary */ @@ -638,33 +637,38 @@ function captiveportal_allowedip_configure() { if ($line) { list($ip,$rule) = explode(",",$line); mwexec("/sbin/ipfw delete $rule"); - } + } } } fclose($fd); unlink("{$g['vardb_path']}/captiveportal_ip.db"); } - /* get next ipfw rule number */ - if (file_exists("{$g['vardb_path']}/captiveportal.nextrule")) - $ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule")); - if (!$ruleno) - $ruleno = 10000; /* first rule number */ - if (is_array($config['captiveportal']['allowedip'])) { - + $fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "w"); if (!$fd) { printf("Error: cannot open allowed ip DB file in captiveportal_allowedip_configure().\n"); captiveportal_unlock(); - return 1; + return 1; } - + foreach ($config['captiveportal']['allowedip'] as $ipent) { - + + /* get next ipfw rule number */ + $ruleno = captiveportal_get_next_ipfw_ruleno(); + + /* if the pool is empty, return apprioriate message and fail */ + if (is_null($ruleno)) { + printf("Error: system reached maximum login capacity, no free FW rulenos in captiveportal_allowedip_configure().\n"); + fclose($fd); + captiveportal_unlock(); + return 1; + } + /* record allowed ip so it can be recognized and removed later */ fwrite($fd, $ipent['ip'] . "," . $ruleno ."\n"); - + /* insert ipfw rule to allow ip thru */ if ($ipent['dir'] == "from") { mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any in"); @@ -673,38 +677,28 @@ function captiveportal_allowedip_configure() { mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " in"); mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any out"); } - - $ruleno++; - if ($ruleno > 19899) - $ruleno = 10000; - } - - fclose($fd); - /* write next rule number */ - $fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w"); - if ($fd) { - fwrite($fd, $ruleno); - fclose($fd); } + + fclose($fd); } - + captiveportal_unlock(); return 0; } /* get last activity timestamp given ipfw rule number */ function captiveportal_get_last_activity($ruleno) { - + exec("/sbin/ipfw -T list {$ruleno} 2>/dev/null", $ipfwoutput); - + /* in */ if ($ipfwoutput[0]) { $ri = explode(" ", $ipfwoutput[0]); if ($ri[1]) return $ri[1]; } - + return 0; } @@ -781,7 +775,16 @@ function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) { function radius($username,$password,$clientip,$clientmac,$type) { global $g, $config; - $next_ruleno = get_next_ipfw_ruleno(); + $ruleno = captiveportal_get_next_ipfw_ruleno(); + + /* if the pool is empty, return apprioriate message and fail authentication */ + if (is_null($ruleno)) { + $auth_list = array(); + $auth_list['auth_val'] = 1; + $auth_list['error'] = "System reached maximum login capacity"; + return $auth_list; + } + $radiusservers = captiveportal_get_radius_servers(); $radacct_enable = isset($config['captiveportal']['radacct_enable']); @@ -798,10 +801,11 @@ function radius($username,$password,$clientip,$clientmac,$type) { $clientmac, $username, $password, - $auth_list); + $auth_list, + $ruleno); if ($radacct_enable) { - $auth_list['acct_val'] = RADIUS_ACCOUNTING_START($next_ruleno, + $auth_list['acct_val'] = RADIUS_ACCOUNTING_START($ruleno, $username, $sessionid, $radiusservers[0]['ipaddr'], @@ -883,6 +887,28 @@ function captiveportal_write_elements() { return 0; } +/** + * This function will calculate the lowest free firewall ruleno + * within the range specified based on the actual installed rules + * + */ + +function captiveportal_get_next_ipfw_ruleno() { + + exec("/sbin/ipfw show", $fwrules); + foreach ($fwrules as $fwrule) { + preg_match("/^(\d+)\s+/", $fwrule, $matches); + $rulenos_used[] = $matches[1]; + } + $rulenos_pool = range(10000, 19899); + $rulenos_used = array_unique($rulenos_used); + $rulenos_free = array_diff($rulenos_pool, $rulenos_used); + $ruleno = array_shift($rulenos_free); + + return $ruleno; +} + + /** * This function will calculate the traffic produced by a client * based on its firewall rule -- 2.43.0