]> git.gsnw.org Git - m0n0chwall.git/commitdiff
Introduction of rule pooling
authorjdegraeve <jdegraeve@e36fee2c-cc09-0410-a7cc-ebac5c6737de>
Wed, 29 Mar 2006 09:46:48 +0000 (09:46 +0000)
committerjdegraeve <jdegraeve@e36fee2c-cc09-0410-a7cc-ebac5c6737de>
Wed, 29 Mar 2006 09:46:48 +0000 (09:46 +0000)
git-svn-id: https://svn.m0n0.ch/wall/trunk@114 e36fee2c-cc09-0410-a7cc-ebac5c6737de

CHANGELOG
captiveportal/index.php
phpconf/inc/captiveportal.inc

index b6a23fe785ab69f847d86fc53a9a1e395963d911..f93422b337a02f0e43af0a035c38666a19d24cd7 100644 (file)
--- 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)
index 0611ca1b100f03f9e693c0585e4cf6a61ac44b38..5c533d2e37c2b62e81e5748950a18a62153b4c43 100755 (executable)
@@ -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 <<<EOD
 <HTML>
 <HEAD><TITLE>Redirecting...</TITLE></HEAD>
@@ -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;
-}
 
 ?>
index 18fa76744943a37b05c399374cadd9118f6812c2..3fab3da138b6f899ba204d57e7e037b7846051b6 100644 (file)
@@ -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