diff options
Diffstat (limited to 'etc/inc/captiveportal.inc')
-rw-r--r-- | etc/inc/captiveportal.inc | 289 |
1 files changed, 175 insertions, 114 deletions
diff --git a/etc/inc/captiveportal.inc b/etc/inc/captiveportal.inc index 33d1b75..c7095d6 100644 --- a/etc/inc/captiveportal.inc +++ b/etc/inc/captiveportal.inc @@ -102,6 +102,16 @@ function captiveportal_configure() { killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid"); killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid"); + /* remove old information */ + 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"); + unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db"); + mwexec("/sbin/ipfw table all flush"); + + /* setup new database in case someone tries to access the status -> captive portal page */ + touch("{$g['vardb_path']}/captiveportal.db"); + /* kill any running minicron */ killbypid("{$g['varrun_path']}/minicron.pid"); @@ -112,7 +122,14 @@ function captiveportal_configure() { mwexec("/sbin/kldload dummynet"); /* generate ipfw rules */ + captiveportal_init_ipfw_ruleno(); $cprules = captiveportal_rules_generate($cpinterface, $cpips); + $cprules .= "\n"; + /* generate passthru mac database */ + $cprules .= captiveportal_passthrumac_configure(true); + $cprules .= "\n"; + /* allowed ipfw rules to make allowed ip work */ + $cprules .= captiveportal_allowedip_configure(); /* stop accounting on all clients */ captiveportal_radius_stop_all(true); @@ -123,17 +140,6 @@ function captiveportal_configure() { /* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */ 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"); - unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db"); - mwexec("/sbin/ipfw table all flush"); - - /* setup new database in case someone tries to access the status -> captive portal page */ - touch("{$g['vardb_path']}/captiveportal.db"); - /* write portal page */ if ($config['captiveportal']['page']['htmltext']) $htmltext = base64_decode($config['captiveportal']['page']['htmltext']); @@ -210,7 +216,7 @@ EOD; captiveportal_write_elements(); /* load rules */ - mwexec("/sbin/ipfw -f delete set 1"); + mwexec("/sbin/ipfw -q flush"); /* ipfw cannot accept rules directly on stdin, so we have to write them to a temporary file first */ @@ -225,7 +231,7 @@ EOD; mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.cp.rules"); - unlink("{$g['tmp_path']}/ipfw.cp.rules"); + @unlink("{$g['tmp_path']}/ipfw.cp.rules"); /* filter on layer2 as well so we can check MAC addresses */ mwexec("/sbin/sysctl net.link.ether.ipfw=1"); @@ -268,11 +274,6 @@ EOD; mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/minicron.pid " . "/etc/rc.prunecaptiveportal"); - /* generate passthru mac database */ - captiveportal_passthrumac_configure(true); - /* allowed ipfw rules to make allowed ip work */ - captiveportal_allowedip_configure(); - /* generate radius server database */ if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) || ($config['captiveportal']['auth_method'] == "radius"))) { @@ -346,36 +347,30 @@ EOD; function captiveportal_rules_generate($cpif, &$cpiparray) { global $config, $g; - $cpifn = $config['captiveportal']['interface']; - $lanip = get_interface_ip("lan"); - - /* note: the captive portal daemon inserts all pass rules for authenticated - clients as skipto 50000 rules to make traffic shaping work */ - - $cprules = "add 500 set 1 allow pfsync from any to any\n"; - $cprules .= "add 500 set 1 allow carp from any to any\n"; + $cprules = "add 65301 set 1 allow pfsync from any to any\n"; + $cprules .= "add 65302 set 1 allow carp from any to any\n"; $cprules .= <<<EOD -add 1000 set 1 skipto 1150 all from any to any not layer2 +# add 65305 set 1 skipto 65534 all from any to any not layer2 # layer 2: pass ARP -add 1100 set 1 pass layer2 mac-type arp +add 65310 set 1 pass layer2 mac-type arp # pfsense requires for WPA -add 1100 set 1 pass layer2 mac-type 0x888e -add 1100 set 1 pass layer2 mac-type 0x88c7 +add 65311 set 1 pass layer2 mac-type 0x888e +add 65312 set 1 pass layer2 mac-type 0x88c7 # PPP Over Ethernet Discovery Stage -add 1100 set 1 pass layer2 mac-type 0x8863 +add 65313 set 1 pass layer2 mac-type 0x8863 # PPP Over Ethernet Session Stage -add 1100 set 1 pass layer2 mac-type 0x8864 +add 65314 set 1 pass layer2 mac-type 0x8864 # Allow WPA -add 1100 set 1 pass layer2 mac-type 0x888e +add 65315 set 1 pass layer2 mac-type 0x888e # layer 2: block anything else non-IP -add 1101 set 1 deny layer2 not mac-type ip +add 65316 set 1 deny layer2 not mac-type ip EOD; - $rulenum = 1150; + $rulenum = 65320; $ips = "255.255.255.255 "; foreach ($cpiparray as $cpip) $ips .= "or {$cpip} "; @@ -420,30 +415,27 @@ EOD; $rulenum++; if (isset($config['captiveportal']['peruserbw'])) { - $cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(3) to any in\n"; + $cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(1) to any in\n"; $rulenum++; - $cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(4) out\n"; + $cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(2) out\n"; $rulenum++; } else { - $cprules .= "add {$rulenum} set 1 skipto 50000 ip from table(3) to any in\n"; + $cprules .= "add {$rulenum} set 1 allow ip from table(1) to any in\n"; $rulenum++; - $cprules .= "add {$rulenum} set 1 skipto 50000 ip from any to table(4) out\n"; + $cprules .= "add {$rulenum} set 1 allow ip from any to table(2) out\n"; $rulenum++; } $cprules .= <<<EOD # redirect non-authenticated clients to captive portal -add 1990 set 1 fwd 127.0.0.1,8000 tcp from any to any in +add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in # let the responses from the captive portal web server back out -add 1991 set 1 pass tcp from any to any out +add 65532 set 1 pass tcp from any to any out # block everything else -add 1992 set 1 deny all from any to any - -# ... 2000-49899: layer2 block rules per authenticated client go here... - +add 65533 set 1 deny all from any to any # pass everything else on layer2 -add 49900 set 1 pass all from any to any layer2 +add 65534 set 1 pass all from any to any layer2 EOD; @@ -566,8 +558,8 @@ function captiveportal_prune_old() { $cpdb[$i][2], // clientip $cpdb[$i][3], // clientmac 10); // NAS Request - exec("/sbin/ipfw table 3 entryzerostats {$cpdb[$i][2]}"); - exec("/sbin/ipfw table 4 entryzerostats {$cpdb[$i][2]}"); + exec("/sbin/ipfw table 1 entryzerostats {$cpdb[$i][2]}"); + exec("/sbin/ipfw table 2 entryzerostats {$cpdb[$i][2]}"); RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno $cpdb[$i][4], // username $cpdb[$i][5], // sessionid @@ -633,8 +625,10 @@ function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_t $stop_time); } /* Delete client's ip entry from tables 3 and 4. */ - mwexec("/sbin/ipfw table 3 delete {$dbent[2]}"); - mwexec("/sbin/ipfw table 4 delete {$dbent[2]}"); + mwexec("/sbin/ipfw table 1 delete {$dbent[2]}"); + mwexec("/sbin/ipfw table 2 delete {$dbent[2]}"); + /* Release the ruleno so it can be reallocated to new clients. */ + captiveportal_free_ipfw_ruleno($dbent[1]); /* * These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal @@ -645,7 +639,7 @@ function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_t mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete"); } - /* Ensure all pf(4) states are killed. */ + /* XXX: Redundant?! Ensure all pf(4) states are killed. */ mwexec("pfctl -k {$dbent[2]}"); mwexec("pfctl -K {$dbent[2]}"); @@ -664,7 +658,8 @@ function captiveportal_disconnect_client($id,$term_cause = 1) { /* find entry */ $tmpindex = 0; - for ($i = 0; $i < count($cpdb); $i++) { + $cpdbcount = count($cpdb); + for ($i = 0; $i < $cpdbcount; $i++) { if ($cpdb[$i][1] == $id) { captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause); captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT"); @@ -717,6 +712,7 @@ function captiveportal_passthrumac_configure($lock = false) { /* clear out passthru macs, if necessary */ unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db"); + $rules = ""; if (is_array($config['captiveportal']['passthrumac'])) { $fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w"); @@ -726,69 +722,85 @@ function captiveportal_passthrumac_configure($lock = false) { return 1; } + $peruserbw = isset($config['captiveportal']['peruserbw']); + $macdb = ""; foreach ($config['captiveportal']['passthrumac'] as $macent) { - /* record passthru mac so it can be recognized and let thru */ - fwrite($fd, $macent['mac'] . "\n"); + $ruleno = captiveportal_get_next_ipfw_ruleno(); + + $macdb .= $macent['mac'] . "\n"; + + /* pfSense: + * pass through mac entries should always exist. the reason + * for this is because we do not have native mac address filtering + * mechanisms. this allows us to filter by mac address easily + * and get around this limitation. I consider this a bug in + * m0n0wall and pfSense as m0n0wall does not have native mac + * filtering mechanisms as well. -Scott Ullrich + * + * Add rules for traffic shaping. + * This assumes that net.inet.ip.fw.one_pass: 1 is set. + */ + + $actionup = "allow"; + $actiondown = "allow"; + if ($peruserbw) { + $bw_up = isset($macent['bw_up']) ? trim($macent['bw_up']) : $config['captiveportal']['bwdefaultup']; + $bw_down = isset($macent['bw_down']) ? trim($macent['bw_down']) : $config['captiveportal']['bwdefaultdn']; + if (!empty($bw_up) && is_numeric($bw_up)) { + $bw_up_pipeno = $ruleno + 20000; + $rules .= "pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100\n"; + $actionup = "pipe {$bw_up_pipeno}"; + } + if (!empty($bw_down) && is_numeric($bw_down)) { + $bw_down_pipeno = $ruleno + 20001; + $rules .= "pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100\n"; + $actiondown = "pipe {$bw_down_pipeno}"; + } + } + $rules .= "add {$ruleno} {$actionup} ip from any to any MAC {$macent['mac']} any\n"; + $ruleno++; + $rules .= "add {$ruleno} {$actiondown} ip from any to any MAC any {$macent['mac']}\n"; } - + /* record passthru MACs so can be recognized and let thru */ + fwrite($fd, $macdb); fclose($fd); } - /* pfSense: - * pass through mac entries should always exist. the reason - * for this is because we do not have native mac address filtering - * mechanisms. this allows us to filter by mac address easily - * and get around this limitation. I consider this a bug in - * m0n0wall and pfSense as m0n0wall does not have native mac - * filtering mechanisms as well. -Scott Ullrich - */ - if (is_array($config['captiveportal']['passthrumac'])) { - mwexec("/sbin/ipfw delete 50"); - foreach($config['captiveportal']['passthrumac'] as $ptm) { - /* create the pass through mac entry */ - //system("echo /sbin/ipfw add 50 skipto 65535 ip from any to any MAC {$ptm['mac']} any > /tmp/cp"); - mwexec("/sbin/ipfw add 50 skipto 49900 ip from any to any MAC {$ptm['mac']} any keep-state"); - mwexec("/sbin/ipfw add 50 skipto 49900 ip from any to any MAC any {$ptm['mac']} keep-state"); - } - } - if (!$lock) unlock($captiveportallck); - return 0; + return $rules; } function captiveportal_allowedip_configure() { global $config, $g; - /* clear out existing allowed ips, if necessary */ - mwexec("/sbin/ipfw table 1 flush"); - mwexec("/sbin/ipfw table 2 flush"); - + $rules = ""; if (is_array($config['captiveportal']['allowedip'])) { - $tableone = false; - $tabletwo = false; + $peruserbw = isset($config['captiveportal']['peruserbw']); foreach ($config['captiveportal']['allowedip'] as $ipent) { + $ruleno = captiveportal_get_next_ipfw_ruleno(); + $bw_up = ""; + $bw_down = ""; + if ($peruserbw) { + $bwup = isset($ipent['bw_up']) ? trim($ipent['bw_up']) : $config['captiveportal']['bwdefaultup']; + $bwdown = isset($ipent['bw_down']) ? trim($ipent['bw_down']) : $config['captiveportal']['bwdefaultdn']; + if (!empty($bwup) && is_numeric($bwup)) { + $bw_up = $ruleno + 20000; + $rules .= "pipe {$bw_up} config bw {$bw_up}Kbit/s queue 100\n"; + } + if (!empty($bwdown) && is_numeric($bwdown)) { + $bw_down = $ruleno + 20001; + $rules .= "pipe {$bw_down} config bw {$bw_down}Kbit/s queue 100\n"; + } + } /* insert address in ipfw table */ - if ($ipent['dir'] == "from") { - mwexec("/sbin/ipfw table 1 add {$ipent['ip']}"); - $tableone = true; - } else { - mwexec("/sbin/ipfw table 2 add {$ipent['ip']}"); - $tabletwo = true; - } - } - if ($tableone == true) { - mwexec("/sbin/ipfw add 1890 set 1 skipto 50000 ip from table\(1\) to any in"); - mwexec("/sbin/ipfw add 1891 set 1 skipto 50000 ip from any to table\(1\) out"); - } - if ($tabletwo == true) { - mwexec("/sbin/ipfw add 1892 set 1 skipto 50000 ip from any to table\(2\) in"); - mwexec("/sbin/ipfw add 1893 set 1 skipto 50000 ip from table\(2\) to any out"); + $rules .= "table 1 add {$ipent['ip']} ${bw_up}\n"; + $rules .= "table 2 add {$ipent['ip']} ${bw_down}\n"; } } - return 0; + return $rules; } /* get last activity timestamp given client IP address */ @@ -796,7 +808,7 @@ function captiveportal_get_last_activity($ip) { $ipfwoutput = ""; - exec("/sbin/ipfw table 3 entrystats {$ip} 2>/dev/null", $ipfwoutput); + exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput); /* Reading only from one of the tables is enough of approximation. */ if ($ipfwoutput[0]) { $ri = explode(" ", $ipfwoutput[0]); @@ -974,6 +986,14 @@ function captiveportal_write_elements() { return 0; } +function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) { + global $g; + + @unlink("{$g['vardb_path']}/captiveportal.rules"); + $rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false); + file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules)); +} + /* * This function will calculate the lowest free firewall ruleno * within the range specified based on the actual logged on users @@ -981,26 +1001,67 @@ function captiveportal_write_elements() { */ function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) { global $config, $g; + if(!isset($config['captiveportal']['enable'])) return NULL; + $ruleno = 0; - if (file_exists("{$g['vardb_path']}/captiveportal.nextrule")) - $ruleno = intval(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule")); - else - $ruleno = 1; - if ($ruleno > 0 && (($rulenos_start + $ruleno) < $rulenos_range_max)) { - /* - * This allows our traffic shaping pipes to be the in pipe the same as ruleno - * and the out pipe ruleno + 1. This removes limitation that where present in - * previous version of the peruserbw. - */ + if (file_exists("{$g['vardb_path']}/captiveportal.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules")); + for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) { + if ($rules[$ridx]) { + /* + * This allows our traffic shaping pipes to be the in pipe the same as ruleno + * and the out pipe ruleno + 1. This removes limitation that where present in + * previous version of the peruserbw. + */ + if (isset($config['captiveportal']['peruserbw'])) + $ridx++; + continue; + } + $ruleno = $ridx; + $rules[$ridx] = "used"; + if (isset($config['captiveportal']['peruserbw'])) + $rules[++$ridx] = "used"; + break; + } + } else { + $rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false); + $rules[2] = "used"; + $ruleno = 2; + } + file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules)); + return $ruleno; +} + +function captiveportal_free_ipfw_ruleno($ruleno) { + global $config, $g; + + if(!isset($config['captiveportal']['enable'])) + return NULL; + + if (file_exists("{$g['vardb_path']}/captiveportal.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules")); + $rules[$ruleno] = false; if (isset($config['captiveportal']['peruserbw'])) - $ruleno += 2; - else - $ruleno++; - file_put_contents("{$g['vardb_path']}/captiveportal.nextrule", $ruleno); - return $rulenos_start + $ruleno; + $rules[++$ruleno] = false; + file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules)); } +} + +function captiveportal_get_ipfw_ruleno_byvalue($value) { + global $config, $g; + + if(!isset($config['captiveportal']['enable'])) + return NULL; + + if (file_exists("{$g['vardb_path']}/captiveportal.rules")) { + $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules")); + $ruleno = intval(`/sbin/ipfw show | /usr/bin/grep 00:0c:29:88:77:80 | /usr/bin/grep -v grep | /usr/bin/cut -d " " -f 1 | /usr/bin/head -n 1`); + if ($rules[$ruleno]) + return $ruleno; + } + return NULL; } @@ -1027,14 +1088,14 @@ function getVolume($ip) { $ipfwout = ""; $matchesin = ""; $matchesout = ""; - exec("/sbin/ipfw table 3 entrystats {$ip}", $ipfwin); + exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin); if ($ipfwin[0]) { $ipfwin = split(" ", $ipfwin[0]); $volume['input_pkts'] = $ipfwin[2]; $volume['input_bytes'] = $ipfwin[3]; } - exec("/sbin/ipfw table 4 entrystats {$ip}", $ipfwout); + exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout); if ($ipfwout[0]) { $ipfwout = split(" ", $ipfwout[0]); $volume['output_pkts'] = $ipfwout[2]; |