summaryrefslogtreecommitdiffstats
path: root/etc/inc
diff options
context:
space:
mode:
authorScott Ullrich <sullrich@pfsense.org>2004-11-07 03:06:49 +0000
committerScott Ullrich <sullrich@pfsense.org>2004-11-07 03:06:49 +0000
commit5b237745003431d487de361ca0980a467ee2f5d5 (patch)
tree0a29f0237f9e8e536112f9fc816e7a52bbc19691 /etc/inc
downloadpfsense-5b237745003431d487de361ca0980a467ee2f5d5.zip
pfsense-5b237745003431d487de361ca0980a467ee2f5d5.tar.gz
Initial revision
Diffstat (limited to 'etc/inc')
-rw-r--r--etc/inc/captiveportal.inc642
-rw-r--r--etc/inc/config.inc551
-rw-r--r--etc/inc/filter.inc946
-rw-r--r--etc/inc/functions.inc41
-rw-r--r--etc/inc/globals.inc54
-rw-r--r--etc/inc/interfaces.inc740
-rw-r--r--etc/inc/openvpn.inc559
-rw-r--r--etc/inc/services.inc440
-rw-r--r--etc/inc/shaper.inc403
-rw-r--r--etc/inc/system.inc563
-rw-r--r--etc/inc/util.inc421
-rw-r--r--etc/inc/vpn.inc559
-rw-r--r--etc/inc/xmlparse.inc205
13 files changed, 6124 insertions, 0 deletions
diff --git a/etc/inc/captiveportal.inc b/etc/inc/captiveportal.inc
new file mode 100644
index 0000000..d5d78b1
--- /dev/null
+++ b/etc/inc/captiveportal.inc
@@ -0,0 +1,642 @@
+<?php
+/*
+ captiveportal.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("functions.inc");
+require_once("radius_accounting.inc") ;
+
+function captiveportal_configure() {
+ global $config, $g;
+
+ if (isset($config['captiveportal']['enable']) &&
+ (($config['captiveportal']['interface'] == "lan") ||
+ isset($config['interfaces'][$config['captiveportal']['interface']]['enable']))) {
+
+ if ($g['booting'])
+ echo "Starting captive portal... ";
+
+ /* kill any running mini_httpd */
+ killbypid("{$g['varrun_path']}/mini_httpd.cp.pid");
+ killbypid("{$g['varrun_path']}/mini_httpd.cps.pid");
+
+ /* kill any running minicron */
+ killbypid("{$g['varrun_path']}/minicron.pid");
+
+ /* generate ipfw rules */
+ $cprules = captiveportal_rules_generate();
+
+ /* make sure ipfw is loaded */
+ mwexec("/sbin/kldload ipfw");
+
+ /* stop accounting on all clients */
+ captiveportal_radius_stop_all() ;
+
+ /* 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");
+
+ /* write portal page */
+ if ($config['captiveportal']['page']['htmltext'])
+ $htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
+ else {
+ /* example/template page */
+ $htmltext = <<<EOD
+<html>
+<head>
+<title>m0n0wall captive portal</title>
+</head>
+<body>
+<h2>m0n0wall captive portal</h2>
+<p>This is the default captive portal page. Please upload your own custom HTML file on the <em>Services: Captive portal</em> screen in the m0n0wall webGUI.</p>
+<form method="post" action="">
+ <input name="accept" type="submit" value="Continue">
+</form>
+</body>
+</html>
+
+EOD;
+ }
+
+ $fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
+ if ($fd) {
+ fwrite($fd, $htmltext);
+ fclose($fd);
+ }
+
+ /* write error page */
+ if ($config['captiveportal']['page']['errtext'])
+ $errtext = base64_decode($config['captiveportal']['page']['errtext']);
+ else {
+ /* example page */
+ $errtext = <<<EOD
+<html>
+<head>
+<title>Authentication error</title>
+</head>
+<body>
+<font color="#cc0000"><h2>Authentication error</h2></font>
+<b>
+Username and/or password invalid.
+<br><br>
+<a href="javascript:history.back()">Go back</a>
+</b>
+</body>
+</html>
+
+EOD;
+ }
+
+ $fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
+ if ($fd) {
+ fwrite($fd, $errtext);
+ fclose($fd);
+ }
+
+ /* load rules */
+ mwexec("/sbin/ipfw -f delete set 1");
+ mwexec("/sbin/ipfw -f delete set 2");
+ mwexec("/sbin/ipfw -f delete set 3");
+
+ /* XXX - seems like ipfw cannot accept rules directly on stdin,
+ so we have to write them to a temporary file first */
+ $fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w");
+ if (!$fd) {
+ printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n");
+ return 1;
+ }
+
+ fwrite($fd, $cprules);
+ fclose($fd);
+
+ mwexec("/sbin/ipfw {$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");
+
+ chdir($g['captiveportal_path']);
+
+ /* start web server */
+ mwexec("/usr/local/sbin/mini_httpd -a -M 0 -u root -maxproc 16" .
+ " -p 8000 -i {$g['varrun_path']}/mini_httpd.cp.pid");
+
+ /* fire up another one for HTTPS if requested */
+ if (isset($config['captiveportal']['httpslogin']) &&
+ $config['captiveportal']['certificate'] && $config['captiveportal']['private-key']) {
+
+ $cert = base64_decode($config['captiveportal']['certificate']);
+ $key = base64_decode($config['captiveportal']['private-key']);
+
+ $fd = fopen("{$g['varetc_path']}/cert-portal.pem", "w");
+ if (!$fd) {
+ printf("Error: cannot open cert-portal.pem in system_webgui_start().\n");
+ return 1;
+ }
+ chmod("{$g['varetc_path']}/cert-portal.pem", 0600);
+ fwrite($fd, $cert);
+ fwrite($fd, "\n");
+ fwrite($fd, $key);
+ fclose($fd);
+
+ mwexec("/usr/local/sbin/mini_httpd -S -a -M 0 -E {$g['varetc_path']}/cert-portal.pem" .
+ " -u root -maxproc 16 -p 8001" .
+ " -i {$g['varrun_path']}/mini_httpd.cps.pid");
+ }
+
+ /* start pruning process (interval = 60 seconds) */
+ mwexec("/usr/local/bin/minicron 60 {$g['varrun_path']}/minicron.pid " .
+ "/etc/rc.prunecaptiveportal");
+
+ /* generate passthru mac database */
+ captiveportal_passthrumac_configure() ;
+ /* create allowed ip database and insert ipfw rules to make it so */
+ captiveportal_allowedip_configure() ;
+
+ /* generate radius server database */
+ if($config['captiveportal']['radiusip']) {
+ $radiusip = $config['captiveportal']['radiusip'] ;
+
+ if($config['captiveportal']['radiusport'])
+ $radiusport = $config['captiveportal']['radiusport'] ;
+ else
+ $radiusport = 1812;
+
+ if($config['captiveportal']['radiusacctport'])
+ $radiusacctport = $config['captiveportal']['radiusacctport'] ;
+ else
+ $radiusacctport = 1813;
+
+ $radiuskey = $config['captiveportal']['radiuskey'];
+
+ $fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
+ if (!$fd) {
+ printf("Error: cannot open radius DB file in captiveportal_configure().\n");
+ return 1;
+ } else {
+ fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey) ;
+ }
+ fclose($fd) ;
+ }
+
+
+ if ($g['booting'])
+ echo "done\n";
+
+ } else {
+ killbypid("{$g['varrun_path']}/mini_httpd.cp.pid");
+ killbypid("{$g['varrun_path']}/minicron.pid");
+ captiveportal_radius_stop_all() ;
+ mwexec("/sbin/sysctl net.link.ether.ipfw=0");
+ if (!isset($config['shaper']['enable'])) {
+ /* unload ipfw */
+ mwexec("/sbin/kldunload ipfw");
+ } else {
+ /* shaper is on - just remove our rules */
+ mwexec("/sbin/ipfw -f delete set 1");
+ mwexec("/sbin/ipfw -f delete set 2");
+ mwexec("/sbin/ipfw -f delete set 3");
+ }
+ }
+
+ return 0;
+}
+
+function captiveportal_rules_generate() {
+ global $config, $g;
+
+ $cpifn = $config['captiveportal']['interface'];
+ $cpif = $config['interfaces'][$cpifn]['if'];
+ $cpip = $config['interfaces'][$cpifn]['ipaddr'];
+
+ /* note: the captive portal daemon inserts all pass rules for authenticated
+ clients as skipto 50000 rules to make traffic shaping work */
+
+ $cprules = "";
+
+ /* captive portal on LAN interface? */
+ if ($cpifn == "lan") {
+ /* add anti-lockout rules */
+ $cprules .= <<<EOD
+add 500 set 1 pass all from $cpip to any out via $cpif
+add 501 set 1 pass all from any to $cpip in via $cpif
+
+EOD;
+ }
+
+ $cprules .= <<<EOD
+# skip to traffic shaper if not on captive portal interface
+add 1000 set 1 skipto 50000 all from any to any not layer2 not via $cpif
+# pass all layer2 traffic on other interfaces
+add 1001 set 1 pass layer2 not via $cpif
+
+# layer 2: pass ARP
+add 1100 set 1 pass layer2 mac-type arp
+# layer 2: block anything else non-IP
+add 1101 set 1 deny layer2 not mac-type ip
+# layer 2: check if MAC addresses of authenticated clients are correct
+add 1102 set 1 skipto 20000 layer2
+
+# allow access to our DHCP server (which needs to be able to ping clients as well)
+add 1200 set 1 pass udp from any 68 to 255.255.255.255 67 in
+add 1201 set 1 pass udp from any 68 to $cpip 67 in
+add 1202 set 1 pass udp from $cpip 67 to any 68 out
+add 1203 set 1 pass icmp from $cpip to any out icmptype 8
+add 1204 set 1 pass icmp from any to $cpip in icmptype 0
+
+# allow access to our DNS forwarder
+add 1300 set 1 pass udp from any to $cpip 53 in
+add 1301 set 1 pass udp from $cpip 53 to any out
+
+# allow access to our web server
+add 1302 set 1 pass tcp from any to $cpip 8000 in
+add 1303 set 1 pass tcp from $cpip 8000 to any out
+
+EOD;
+
+ if (isset($config['captiveportal']['httpslogin'])) {
+ $cprules .= <<<EOD
+add 1304 set 1 pass tcp from any to $cpip 8001 in
+add 1305 set 1 pass tcp from $cpip 8001 to any out
+
+EOD;
+ }
+
+ $cprules .= <<<EOD
+
+# ... 10000-19899: rules per authenticated client go here...
+
+# redirect non-authenticated clients to captive portal
+add 19900 set 1 fwd 127.0.0.1,8000 tcp from any to any 80 in
+# let the responses from the captive portal web server back out
+add 19901 set 1 pass tcp from any 80 to any out
+# block everything else
+add 19902 set 1 deny all from any to any
+
+# ... 20000-29899: layer2 block rules per authenticated client go here...
+
+# pass everything else on layer2
+add 29900 set 1 pass all from any to any layer2
+
+EOD;
+
+ return $cprules;
+}
+
+/* remove clients that have been around for longer than the specified amount of time */
+/* db file structure: timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid */
+function captiveportal_prune_old() {
+
+ global $g, $config;
+
+ /* check for expired entries */
+ if ($config['captiveportal']['timeout'])
+ $timeout = $config['captiveportal']['timeout'] * 60;
+ else
+ $timeout = 0;
+
+ if ($config['captiveportal']['idletimeout'])
+ $idletimeout = $config['captiveportal']['idletimeout'] * 60;
+ else
+ $idletimeout = 0;
+
+ if (!$timeout && !$idletimeout)
+ return;
+
+ captiveportal_lock();
+
+ /* read database */
+ $cpdb = captiveportal_read_db();
+
+ $radiusservers = captiveportal_get_radius_servers();
+
+ for ($i = 0; $i < count($cpdb); $i++) {
+
+ $timedout = false;
+
+ /* hard timeout? */
+ if ($timeout) {
+ if ((time() - $cpdb[$i][0]) >= $timeout)
+ $timedout = true;
+ }
+
+ /* if an idle timeout is specified, get last activity timestamp from ipfw */
+ if (!$timedout && $idletimeout) {
+ $lastact = captiveportal_get_last_activity($cpdb[$i][1]);
+ if ($lastact && ((time() - $lastact) >= $idletimeout))
+ $timedout = true;
+ }
+
+ if ($timedout) {
+ /* this client needs to be deleted - remove ipfw rules */
+ if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
+ RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
+ $cpdb[$i][4], // username
+ $cpdb[$i][5], // sessionid
+ $cpdb[$i][0], // start time
+ $radiusservers[0]['ipaddr'],
+ $radiusservers[0]['acctport'],
+ $radiusservers[0]['key']);
+ }
+ mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000));
+ unset($cpdb[$i]);
+ }
+ }
+
+ /* write database */
+ captiveportal_write_db($cpdb);
+
+ captiveportal_unlock();
+}
+
+/* remove a single client by ipfw rule number */
+function captiveportal_disconnect_client($id) {
+
+ global $g, $config;
+
+ captiveportal_lock();
+
+ /* read database */
+ $cpdb = captiveportal_read_db();
+ $radiusservers = captiveportal_get_radius_servers();
+
+ /* find entry */
+ for ($i = 0; $i < count($cpdb); $i++) {
+ if ($cpdb[$i][1] == $id) {
+ /* this client needs to be deleted - remove ipfw rules */
+ if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
+ RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
+ $cpdb[$i][4], // username
+ $cpdb[$i][5], // sessionid
+ $cpdb[$i][0], // start time
+ $radiusservers[0]['ipaddr'],
+ $radiusservers[0]['acctport'],
+ $radiusservers[0]['key']);
+ }
+ mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000));
+ unset($cpdb[$i]);
+ break;
+ }
+ }
+
+ /* write database */
+ captiveportal_write_db($cpdb);
+
+ captiveportal_unlock();
+}
+
+/* send RADIUS acct stop for all current clients */
+function captiveportal_radius_stop_all() {
+ global $g, $config;
+
+ captiveportal_lock() ;
+ $cpdb = captiveportal_read_db() ;
+
+ $radiusservers = captiveportal_get_radius_servers();
+
+ if (isset($radiusservers[0])) {
+ for ($i = 0; $i < count($cpdb); $i++) {
+ RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
+ $cpdb[$i][4], // username
+ $cpdb[$i][5], // sessionid
+ $cpdb[$i][0], // start time
+ $radiusservers[0]['ipaddr'],
+ $radiusservers[0]['acctport'],
+ $radiusservers[0]['key']);
+ }
+ }
+ captiveportal_unlock() ;
+}
+
+function captiveportal_passthrumac_configure() {
+ global $config, $g;
+
+ /* clear out passthru macs, if necessary */
+ if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) {
+ unlink("{$g['vardb_path']}/captiveportal_mac.db");
+ }
+
+ if (is_array($config['captiveportal']['passthrumac'])) {
+
+ $fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w");
+ if (!$fd) {
+ printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n");
+ return 1;
+ }
+
+ foreach ($config['captiveportal']['passthrumac'] as $macent) {
+ /* record passthru mac so it can be recognized and let thru */
+ fwrite($fd, $macent['mac'] . "\n");
+ }
+
+ fclose($fd);
+ }
+
+ return 0;
+}
+
+function captiveportal_allowedip_configure() {
+ global $config, $g;
+
+ captiveportal_lock() ;
+
+ /* clear out existing allowed ips, if necessary */
+ if (file_exists("{$g['vardb_path']}/captiveportal_ip.db")) {
+ $fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "r");
+ if ($fd) {
+ while (!feof($fd)) {
+ $line = trim(fgets($fd));
+ 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;
+ }
+
+ foreach ($config['captiveportal']['allowedip'] as $ipent) {
+ /* 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") ;
+ mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to ".$ipent['ip']." out") ;
+ } else {
+ 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);
+ }
+ }
+
+ 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;
+}
+
+/* read captive portal DB into array */
+function captiveportal_read_db() {
+
+ global $g;
+
+ $cpdb = array();
+ $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
+ if ($fd) {
+ while (!feof($fd)) {
+ $line = trim(fgets($fd));
+ if ($line) {
+ $cpdb[] = explode(",", $line);
+ }
+ }
+ fclose($fd);
+ }
+ return $cpdb;
+}
+
+/* write captive portal DB */
+function captiveportal_write_db($cpdb) {
+
+ global $g;
+
+ $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
+ if ($fd) {
+ foreach ($cpdb as $cpent) {
+ fwrite($fd, join(",", $cpent) . "\n");
+ }
+ fclose($fd);
+ }
+}
+
+/* read RADIUS servers into array */
+function captiveportal_get_radius_servers() {
+
+ global $g;
+
+ if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
+ $fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
+ if ($fd) {
+ $radiusservers = array();
+ while (!feof($fd)) {
+ $line = trim(fgets($fd));
+ if ($line) {
+ $radsrv = array();
+ list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
+ $radiusservers[] = $radsrv;
+ }
+ }
+ fclose($fd);
+
+ return $radiusservers;
+ }
+ }
+
+ return false;
+}
+
+/* lock captive portal information, decide that the lock file is stale after
+ 10 seconds */
+function captiveportal_lock() {
+
+ global $g;
+
+ $lockfile = "{$g['varrun_path']}/captiveportal.lock";
+
+ $n = 0;
+ while ($n < 10) {
+ /* open the lock file in append mode to avoid race condition */
+ if ($fd = @fopen($lockfile, "x")) {
+ /* succeeded */
+ fclose($fd);
+ return;
+ } else {
+ /* file locked, wait and try again */
+ sleep(1);
+ $n++;
+ }
+ }
+}
+
+/* unlock configuration file */
+function captiveportal_unlock() {
+
+ global $g;
+
+ $lockfile = "{$g['varrun_path']}/captiveportal.lock";
+
+ if (file_exists($lockfile))
+ unlink($lockfile);
+}
+
+?>
diff --git a/etc/inc/config.inc b/etc/inc/config.inc
new file mode 100644
index 0000000..58202aa
--- /dev/null
+++ b/etc/inc/config.inc
@@ -0,0 +1,551 @@
+<?php
+/*
+ config.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include globals/utility/XML parser files */
+require_once("globals.inc");
+require_once("util.inc");
+require_once("xmlparse.inc");
+
+/* read platform */
+if (file_exists("{$g['etc_path']}/platform")) {
+ $g['platform'] = chop(file_get_contents("{$g['etc_path']}/platform"));
+} else {
+ $g['platform'] = "unknown";
+}
+
+if ($g['booting']) {
+ /* find the device where config.xml resides and write out an fstab */
+ unset($cfgdevice);
+
+ /* check if there's already an fstab (NFS booting?) */
+ if (!file_exists("{$g['etc_path']}/fstab")) {
+
+ if (strstr($g['platform'], "cdrom")) {
+ /* config is on floppy disk for CD-ROM version */
+ $cfgdevice = $cfgpartition = "fd0";
+ $cfgfstype = "msdos";
+ } else {
+ /* probe kernel known disks until we find one with config.xml */
+ $disks = explode(" ", trim(preg_replace("/kern.disks: /", "", exec("/sbin/sysctl kern.disks"))));
+ foreach ($disks as $mountdisk) {
+ /* skip mfs mounted filesystems */
+ if (strstr($mountdisk, "md"))
+ continue;
+ if (mwexec("/sbin/mount -r /dev/{$mountdisk}a {$g['cf_path']}") == 0) {
+ if (file_exists("{$g['cf_conf_path']}/config.xml")) {
+ /* found it */
+ $cfgdevice = $mountdisk;
+ $cfgpartition = $cfgdevice . "a";
+ $cfgfstype = "ufs";
+ echo "Found configuration on $cfgdevice.\n";
+ }
+
+ mwexec("/sbin/umount -f {$g['cf_path']}");
+
+ if ($cfgdevice)
+ break;
+ }
+ }
+ }
+
+ if (!$cfgdevice) {
+ /* no device found, print an error and die */
+ echo <<<EOD
+
+
+*******************************************************************************
+* FATAL ERROR *
+* The device that contains the configuration file (config.xml) could not be *
+* found. m0n0wall cannot continue booting. *
+*******************************************************************************
+
+
+EOD;
+
+ mwexec("/sbin/halt");
+ exit;
+ }
+
+ /* write device name to a file for rc.firmware */
+ $fd = fopen("{$g['varetc_path']}/cfdevice", "w");
+ fwrite($fd, $cfgdevice . "\n");
+ fclose($fd);
+
+ /* write out an fstab */
+ $fd = fopen("{$g['etc_path']}/fstab", "w");
+
+ $fstab = "/dev/{$cfgpartition} {$g['cf_path']} {$cfgfstype} ro 1 1\n";
+ $fstab .= "proc /proc procfs rw 0 0\n";
+
+ fwrite($fd, $fstab);
+ fclose($fd);
+ }
+
+ /* mount all filesystems */
+ mwexec("/sbin/mount -a");
+}
+
+/* parse configuration */
+if (!$noparseconfig) {
+
+ config_lock();
+
+ /* see if there's a newer cache file */
+ if (file_exists("{$g['tmp_path']}/config.cache") &&
+ (filemtime("{$g['tmp_path']}/config.cache") >=
+ filemtime("{$g['conf_path']}/config.xml"))) {
+
+ /* read cache */
+ $config = unserialize(file_get_contents("{$g['tmp_path']}/config.cache"));
+ } else {
+
+ if (!file_exists("{$g['conf_path']}/config.xml")) {
+ if ($g['booting']) {
+ if (strstr($g['platform'], "cdrom")) {
+ /* try copying the default config. to the floppy */
+ reset_factory_defaults();
+
+ echo "No XML configuration file found - using factory defaults.\n";
+ echo "Make sure that the configuration floppy disk with the conf/config.xml\n";
+ echo "file is inserted. If it isn't, your configuration changes will be lost\n";
+ echo "on reboot.\n";
+ } else {
+ echo "XML configuration file not found. m0n0wall cannot continue booting.\n";
+ mwexec("/sbin/halt");
+ exit;
+ }
+ } else {
+ config_unlock();
+ exit(0);
+ }
+ }
+
+ $config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
+
+ if ((float)$config['version'] > (float)$g['latest_config']) {
+ if ($g['booting']) {
+ echo <<<EOD
+
+
+*******************************************************************************
+* WARNING! *
+* The current configuration has been created with a newer version of m0n0wall *
+* than this one! This can lead to serious misbehavior and even security *
+* holes! You are urged to either upgrade to a newer version of m0n0wall or *
+* revert to the default configuration immediately! *
+*******************************************************************************
+
+
+EOD;
+ }
+ }
+
+ /* write config cache */
+ $fd = @fopen("{$g['tmp_path']}/config.cache", "wb");
+ if ($fd) {
+ fwrite($fd, serialize($config));
+ fclose($fd);
+ }
+ }
+
+ config_unlock();
+
+ /* make alias table (for faster lookups) */
+ alias_make_table();
+}
+
+/* mount flash card read/write */
+function conf_mount_rw() {
+ global $g;
+
+ /* don't use mount -u anymore
+ (doesn't sync the files properly and /bin/sync won't help either) */
+ mwexec("/sbin/umount -f {$g['cf_path']}");
+ mwexec("/sbin/mount -w -o noatime {$g['cf_path']}");
+}
+
+/* mount flash card read only */
+function conf_mount_ro() {
+ global $g;
+
+ mwexec("/sbin/umount -f {$g['cf_path']}");
+ mwexec("/sbin/mount -r {$g['cf_path']}");
+}
+
+/* convert configuration, if necessary */
+function convert_config() {
+ global $config, $g;
+
+ if ($config['version'] == $g['latest_config'])
+ return; /* already at latest version */
+
+ if ($g['booting'])
+ echo "Converting configuration... ";
+
+ /* convert 1.0 -> 1.1 */
+ if ($config['version'] == "1.0") {
+ $opti = 1;
+ $ifmap = array('lan' => 'lan', 'wan' => 'wan', 'pptp' => 'pptp');
+
+ /* convert DMZ to optional, if necessary */
+ if (isset($config['interfaces']['dmz'])) {
+
+ $dmzcfg = &$config['interfaces']['dmz'];
+
+ if ($dmzcfg['if']) {
+ $config['interfaces']['opt' . $opti] = array();
+ $optcfg = &$config['interfaces']['opt' . $opti];
+
+ $optcfg['enable'] = $dmzcfg['enable'];
+ $optcfg['descr'] = "DMZ";
+ $optcfg['if'] = $dmzcfg['if'];
+ $optcfg['ipaddr'] = $dmzcfg['ipaddr'];
+ $optcfg['subnet'] = $dmzcfg['subnet'];
+
+ $ifmap['dmz'] = "opt" . $opti;
+ $opti++;
+ }
+
+ unset($config['interfaces']['dmz']);
+ }
+
+ /* convert WLAN1/2 to optional, if necessary */
+ for ($i = 1; isset($config['interfaces']['wlan' . $i]); $i++) {
+
+ if (!$config['interfaces']['wlan' . $i]['if']) {
+ unset($config['interfaces']['wlan' . $i]);
+ continue;
+ }
+
+ $wlancfg = &$config['interfaces']['wlan' . $i];
+ $config['interfaces']['opt' . $opti] = array();
+ $optcfg = &$config['interfaces']['opt' . $opti];
+
+ $optcfg['enable'] = $wlancfg['enable'];
+ $optcfg['descr'] = "WLAN" . $i;
+ $optcfg['if'] = $wlancfg['if'];
+ $optcfg['ipaddr'] = $wlancfg['ipaddr'];
+ $optcfg['subnet'] = $wlancfg['subnet'];
+ $optcfg['bridge'] = $wlancfg['bridge'];
+
+ $optcfg['wireless'] = array();
+ $optcfg['wireless']['mode'] = $wlancfg['mode'];
+ $optcfg['wireless']['ssid'] = $wlancfg['ssid'];
+ $optcfg['wireless']['channel'] = $wlancfg['channel'];
+ $optcfg['wireless']['wep'] = $wlancfg['wep'];
+
+ $ifmap['wlan' . $i] = "opt" . $opti;
+
+ unset($config['interfaces']['wlan' . $i]);
+ $opti++;
+ }
+
+ /* convert filter rules */
+ $n = count($config['filter']['rule']);
+ for ($i = 0; $i < $n; $i++) {
+
+ $fr = &$config['filter']['rule'][$i];
+
+ /* remap interface */
+ if (array_key_exists($fr['interface'], $ifmap))
+ $fr['interface'] = $ifmap[$fr['interface']];
+ else {
+ /* remove the rule */
+ echo "\nWarning: filter rule removed " .
+ "(interface '{$fr['interface']}' does not exist anymore).";
+ unset($config['filter']['rule'][$i]);
+ continue;
+ }
+
+ /* remap source network */
+ if (isset($fr['source']['network'])) {
+ if (array_key_exists($fr['source']['network'], $ifmap))
+ $fr['source']['network'] = $ifmap[$fr['source']['network']];
+ else {
+ /* remove the rule */
+ echo "\nWarning: filter rule removed " .
+ "(source network '{$fr['source']['network']}' does not exist anymore).";
+ unset($config['filter']['rule'][$i]);
+ continue;
+ }
+ }
+
+ /* remap destination network */
+ if (isset($fr['destination']['network'])) {
+ if (array_key_exists($fr['destination']['network'], $ifmap))
+ $fr['destination']['network'] = $ifmap[$fr['destination']['network']];
+ else {
+ /* remove the rule */
+ echo "\nWarning: filter rule removed " .
+ "(destination network '{$fr['destination']['network']}' does not exist anymore).";
+ unset($config['filter']['rule'][$i]);
+ continue;
+ }
+ }
+ }
+
+ /* convert shaper rules */
+ $n = count($config['pfqueueing']['rule']);
+ if (is_array($config['pfqueueing']['rule']))
+ for ($i = 0; $i < $n; $i++) {
+
+ $fr = &$config['pfqueueing']['rule'][$i];
+
+ /* remap interface */
+ if (array_key_exists($fr['interface'], $ifmap))
+ $fr['interface'] = $ifmap[$fr['interface']];
+ else {
+ /* remove the rule */
+ echo "\nWarning: traffic shaper rule removed " .
+ "(interface '{$fr['interface']}' does not exist anymore).";
+ unset($config['pfqueueing']['rule'][$i]);
+ continue;
+ }
+
+ /* remap source network */
+ if (isset($fr['source']['network'])) {
+ if (array_key_exists($fr['source']['network'], $ifmap))
+ $fr['source']['network'] = $ifmap[$fr['source']['network']];
+ else {
+ /* remove the rule */
+ echo "\nWarning: traffic shaper rule removed " .
+ "(source network '{$fr['source']['network']}' does not exist anymore).";
+ unset($config['pfqueueing']['rule'][$i]);
+ continue;
+ }
+ }
+
+ /* remap destination network */
+ if (isset($fr['destination']['network'])) {
+ if (array_key_exists($fr['destination']['network'], $ifmap))
+ $fr['destination']['network'] = $ifmap[$fr['destination']['network']];
+ else {
+ /* remove the rule */
+ echo "\nWarning: traffic shaper rule removed " .
+ "(destination network '{$fr['destination']['network']}' does not exist anymore).";
+ unset($config['pfqueueing']['rule'][$i]);
+ continue;
+ }
+ }
+ }
+
+ $config['version'] = "1.1";
+ }
+
+ /* convert 1.1 -> 1.2 */
+ if ($config['version'] == "1.1") {
+ /* move LAN DHCP server config */
+ $tmp = $config['dhcpd'];
+ $config['dhcpd'] = array();
+ $config['dhcpd']['lan'] = $tmp;
+
+ /* encrypt password */
+ $config['system']['password'] = crypt($config['system']['password']);
+
+ $config['version'] = "1.2";
+ }
+
+ /* convert 1.2 -> 1.3 */
+ if ($config['version'] == "1.2") {
+ /* convert advanced outbound NAT config */
+ for ($i = 0; isset($config['nat']['advancedoutbound']['rule'][$i]); $i++) {
+ $curent = &$config['nat']['advancedoutbound']['rule'][$i];
+ $src = $curent['source'];
+ $curent['source'] = array();
+ $curent['source']['network'] = $src;
+ $curent['destination'] = array();
+ $curent['destination']['any'] = true;
+ }
+
+ /* add an explicit type="pass" to all filter rules to make things consistent */
+ for ($i = 0; isset($config['filter']['rule'][$i]); $i++) {
+ $config['filter']['rule'][$i]['type'] = "pass";
+ }
+
+ $config['version'] = "1.3";
+ }
+
+ /* convert 1.3 -> 1.4 */
+ if ($config['version'] == "1.3") {
+ /* convert shaper rules (make pipes) */
+ if (is_array($config['pfqueueing']['rule'])) {
+ $config['pfqueueing']['pipe'] = array();
+
+ for ($i = 0; isset($config['pfqueueing']['rule'][$i]); $i++) {
+ $curent = &$config['pfqueueing']['rule'][$i];
+
+ /* make new pipe and associate with this rule */
+ $newpipe = array();
+ $newpipe['descr'] = $curent['descr'];
+ $newpipe['bandwidth'] = $curent['bandwidth'];
+ $newpipe['delay'] = $curent['delay'];
+ $newpipe['mask'] = $curent['mask'];
+ $config['pfqueueing']['pipe'][$i] = $newpipe;
+
+ $curent['targetpipe'] = $i;
+
+ unset($curent['bandwidth']);
+ unset($curent['delay']);
+ unset($curent['mask']);
+ }
+ }
+
+ $config['version'] = "1.4";
+ }
+
+ write_config();
+
+ if ($g['booting'])
+ echo "done\n";
+}
+
+/* save the system configuration */
+function write_config() {
+
+ global $config, $g;
+
+ config_lock();
+
+ conf_mount_rw();
+
+ if (time() > mktime(0, 0, 0, 9, 1, 2004)) /* make sure the clock settings is plausible */
+ $config['lastchange'] = time();
+
+ /* generate configuration XML */
+ $xmlconfig = dump_xml_config($config, $g['xml_rootobj']);
+
+ /* write configuration */
+ $fd = fopen("{$g['cf_conf_path']}/config.xml", "w");
+
+ if (!$fd)
+ die("Unable to open config.xml for writing in write_config()\n");
+
+ fwrite($fd, $xmlconfig);
+ fclose($fd);
+
+ conf_mount_ro();
+
+ /* re-read configuration */
+ $config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
+
+ /* write config cache */
+ $fd = @fopen("{$g['tmp_path']}/config.cache", "wb");
+ if ($fd) {
+ fwrite($fd, serialize($config));
+ fclose($fd);
+ }
+
+ config_unlock();
+}
+
+function reset_factory_defaults() {
+
+ global $g;
+
+ config_lock();
+
+ conf_mount_rw();
+
+ /* create conf directory, if necessary */
+ if (!file_exists("{$g['cf_conf_path']}"))
+ @mkdir("{$g['cf_conf_path']}");
+
+ /* clear out /conf */
+ $dh = opendir($g['conf_path']);
+ while ($filename = readdir($dh)) {
+ if (($filename != ".") && ($filename != "..")) {
+ unlink($g['conf_path'] . "/" . $filename);
+ }
+ }
+ closedir($dh);
+
+ /* copy default configuration */
+ @copy("{$g['conf_default_path']}/config.xml", "{$g['conf_path']}/config.xml");
+
+ conf_mount_ro();
+
+ config_unlock();
+
+ return 0;
+}
+
+function config_install($conffile) {
+
+ global $config, $g;
+
+ if (!file_exists($conffile))
+ return 1;
+
+ config_lock();
+ conf_mount_rw();
+
+ copy($conffile, "{$g['conf_path']}/config.xml");
+
+ conf_mount_ro();
+ config_unlock();
+
+ return 0;
+}
+
+/* lock configuration file, decide that the lock file is stale after
+ 10 seconds */
+function config_lock() {
+
+ global $g;
+
+ $lockfile = "{$g['varrun_path']}/config.lock";
+
+ $n = 0;
+ while ($n < 10) {
+ /* open the lock file in append mode to avoid race condition */
+ if ($fd = @fopen($lockfile, "x")) {
+ /* succeeded */
+ fclose($fd);
+ return;
+ } else {
+ /* file locked, wait and try again */
+ sleep(1);
+ $n++;
+ }
+ }
+}
+
+/* unlock configuration file */
+function config_unlock() {
+
+ global $g;
+
+ $lockfile = "{$g['varrun_path']}/config.lock";
+
+ if (file_exists($lockfile))
+ unlink($lockfile);
+}
+
+?>
diff --git a/etc/inc/filter.inc b/etc/inc/filter.inc
new file mode 100644
index 0000000..1b409f8
--- /dev/null
+++ b/etc/inc/filter.inc
@@ -0,0 +1,946 @@
+<?php
+/*
+ filter.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("functions.inc");
+
+function filter_resync() {
+ global $config, $g;
+
+ mwexec("/sbin/pfctl -y"); /* XXX */
+}
+
+function filter_ipmon_start() {
+ global $config, $g;
+
+ mwexec("/pflogd -sD");
+}
+
+function filter_configure() {
+ global $config, $g;
+
+ if ($g['booting'])
+ echo "Configuring firewall... ";
+
+ /* set TCP timeouts */
+ $tcpidletimeout = 9000;
+ if ($config['filter']['tcpidletimeout'])
+ $tcpidletimeout = $config['filter']['tcpidletimeout'];
+ mwexec("/sbin/sysctl net.inet.ipf.fr_tcpidletimeout={$tcpidletimeout}");
+ mwexec("/sbin/sysctl net.inet.ipf.fr_tcphalfclosed=480");
+
+ /* generate pfctl rules */
+ $natrules = filter_nat_rules_generate();
+ /* generate pfctl rules */
+ $pfrules = filter_rules_generate();
+ /* generate altq interface setup parms */
+ $altq_ints = filter_setup_altq_interfaces();
+ /* generate altq queues */
+ $altq_queues = filter_generate_altq_queues();
+
+ mwexec("/sbin/pfctl -e");
+ mwexec("/sbin/pfctl -F nat");
+ mwexec("/sbin/pfctl -F rules");
+
+ /* get our wan interface? */
+ $wanif = get_real_wan_interface();
+
+ $fd = fopen("/tmp/rules.debug", "w");
+ fwrite($fd, "set loginterface $wanif \n");
+ fwrite($fd, "set optimization aggressive\n");
+ fwrite($fd, $altq_ints);
+ fwrite($fd, $altq_queues);
+ fwrite($fd, $natrules);
+ fwrite($fd, $pfrules);
+ fclose($fd);
+
+ mwexec("chmod a+x /tmp/rules.debug");
+ mwexec("/sbin/pfctl -f /tmp/rules.debug");
+
+ /* set up MSS clamping */
+ if ($config['interfaces']['wan']['mtu'])
+ $mssclamp = $config['interfaces']['wan']['mtu'] - 40;
+ else if ($config['interfaces']['wan']['ipaddr'] == "pppoe")
+ $mssclamp = 1452;
+ else
+ $mssclamp = 0;
+
+ mwexec("/sbin/sysctl net.inet.ipf.fr_mssif={$wanif}");
+ mwexec("/sbin/sysctl net.inet.ipf.fr_mssclamp={$mssclamp}");
+
+ if ($g['booting'])
+ echo "done\n";
+
+ return 0;
+}
+
+function filter_generate_altq_queues() {
+ global $config;
+ $altq_rules = "";
+ if (is_array($config['pfqueueing']['queue'])) {
+ foreach ($config['pfqueueing']['queue'] as $rule) {
+ $altq_rules .= "queue " . $rule['name'] . " ";
+ if (isset($rule['bandwidth']))
+ $altq_rules .= "bandwidth " . $rule['bandwidth'] . " ";
+ if (isset($rule['priority']))
+ $altq_rules .= "priority " . $rule['priority'] . " ";
+ if (isset($rule['options'])) /* XXX turn options into an xml array */
+ $altq_rules .= $rule['schedulertype'] . "(". $rule['options'] . ")";
+ if (isset($rule['subqueue'])) {
+ $altq_rules .= "{ ";
+ $fsq = "";
+ foreach ($rule['subqueue'] as $sq) {
+ if($fsq) $altq_rules .= ",";
+ $altq_rules .= $sq['name'];
+ $fsq = "1";
+ }
+ $altq_rules .= " }";
+ }
+ $altq_rules .= "\n";
+ }
+ }
+ return $altq_rules;
+}
+
+function filter_setup_altq_interfaces() {
+ global $config;
+ $altq_rules = "";
+ $queue_names = "";
+ $is_first = "";
+ if (is_array($config['pfqueueing']['queue'])) {
+ foreach ($config['pfqueueing']['queue'] as $queue) {
+ if(is_subqueue($queue['name']) == 0) {
+ if($is_first) $queue_names .= ", ";
+ $queue_names .= $queue['name'];
+ $is_first = "1";
+ }
+ }
+ }
+ if (is_array($config['interfaces'])) {
+ foreach ($config['interfaces'] as $ifname) {
+ if(isset($ifname['bandwidth'])) {
+ $subnet = $ifname['ipaddr'] . "/" . $ifname['subnet'];
+ $altq_rules .= "altq on " . $ifname['if'] . " ";
+ $altq_rules .= $ifname['schedulertype'] . " bandwidth " . $ifname['bandwidth'] . " ";
+ if($queue_names <> "")
+ $altq_rules .= "queue { " . $queue_names . " }";
+ $altq_rules .= "\n";
+ }
+ }
+ }
+ return $altq_rules;
+}
+
+function is_subqueue($name) {
+ global $config;
+ $status = "";
+ if (is_array($config['pfqueueing']['queue'])) {
+ foreach ($config['pfqueueing']['queue'] as $queue) {
+ if(is_array($queue['subqueue'])) {
+ foreach ($queue['subqueue'] as $sq) {
+ if($sq['name'] == $name) return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+function filter_flush_nat_table() {
+ global $config, $g;
+
+ return mwexec("/sbin/pfctl -F nat");
+}
+
+function filter_flush_state_table() {
+ global $config, $g;
+
+ return mwexec("/sbin/pfctl -F state");
+}
+
+function filter_nat_rules_generate_if($if, $src, $dst, $target) {
+
+ if ($target)
+ $tgt = $target . "/32";
+ else
+ $tgt = "0/32";
+
+ $natrule = <<<EOD
+nat on $if from $src to any -> $if
+
+EOD;
+
+ return $natrule;
+}
+
+function filter_nat_rules_generate() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+ $lancfg = $config['interfaces']['lan'];
+
+ $pptpdcfg = $config['pptpd'];
+ $wanif = get_real_wan_interface();
+
+ $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']);
+
+ $natrules = "";
+
+ /* any 1:1 mappings? */
+ if (is_array($config['nat']['onetoone'])) {
+ foreach ($config['nat']['onetoone'] as $natent) {
+ if (!is_numeric($natent['subnet']))
+ $sn = 32;
+ else
+ $sn = $natent['subnet'];
+
+ if (!$natent['interface'] || ($natent['interface'] == "wan"))
+ $natif = $wanif;
+ else
+ $natif = $config['interfaces'][$natent['interface']]['if'];
+
+ $natrules .= "binat on {$natif} from {$natent['internal']}/{$sn} to any -> {$natent['external']}\n";
+ }
+ }
+
+ /* outbound rules - advanced or standard */
+ if (isset($config['nat']['advancedoutbound']['enable'])) {
+ /* advanced outbound rules */
+ if (is_array($config['nat']['advancedoutbound']['rule'])) {
+ foreach ($config['nat']['advancedoutbound']['rule'] as $obent) {
+ $dst = "";
+ $src = "";
+ if (!isset($obent['destination']['any'])) {
+ $src = "from ";
+ if (isset($obent['destination']['not']))
+ $dst = "! to ";
+ else
+ $dst = "to ";
+ $dst .= $obent['destination']['network'];
+ }
+ $src .= $obent['source']['network'];
+
+ if (!$obent['interface'] || ($obent['interface'] == "wan"))
+ $natif = $wanif;
+ else
+ $natif = $config['interfaces'][$obent['interface']]['if'];
+
+ $natrules .= filter_nat_rules_generate_if($natif, $src, $dst,
+ $obent['target']);
+ }
+ }
+ } else {
+ /* standard outbound rules (one for each interface) */
+ $natrules .= filter_nat_rules_generate_if($wanif,
+ $lansa . "/" . $lancfg['subnet'], "", null);
+
+ /* optional interfaces */
+ for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
+ $optcfg = $config['interfaces']['opt' . $i];
+
+ if (isset($optcfg['enable']) && !$optcfg['bridge']) {
+ $optsa = gen_subnet($optcfg['ipaddr'], $optcfg['subnet']);
+ $natrules .= filter_nat_rules_generate_if($wanif,
+ $optsa . "/" . $optcfg['subnet'], "", null);
+ }
+ }
+
+ /* PPTP subnet */
+ if ($pptpdcfg['mode'] == "server") {
+ $natrules .= filter_nat_rules_generate_if($wanif,
+ $pptpdcfg['remoteip'] . "/" . $g['pptp_subnet'], "", null);
+ }
+
+ /* static routes */
+ if (is_array($config['staticroutes']['route'])) {
+ foreach ($config['staticroutes']['route'] as $route) {
+ if ($route['interface'] != "wan")
+ $natrules .= filter_nat_rules_generate_if($wanif,
+ $route['network'], "", null);
+ }
+ }
+ }
+
+ /* DIAG: add ipv6 NAT, if requested */
+ if (isset($config['diag']['ipv6nat']['enable'])) {
+ $natrules .= "rdr on $wanif proto ipv6 from any to any port 0 -> " .
+ "{$config['diag']['ipv6nat']['ipaddr']}\n";
+ }
+
+ if (isset($config['nat']['rule'])) {
+ foreach ($config['nat']['rule'] as $rule) {
+
+ $extport = explode("-", $rule['external-port']);
+ $target = alias_expand_host($rule['target']);
+
+ if (!$target)
+ continue; /* unresolvable alias */
+
+ if ($rule['external-address'])
+ $extaddr = $rule['external-address'] . "/32";
+ else
+ $extaddr = "0/0";
+
+ if (!$rule['interface'] || ($rule['interface'] == "wan"))
+ $natif = $wanif;
+ else
+ $natif = $config['interfaces'][$rule['interface']]['if'];
+
+ $lanif = $lancfg['if'];
+
+ if ((!$extport[1]) || ($extport[0] == $extport[1])) {
+ $natrules .=
+ "rdr on $natif proto " . $rule['protocol'] . " from any to any port {$extport[0]} -> {$target} \n";
+ } else {
+ $natrules .=
+ "rdr on $natif proto " . $rule['protocol']. " from any to any port {$extport[0]}:{$extport[1]} " .
+ "-> {$target} \n";
+ }
+
+ $natrules .= "\n";
+ }
+ }
+
+ if ($pptpdcfg['mode'] && $pptpdcfg['mode'] != "off") {
+
+ if ($pptpdcfg['mode'] == "server")
+ $pptpdtarget = "127.0.0.1";
+ else if ($pptpdcfg['mode'] == "redir")
+ $pptpdtarget = $pptpdcfg['redir'];
+
+ if ($pptpdtarget) {
+
+ $natrules .= <<<EOD
+
+# PPTP
+rdr on $wanif proto gre from any to any port 0 -> $pptpdtarget
+rdr on $wanif proto tcp from any to any port 1723 -> $pptpdtarget
+
+EOD;
+ }
+ }
+
+ return $natrules;
+}
+
+function filter_rules_generate() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+ $lancfg = $config['interfaces']['lan'];
+ $pptpdcfg = $config['pptpd'];
+
+ $lanif = $lancfg['if'];
+ $wanif = get_real_wan_interface();
+
+ /* rule groups (optional interfaces: see below) */
+ $ifgroups = array("lan" => 100, "wan" => 200);
+
+ $lanip = $lancfg['ipaddr'];
+ $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']);
+ $lansn = $lancfg['subnet'];
+
+ /* optional interfaces */
+ $optcfg = array();
+
+ for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
+ $oc = $config['interfaces']['opt' . $i];
+
+ if (isset($oc['enable']) && $oc['if']) {
+ $oic = array();
+ $oic['if'] = $oc['if'];
+
+ if ($oc['bridge']) {
+ if (!strstr($oc['bridge'], "opt") ||
+ isset($config['interfaces'][$oc['bridge']]['enable'])) {
+ if (is_ipaddr($config['interfaces'][$oc['bridge']]['ipaddr'])) {
+ $oic['ip'] = $config['interfaces'][$oc['bridge']]['ipaddr'];
+ $oic['sn'] = $config['interfaces'][$oc['bridge']]['subnet'];
+ $oic['sa'] = gen_subnet($oic['ip'], $oic['sn']);
+ }
+ }
+ $oic['bridge'] = 1;
+ } else {
+ $oic['ip'] = $oc['ipaddr'];
+ $oic['sn'] = $oc['subnet'];
+ $oic['sa'] = gen_subnet($oic['ip'], $oic['sn']);
+ }
+
+ $optcfg['opt' . $i] = $oic;
+ $ifgroups['opt' . $i] = ($i * 100) + 200;
+ }
+ }
+
+ if ($pptpdcfg['mode'] == "server") {
+ $pptpip = $pptpdcfg['localip'];
+ $pptpsa = $pptpdcfg['remoteip'];
+ $pptpsn = $g['pptp_subnet'];
+ }
+
+ /* default block logging? */
+ if (!isset($config['syslog']['nologdefaultblock']))
+ $log = "log";
+ else
+ $log = "";
+
+ $ipfrules = <<<EOD
+
+# loopback
+pass in quick on lo0 all
+pass out quick on lo0 all
+
+# allow access to DHCP server on LAN
+pass in quick on $lanif proto udp from any port = 68 to 255.255.255.255 port = 67
+pass in quick on $lanif proto udp from any port = 68 to $lanip port = 67
+pass out quick on $lanif proto udp from $lanip port = 67 to any port = 68
+
+EOD;
+
+ /* allow access to DHCP server on optional interfaces */
+ foreach ($optcfg as $on => $oc) {
+ if (isset($config['dhcpd'][$on]['enable']) && (!$oc['bridge'])) {
+ $ipfrules .= <<<EOD
+
+# allow access to DHCP server on {$on}
+pass in quick on {$oc['if']} proto udp from any port = 68 to 255.255.255.255 port = 67
+pass in quick on {$oc['if']} proto udp from any port = 68 to {$oc['ip']} port = 67
+pass out quick on {$oc['if']} proto udp from {$oc['ip']} port = 67 to any port = 68
+
+EOD;
+ }
+ }
+
+ /* pass traffic between statically routed subnets and the subnet on the
+ interface in question to avoid problems with complicated routing
+ topologies */
+ if (is_array($config['staticroutes']['route']) && count($config['staticroutes']['route'])) {
+ foreach ($config['staticroutes']['route'] as $route) {
+ unset($sa);
+
+ if ($route['interface'] == "lan") {
+ $sa = $lansa;
+ $sn = $lansn;
+ $if = $lanif;
+ } else if (strstr($route['interface'], "opt")) {
+ $oc = $optcfg[$route['interface']];
+ if ($oc['ip']) {
+ $sa = $oc['sa'];
+ $sn = $oc['sn'];
+ $if = $oc['if'];
+ }
+ }
+
+ if ($sa) {
+ $ipfrules .= <<<EOD
+pass in quick on {$if} from {$sa}/{$sn} to {$route['network']}
+pass in quick on {$if} from {$route['network']} to {$sa}/{$sn}
+pass out quick on {$if} from {$sa}/{$sn} to {$route['network']}
+pass out quick on {$if} from {$route['network']} to {$sa}/{$sn}
+
+EOD;
+ }
+ }
+ }
+
+ $ipfrules .= <<<EOD
+
+# WAN spoof check
+block in $log quick on $wanif from $lansa/$lansn to any
+
+EOD;
+
+ foreach ($optcfg as $oc) {
+ if (!$oc['bridge'])
+ $ipfrules .= "block in $log quick on $wanif from {$oc['sa']}/{$oc['sn']} to any\n";
+ }
+
+ /* allow PPTP traffic if PPTP client is enabled on WAN */
+ if ($wancfg['ipaddr'] == "pptp") {
+ $ipfrules .= <<<EOD
+
+# allow PPTP client
+pass in quick on {$wancfg['if']} proto gre from any to any
+pass out quick on {$wancfg['if']} proto gre from any to any
+pass in quick on {$wancfg['if']} proto tcp from any port = 1723 to any
+pass out quick on {$wancfg['if']} proto tcp from any to any port = 1723
+
+EOD;
+ }
+
+ $ipfrules .= <<<EOD
+
+# allow our DHCP client out to the WAN
+# XXX - should be more restrictive
+# (not possible at the moment - need 'me' like in ipfw)
+pass out quick on $wanif proto udp from any port = 68 to any port = 67
+block in $log quick on $wanif proto udp from any port = 67 to $lansa/$lansn port = 68
+pass in quick on $wanif proto udp from any port = 67 to any port = 68
+
+# LAN/OPT spoof check (needs to be after DHCP because of broadcast addresses)
+
+EOD;
+
+ /* LAN spoof check */
+ $ipfrules .= filter_rules_spoofcheck_generate('lan', $lanif, $lansa, $lansn, $log);
+
+ /* OPT spoof check */
+ foreach ($optcfg as $on => $oc) {
+ if ($oc['ip'])
+ $ipfrules .= filter_rules_spoofcheck_generate($on, $oc['if'], $oc['sa'], $oc['sn'], $log);
+ }
+
+ /* block private networks on WAN? */
+ if (isset($config['interfaces']['wan']['blockpriv'])) {
+ $ipfrules .= <<<EOD
+
+# block anything from private networks on WAN interface
+block in $log quick on $wanif from 10.0.0.0/8 to any
+block in $log quick on $wanif from 127.0.0.0/8 to any
+block in $log quick on $wanif from 172.16.0.0/12 to any
+block in $log quick on $wanif from 192.168.0.0/16 to any
+
+EOD;
+ }
+
+ /* IPsec enabled? */
+ if (isset($config['ipsec']['enable']) &&
+ ((is_array($config['ipsec']['tunnel']) &&
+ count($config['ipsec']['tunnel'])) ||
+ isset($config['ipsec']['mobileclients']['enable']))) {
+
+ $curwanip = get_current_wan_address();
+
+ if ($curwanip)
+ $ipfrules .= filter_rules_ipsec_generate($wanif, $curwanip);
+
+ $ipfrules .= filter_rules_ipsec_generate($lanif, $lanip);
+
+ foreach ($optcfg as $on => $oc) {
+ if ($oc['ip'])
+ $ipfrules .= filter_rules_ipsec_generate($oc['if'], $oc['ip']);
+ }
+ }
+
+ /* XXX - the first section is only needed because pfctl refuses to
+ parse rules that have "flags S/SAFR" and proto "tcp/udp" set because
+ UDP does not have flags, but we still want to offer the TCP/UDP protocol
+ option to the user */
+
+ $ipfrules .= <<<EOD
+
+
+# let out anything from the firewall host itself and decrypted IPsec traffic
+pass out quick on $wanif all keep state
+
+EOD;
+
+ /* group heads for optional interfaces */
+ foreach ($optcfg as $on => $oc) {
+
+ $ingroup = $ifgroups[$on];
+
+ $ipfrules .= <<<EOD
+
+
+# let out anything from the firewall host itself and decrypted IPsec traffic
+pass out quick on {$oc['if']} all keep state
+
+EOD;
+
+ }
+
+ if (!isset($config['system']['webgui']['noantilockout'])) {
+
+ $ipfrules .= <<<EOD
+
+# make sure the user cannot lock himself out of the webGUI
+pass in quick from $lansa/$lansn to $lanip keep state
+# group 100
+
+EOD;
+ }
+
+ /* PPTPd enabled? */
+ if ($pptpdcfg['mode'] && ($pptpdcfg['mode'] != "off")) {
+
+ if ($pptpdcfg['mode'] == "server")
+ $pptpdtarget = "127.0.0.1";
+ else
+ $pptpdtarget = $pptpdcfg['redir'];
+
+ $ipfrules .= <<<EOD
+
+# PPTP rules
+pass in quick proto gre from any to $pptpdtarget keep state
+# group 200
+pass in quick proto tcp from any to $pptpdtarget port = 1723 keep state
+# group 200
+
+EOD;
+ }
+
+ /* BigPond client enabled? */
+ if ($wancfg['ipaddr'] == "bigpond") {
+
+ $ipfrules .= <<<EOD
+
+# BigPond heartbeat rules
+pass in quick proto udp from any to any port = 5050 keep state
+# group 200
+
+EOD;
+ }
+
+ $i = 0;
+
+ $ipfrules .= "\n# User-defined rules follow\n";
+
+ if (isset($config['filter']['rule']))
+ foreach ($config['filter']['rule'] as $rule) {
+
+ /* don't include disabled rules */
+ if (isset($rule['disabled'])) {
+ $i++;
+ continue;
+ }
+
+ /* does the rule deal with a PPTP interface? */
+ if ($rule['interface'] == "pptp") {
+
+ if ($pptpdcfg['mode'] != "server") {
+ $i++;
+ continue;
+ }
+
+ $nif = $g['n_pptp_units'];
+ $ispptp = true;
+ } else {
+
+ if (strstr($rule['interface'], "opt")) {
+ if (!array_key_exists($rule['interface'], $optcfg)) {
+ $i++;
+ continue;
+ }
+ }
+
+ $nif = 1;
+ $ispptp = false;
+ }
+
+ if ($pptpdcfg['mode'] != "server") {
+ if (($rule['source']['network'] == "pptp") ||
+ ($rule['destination']['network'] == "pptp")) {
+ $i++;
+ continue;
+ }
+ }
+
+ if ($rule['source']['network'] && strstr($rule['source']['network'], "opt")) {
+ if (!array_key_exists($rule['source']['network'], $optcfg)) {
+ $i++;
+ continue;
+ }
+ }
+ if ($rule['destination']['network'] && strstr($rule['destination']['network'], "opt")) {
+ if (!array_key_exists($rule['destination']['network'], $optcfg)) {
+ $i++;
+ continue;
+ }
+ }
+
+ /* check for unresolvable aliases */
+ if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) {
+ $i++;
+ continue;
+ }
+ if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) {
+ $i++;
+ continue;
+ }
+
+ for ($iif = 0; $iif < $nif; $iif++) {
+
+ if (!$ispptp) {
+
+ $groupnum = $ifgroups[$rule['interface']];
+
+ if (!$groupnum) {
+ printf("Invalid interface name in rule $i\n");
+ break;
+ }
+ }
+
+ $type = $rule['type'];
+ if ($type != "pass" && $type != "block" && $type != "reject") {
+ /* default (for older rules) is pass */
+ $type = "pass";
+ }
+
+ if ($type == "reject") {
+ /* special reject packet */
+ if ($rule['protocol'] == "tcp") {
+ $line = "block return-rst";
+ } else if ($rule['protocol'] == "udp") {
+ $line = "block return-icmp";
+ } else {
+ $line = "block";
+ }
+ } else {
+ $line = $type;
+ }
+
+ if(!isset($rule['direction'])) {
+ $line .= " in ";
+ } else {
+ $line .= " " . $rule['direction'] . " ";
+ }
+
+ if (isset($rule['log']))
+ $line .= "log ";
+
+ $line .= "quick ";
+
+ if ($ispptp) {
+ $line .= "on ng" . ($iif+1) . " ";
+ }
+
+ if (isset($rule['protocol'])) {
+ $line .= "proto {$rule['protocol']} ";
+ }
+
+ /* source address */
+ if (isset($rule['source']['any'])) {
+ $src = "any";
+ } else if ($rule['source']['network']) {
+
+ if (strstr($rule['source']['network'], "opt")) {
+ $src = $optcfg[$rule['source']['network']]['sa'] . "/" .
+ $optcfg[$rule['source']['network']]['sn'];
+ } else {
+ switch ($rule['source']['network']) {
+ case 'lan':
+ $src = "$lansa/$lansn";
+ break;
+ case 'pptp':
+ $src = "$pptpsa/$pptpsn";
+ break;
+ }
+ }
+ } else if ($rule['source']['address']) {
+ $src = alias_expand($rule['source']['address']);
+ }
+
+ if (!$src || ($src == "/")) {
+ //printf("No source address found in rule $i\n");
+ break;
+ }
+
+ if (isset($rule['source']['not'])) {
+ $line .= "from !$src ";
+ } else {
+ $line .= "from $src ";
+ }
+
+ if (in_array($rule['protocol'], array("tcp","udp","tcp/udp"))) {
+
+ if ($rule['source']['port']) {
+ $srcport = explode("-", $rule['source']['port']);
+
+ if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) {
+ $line .= "port = {$srcport[0]} ";
+ } else if (($srcport[0] == 1) && ($srcport[1] == 65535)) {
+ /* no need for a port statement here */
+ } else if ($srcport[1] == 65535) {
+ $line .= "port >= {$srcport[0]} ";
+ } else if ($srcport[0] == 1) {
+ $line .= "port <= {$srcport[1]} ";
+ } else {
+ $srcport[0]--;
+ $srcport[1]++;
+ $line .= "port {$srcport[0]} >< {$srcport[1]} ";
+ }
+ }
+ }
+
+ /* destination address */
+ if (isset($rule['destination']['any'])) {
+ $dst = "any";
+ } else if ($rule['destination']['network']) {
+
+ if (strstr($rule['destination']['network'], "opt")) {
+ $dst = $optcfg[$rule['destination']['network']]['sa'] . "/" .
+ $optcfg[$rule['destination']['network']]['sn'];
+ } else {
+ switch ($rule['destination']['network']) {
+ case 'lan':
+ $dst = "$lansa/$lansn";
+ break;
+ case 'pptp':
+ $dst = "$pptpsa/$pptpsn";
+ break;
+ }
+ }
+ } else if ($rule['destination']['address']) {
+ $dst = alias_expand($rule['destination']['address']);
+ }
+
+ if (!$dst || ($dst == "/")) {
+ //printf("No destination address found in rule $i\n");
+ break;
+ }
+
+ if (isset($rule['destination']['not'])) {
+ $line .= "to !$dst ";
+ } else {
+ $line .= "to $dst ";
+ }
+
+ if (in_array($rule['protocol'], array("tcp","udp","tcp/udp"))) {
+
+ if ($rule['destination']['port']) {
+ $dstport = explode("-", $rule['destination']['port']);
+
+ if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) {
+ $line .= "port = {$dstport[0]} ";
+ } else if (($dstport[0] == 1) && ($dstport[1] == 65535)) {
+ /* no need for a port statement here */
+ } else if ($dstport[1] == 65535) {
+ $line .= "port >= {$dstport[0]} ";
+ } else if ($dstport[0] == 1) {
+ $line .= "port <= {$dstport[1]} ";
+ } else {
+ $dstport[0]--;
+ $dstport[1]++;
+ $line .= "port {$dstport[0]} >< {$dstport[1]} ";
+ }
+ }
+ }
+
+ if (($rule['protocol'] == "icmp") && $rule['icmptype']) {
+ $line .= "icmp-type {$rule['icmptype']} ";
+ }
+
+ if ($type == "pass") {
+ $line .= "keep state ";
+
+ if (isset($rule['frags']))
+ $line .= "keep frags ";
+ }
+
+ if ($type == "reject" && $rule['protocol'] == "tcp") {
+ /* special reject packet */
+ $line .= "flags S/SA ";
+ }
+
+ if (isset($rule['flags'])) {
+ $line .= "flags " . $rule['flags'] . " ";
+ }
+
+ if (!$ispptp) {
+ #$line .= "group $groupnum ";
+ }
+
+ if (isset($rule['queuename'])) {
+ $line .= "queue " . $rule['queuename'];
+ }
+
+ $line .= "\n";
+
+ $ipfrules .= $line;
+ }
+
+ $i++;
+ }
+
+ $ipfrules .= <<<EOD
+
+#---------------------------------------------------------------------------
+# default rules (just to be sure)
+#---------------------------------------------------------------------------
+block in $log quick all
+block out $log quick all
+
+EOD;
+
+ return $ipfrules;
+}
+
+function filter_rules_spoofcheck_generate($ifname, $if, $sa, $sn, $log) {
+
+ global $g, $config;
+
+ $ipfrules = "";
+
+ if (is_array($config['staticroutes']['route']) && count($config['staticroutes']['route'])) {
+ /* count rules */
+ $n = 1;
+ foreach ($config['staticroutes']['route'] as $route) {
+ if ($route['interface'] == $ifname)
+ $n++;
+ }
+
+ /* output skip rules */
+ foreach ($config['staticroutes']['route'] as $route) {
+ if ($route['interface'] == $ifname) {
+ $ipfrules .= "skip $n in on $if from {$route['network']} to any\n";
+ $n--;
+ }
+ }
+ $ipfrules .= "skip 1 in on $if from $sa/$sn to any\n";
+ $ipfrules .= "#block in $log quick on $if all\n";
+ } else {
+ $ipfrules .= "#block in $log quick on $if from ! $sa/$sn to any\n";
+ }
+
+ return $ipfrules;
+}
+
+function filter_rules_ipsec_generate($ifname, $ip) {
+
+ $ipfrules = <<<EOD
+
+# Pass IKE packets
+pass in quick on {$ifname} proto udp from any to {$ip} port = 500
+pass out quick on {$ifname} proto udp from {$ip} port = 500 to any
+
+# Pass ESP packets
+pass in quick on {$ifname} proto esp from any to {$ip}
+pass out quick on {$ifname} proto esp from {$ip} to any
+
+# Pass AH packets
+pass in quick on {$ifname} proto ah from any to {$ip}
+pass out quick on {$ifname} proto ah from {$ip} to any
+
+EOD;
+
+ return $ipfrules;
+}
+
+?>
diff --git a/etc/inc/functions.inc b/etc/inc/functions.inc
new file mode 100644
index 0000000..eab4b82
--- /dev/null
+++ b/etc/inc/functions.inc
@@ -0,0 +1,41 @@
+<?php
+/*
+ functions.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("system.inc");
+require_once("interfaces.inc");
+require_once("services.inc");
+require_once("filter.inc");
+require_once("shaper.inc");
+require_once("vpn.inc");
+require_once("captiveportal.inc");
+require_once("openvpn.inc");
+
+?>
diff --git a/etc/inc/globals.inc b/etc/inc/globals.inc
new file mode 100644
index 0000000..eef6cff
--- /dev/null
+++ b/etc/inc/globals.inc
@@ -0,0 +1,54 @@
+<?php
+/*
+ globals.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+$g = array(
+ "varrun_path" => "/var/run",
+ "varetc_path" => "/var/etc",
+ "vardb_path" => "/var/db",
+ "varlog_path" => "/var/log",
+ "etc_path" => "/etc",
+ "tmp_path" => "/tmp",
+ "conf_path" => "/conf",
+ "ftmp_path" => "/ftmp",
+ "conf_default_path" => "/conf.default",
+ "cf_path" => "/cf",
+ "cf_conf_path" => "/cf/conf",
+ "www_path" => "/usr/local/www",
+ "captiveportal_path" => "/usr/local/captiveportal",
+ "xml_rootobj" => "m0n0wall",
+ "pppoe_interface" => "ng0",
+ "n_pptp_units" => 16,
+ "pptp_subnet" => 28,
+ "debug" => false,
+ "latest_config" => "1.4",
+ "nopccard_platforms" => array("wrap", "net48xx")
+);
+
+?>
diff --git a/etc/inc/interfaces.inc b/etc/inc/interfaces.inc
new file mode 100644
index 0000000..00331c1
--- /dev/null
+++ b/etc/inc/interfaces.inc
@@ -0,0 +1,740 @@
+<?php
+/*
+ interfaces.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("functions.inc");
+
+function interfaces_loopback_configure() {
+ global $config, $g;
+
+ mwexec("/sbin/ifconfig lo0 127.0.0.1");
+
+ return 0;
+}
+
+function interfaces_vlan_configure() {
+ global $config, $g;
+
+ if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) {
+
+ /* load the VLAN module */
+ mwexec("/sbin/kldload if_vlan");
+
+ /* devices with native VLAN support */
+ $vlan_native_supp = explode(" ", "bge em gx nge ti txp");
+
+ /* devices with long frame support */
+ $vlan_long_supp = explode(" ", "dc fxp sis ste tl tx xl");
+
+ $i = 0;
+
+ foreach ($config['vlans']['vlan'] as $vlan) {
+
+ $cmd = "/sbin/ifconfig vlan{$i} create vlan " .
+ escapeshellarg($vlan['tag']) . " vlandev " .
+ escapeshellarg($vlan['if']);
+
+ /* get driver name */
+ for ($j = 0; $j < strlen($vlan['if']); $j++) {
+ if ($vlan['if'][$j] >= '0' && $vlan['if'][$j] <= '9')
+ break;
+ }
+ $drvname = substr($vlan['if'], 0, $j);
+
+ if (in_array($drvname, $vlan_native_supp))
+ $cmd .= " link0";
+ else if (in_array($drvname, $vlan_long_supp))
+ $cmd .= " mtu 1500";
+
+ mwexec($cmd);
+
+ /* make sure the parent interface is up */
+ mwexec("/sbin/ifconfig " . escapeshellarg($vlan['if']) . " up");
+
+ $i++;
+ }
+ }
+
+ return 0;
+}
+
+function interfaces_lan_configure() {
+ global $config, $g;
+
+ if ($g['booting'])
+ echo "Configuring LAN interface... ";
+
+ $lancfg = $config['interfaces']['lan'];
+
+ /* wireless configuration? */
+ if (is_array($lancfg['wireless']))
+ interfaces_wireless_configure($lancfg['if'], $lancfg['wireless']);
+
+ /* MAC spoofing? */
+ if ($lancfg['spoofmac'])
+ mwexec("/sbin/ifconfig " . escapeshellarg($lancfg['if']) .
+ " link " . escapeshellarg($lancfg['spoofmac']));
+
+ /* media */
+ if ($lancfg['media'] || $lancfg['mediaopt']) {
+ $cmd = "/sbin/ifconfig " . escapeshellarg($lancfg['if']);
+ if ($lancfg['media'])
+ $cmd .= " media " . escapeshellarg($lancfg['media']);
+ if ($lancfg['mediaopt'])
+ $cmd .= " mediaopt " . escapeshellarg($lancfg['mediaopt']);
+ mwexec($cmd);
+ }
+
+ mwexec("/sbin/ifconfig " . escapeshellarg($lancfg['if']) . " " .
+ escapeshellarg($lancfg['ipaddr'] . "/" . $lancfg['subnet']));
+
+ if (!$g['booting']) {
+ /* make new hosts file */
+ system_hosts_generate();
+
+ /* reconfigure static routes (kernel may have deleted them) */
+ system_routing_configure();
+
+ /* reload ipfilter (address may have changed) */
+ filter_configure();
+
+ /* reload shaper (subnet may have changed) */
+ shaper_configure();
+
+ /* reload IPsec tunnels */
+ vpn_ipsec_configure();
+
+ /* reload dhcpd (gateway may have changed) */
+ services_dhcpd_configure();
+
+ /* reload dnsmasq */
+ services_dnsmasq_configure();
+
+ /* reload webgui */
+ system_webgui_start();
+
+ /* reload captive portal */
+ captiveportal_configure();
+ }
+
+ if ($g['booting'])
+ echo "done\n";
+
+ return 0;
+}
+
+function interfaces_optional_configure() {
+ global $config, $g;
+ global $bridgeconfig;
+
+ /* Reset bridge configuration. Interfaces will add to it. */
+ $bridgeconfig = "";
+
+ for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
+ interfaces_optional_configure_if($i);
+ }
+
+ if ($bridgeconfig) {
+ /* Set the system bridge configuration and enable bridging. */
+ mwexec("/sbin/sysctl net.link.ether.bridge_cfg=" . $bridgeconfig);
+
+ if (isset($config['bridge']['filteringbridge']))
+ mwexec("/sbin/sysctl net.link.ether.bridge_ipf=1");
+
+ mwexec("/sbin/sysctl net.link.ether.bridge=1");
+ } else {
+ mwexec("/sbin/sysctl net.link.ether.bridge_ipf=0");
+ mwexec("/sbin/sysctl net.link.ether.bridge=0");
+ }
+
+ if (!$g['booting']) {
+ /* reconfigure static routes (kernel may have deleted them) */
+ system_routing_configure();
+
+ /* reload ipfilter (address may have changed) */
+ filter_configure();
+
+ /* reload shaper (address may have changed) */
+ shaper_configure();
+
+ /* reload IPsec tunnels */
+ vpn_ipsec_configure();
+
+ /* reload dhcpd (interface enabled/disabled/bridged status may have changed) */
+ services_dhcpd_configure();
+
+ /* restart dnsmasq */
+ services_dnsmasq_configure();
+ }
+
+ return 0;
+}
+
+function interfaces_optional_configure_if($opti) {
+ global $config, $g;
+ global $bridgeconfig;
+
+ $optcfg = $config['interfaces']['opt' . $opti];
+
+ if ($g['booting']) {
+ $optdescr = "";
+ if ($optcfg['descr'])
+ $optdescr = " ({$optcfg['descr']})";
+ echo "Configuring OPT{$opti}{$optdescr} interface... ";
+ }
+
+ if (isset($optcfg['enable'])) {
+ /* wireless configuration? */
+ if (is_array($optcfg['wireless']))
+ interfaces_wireless_configure($optcfg['if'], $optcfg['wireless']);
+
+ /* MAC spoofing? */
+ if ($optcfg['spoofmac'])
+ mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) .
+ " link " . escapeshellarg($optcfg['spoofmac']));
+
+ /* media */
+ if ($optcfg['media'] || $optcfg['mediaopt']) {
+ $cmd = "/sbin/ifconfig " . escapeshellarg($optcfg['if']);
+ if ($optcfg['media'])
+ $cmd .= " media " . escapeshellarg($optcfg['media']);
+ if ($optcfg['mediaopt'])
+ $cmd .= " mediaopt " . escapeshellarg($optcfg['mediaopt']);
+ mwexec($cmd);
+ }
+
+ /* OpenVPN configuration? */
+ if (isset($optcfg['ovpn'])) {
+ if (strstr($if, "tap"))
+ ovpn_link_tap();
+ }
+
+ /* bridged? */
+ if ($optcfg['bridge']) {
+ mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) .
+ " delete up");
+
+ if ($bridgeconfig != "")
+ $bridgeconfig .= ",";
+
+ $bridgeconfig .= $optcfg['if'] . ":" . $opti . "," .
+ $config['interfaces'][$optcfg['bridge']]['if'] .
+ ":" . $opti;
+ } else {
+ mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) . " " .
+ escapeshellarg($optcfg['ipaddr'] . "/" . $optcfg['subnet']));
+ }
+ } else {
+ mwexec("/sbin/ifconfig " . escapeshellarg($optcfg['if']) .
+ " delete down");
+ }
+
+ if ($g['booting'])
+ echo "done\n";
+
+ return 0;
+}
+
+function interfaces_wireless_configure($if, $wlcfg) {
+ global $config, $g;
+
+ /* wireless configuration */
+ $ifcargs = escapeshellarg($if) .
+ " ssid " . escapeshellarg($wlcfg['ssid']) . " channel " .
+ escapeshellarg($wlcfg['channel']) . " ";
+
+ if ($wlcfg['stationname'])
+ $ifcargs .= "stationname " . escapeshellarg($wlcfg['stationname']) . " ";
+
+ if (isset($wlcfg['wep']['enable']) && is_array($wlcfg['wep']['key'])) {
+ $ifcargs .= "wepmode on ";
+
+ $i = 1;
+ foreach ($wlcfg['wep']['key'] as $wepkey) {
+ $ifcargs .= "wepkey " . escapeshellarg("{$i}:{$wepkey['value']}") . " ";
+ if (isset($wepkey['txkey'])) {
+ $ifcargs .= "weptxkey {$i} ";
+ }
+ $i++;
+ }
+ } else {
+ $ifcargs .= "wepmode off ";
+ }
+
+ switch ($wlcfg['mode']) {
+ case 'hostap':
+ if (strstr($if, "wi"))
+ $ifcargs .= "-mediaopt ibss mediaopt hostap ";
+ break;
+ case 'ibss':
+ case 'IBSS':
+ if (strstr($if, "wi"))
+ $ifcargs .= "-mediaopt hostap mediaopt ibss ";
+ else if (strstr($if, "an"))
+ $ifcargs .= "mediaopt adhoc ";
+ break;
+ case 'bss':
+ case 'BSS':
+ if (strstr($if, "wi"))
+ $ifcargs .= "-mediaopt hostap -mediaopt ibss ";
+ else if (strstr($if, "an"))
+ $ifcargs .= "-mediaopt adhoc ";
+ break;
+ }
+
+ $ifcargs .= "up";
+
+ mwexec("/sbin/ifconfig " . $ifcargs);
+
+ return 0;
+}
+
+function interfaces_wan_configure() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+
+ if ($g['booting'])
+ echo "Configuring WAN interface... ";
+ else {
+ /* kill dhclient */
+ killbypid("{$g['varrun_path']}/dhclient.pid");
+
+ /* kill PPPoE client (mpd) */
+ killbypid("{$g['varrun_path']}/mpd.pid");
+
+ /* wait for processes to die */
+ sleep(2);
+
+ /* remove dhclient.conf, if it exists */
+ if (file_exists("{$g['varetc_path']}/dhclient.conf")) {
+ unlink("{$g['varetc_path']}/dhclient.conf");
+ }
+ /* remove mpd.conf, if it exists */
+ if (file_exists("{$g['varetc_path']}/mpd.conf")) {
+ unlink("{$g['varetc_path']}/mpd.conf");
+ }
+ /* remove mpd.links, if it exists */
+ if (file_exists("{$g['varetc_path']}/mpd.links")) {
+ unlink("{$g['varetc_path']}/mpd.links");
+ }
+ /* remove wanip, if it exists */
+ if (file_exists("{$g['vardb_path']}/wanip")) {
+ unlink("{$g['vardb_path']}/wanip");
+ }
+ }
+
+ /* remove all addresses first */
+ while (mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " -alias") == 0);
+ mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " down");
+
+ /* wireless configuration? */
+ if (is_array($wancfg['wireless']))
+ interfaces_wireless_configure($wancfg['if'], $wancfg['wireless']);
+
+ if ($wancfg['spoofmac'])
+ mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) .
+ " link " . escapeshellarg($wancfg['spoofmac']));
+
+ /* media */
+ if ($wancfg['media'] || $wancfg['mediaopt']) {
+ $cmd = "/sbin/ifconfig " . escapeshellarg($wancfg['if']);
+ if ($wancfg['media'])
+ $cmd .= " media " . escapeshellarg($wancfg['media']);
+ if ($wancfg['mediaopt'])
+ $cmd .= " mediaopt " . escapeshellarg($wancfg['mediaopt']);
+ mwexec($cmd);
+ }
+
+ switch ($wancfg['ipaddr']) {
+
+ case 'dhcp':
+ interfaces_wan_dhcp_configure();
+ break;
+
+ case 'pppoe':
+ interfaces_wan_pppoe_configure();
+ break;
+
+ case 'pptp':
+ interfaces_wan_pptp_configure();
+ break;
+
+ case 'bigpond':
+ /* just configure DHCP for now; fire up bpalogin when we've got the lease */
+ interfaces_wan_dhcp_configure();
+ break;
+
+ default:
+ mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " " .
+ escapeshellarg($wancfg['ipaddr'] . "/" . $wancfg['subnet']));
+
+ /* install default route */
+ mwexec("/sbin/route delete default");
+ mwexec("/sbin/route add default " . escapeshellarg($wancfg['gateway']));
+
+ /* resync ipfilter (done automatically for DHCP/PPPoE/PPTP) */
+ filter_resync();
+ }
+
+ if (!$g['booting']) {
+ /* reconfigure static routes (kernel may have deleted them) */
+ system_routing_configure();
+
+ /* reload ipfilter */
+ filter_configure();
+
+ /* reload shaper */
+ shaper_configure();
+
+ /* reload ipsec tunnels */
+ vpn_ipsec_configure();
+
+ /* restart ez-ipupdate */
+ services_dyndns_configure();
+
+ /* restart dnsmasq */
+ services_dnsmasq_configure();
+ }
+
+ if ($g['booting'])
+ echo "done\n";
+
+ return 0;
+}
+
+function interfaces_wan_dhcp_configure() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+
+ /* generate dhclient.conf */
+ $fd = fopen("{$g['varetc_path']}/dhclient.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open dhclient.conf in interfaces_wan_dhcp_configure().\n");
+ return 1;
+ }
+
+ $dhclientconf = "";
+
+ if ($wancfg['dhcphostname']) {
+ $dhclientconf .= <<<EOD
+send dhcp-client-identifier "{$wancfg['dhcphostname']}";
+interface "{$wancfg['if']}" {
+ send host-name "{$wancfg['dhcphostname']}";
+}
+
+EOD;
+ }
+
+ fwrite($fd, $dhclientconf);
+ fclose($fd);
+
+ /* fire up dhclient - don't wait for the lease (-nw) */
+ mwexec("/sbin/dhclient -nw -cf {$g['varetc_path']}/dhclient.conf " .
+ escapeshellarg($wancfg['if']) . " &");
+
+ return 0;
+}
+
+function interfaces_wan_pppoe_configure() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+ $pppoecfg = $config['pppoe'];
+
+ /* generate mpd.conf */
+ $fd = fopen("{$g['varetc_path']}/mpd.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open mpd.conf in interfaces_wan_pppoe_configure().\n");
+ return 1;
+ }
+
+ $idle = 0;
+
+ if (isset($pppoecfg['ondemand'])) {
+ $ondemand = "enable";
+ if ($pppoecfg['timeout'])
+ $idle = $pppoecfg['timeout'];
+ } else {
+ $ondemand = "disable";
+ }
+
+ $mpdconf = <<<EOD
+pppoe:
+ new -i ng0 pppoe pppoe
+ set iface route default
+ set iface {$ondemand} on-demand
+ set iface idle {$idle}
+ set iface up-script /usr/local/sbin/ppp-linkup
+
+EOD;
+
+ if (isset($pppoecfg['ondemand'])) {
+ $mpdconf .= <<<EOD
+ set iface addrs 10.0.0.1 10.0.0.2
+
+EOD;
+ }
+
+ $mpdconf .= <<<EOD
+ set bundle disable multilink
+ set bundle authname "{$pppoecfg['username']}"
+ set bundle password "{$pppoecfg['password']}"
+ set link keep-alive 10 60
+ set link max-redial 0
+ set link no acfcomp protocomp
+ set link disable pap chap
+ set link accept chap
+ set link mtu 1492
+ set ipcp yes vjcomp
+ set ipcp ranges 0.0.0.0/0 0.0.0.0/0
+ set ipcp enable req-pri-dns
+ set ipcp enable req-sec-dns
+ open iface
+
+EOD;
+
+ fwrite($fd, $mpdconf);
+ fclose($fd);
+
+ /* generate mpd.links */
+ $fd = fopen("{$g['varetc_path']}/mpd.links", "w");
+ if (!$fd) {
+ printf("Error: cannot open mpd.links in interfaces_wan_pppoe_configure().\n");
+ return 1;
+ }
+
+ $mpdconf = <<<EOD
+pppoe:
+ set link type pppoe
+ set pppoe iface {$wancfg['if']}
+ set pppoe service "{$pppoecfg['provider']}"
+ set pppoe enable originate
+ set pppoe disable incoming
+
+EOD;
+
+ fwrite($fd, $mpdconf);
+ fclose($fd);
+
+ /* fire up mpd */
+ mwexec("/usr/local/sbin/mpd -b -d {$g['varetc_path']} -p {$g['varrun_path']}/mpd.pid pppoe");
+
+ return 0;
+}
+
+function interfaces_wan_pptp_configure() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+ $pptpcfg = $config['pptp'];
+
+ /* generate mpd.conf */
+ $fd = fopen("{$g['varetc_path']}/mpd.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open mpd.conf in interfaces_wan_pptp_configure().\n");
+ return 1;
+ }
+
+ $idle = 0;
+
+ if (isset($pptpcfg['ondemand'])) {
+ $ondemand = "enable";
+ if ($pptpcfg['timeout'])
+ $idle = $pptpcfg['timeout'];
+ } else {
+ $ondemand = "disable";
+ }
+
+ $mpdconf = <<<EOD
+pptp:
+ new -i ng0 pptp pptp
+ set iface route default
+ set iface {$ondemand} on-demand
+ set iface idle {$idle}
+ set iface up-script /usr/local/sbin/ppp-linkup
+
+EOD;
+
+ if (isset($pptpcfg['ondemand'])) {
+ $mpdconf .= <<<EOD
+ set iface addrs {$pptpcfg['local']} {$pptpcfg['remote']}
+
+EOD;
+ }
+
+ $mpdconf .= <<<EOD
+ set bundle disable multilink
+ set bundle authname "{$pptpcfg['username']}"
+ set bundle password "{$pptpcfg['password']}"
+ set link keep-alive 10 60
+ set link max-redial 0
+ set link no acfcomp protocomp
+ set link disable pap chap
+ set link accept chap
+ set ipcp no vjcomp
+ set ipcp ranges 0.0.0.0/0 0.0.0.0/0
+ set ipcp enable req-pri-dns
+ set ipcp enable req-sec-dns
+ open
+
+EOD;
+
+ fwrite($fd, $mpdconf);
+ fclose($fd);
+
+ /* generate mpd.links */
+ $fd = fopen("{$g['varetc_path']}/mpd.links", "w");
+ if (!$fd) {
+ printf("Error: cannot open mpd.links in interfaces_wan_pptp_configure().\n");
+ return 1;
+ }
+
+ $mpdconf = <<<EOD
+pptp:
+ set link type pptp
+ set pptp enable originate outcall
+ set pptp disable windowing
+ set pptp self {$pptpcfg['local']}
+ set pptp peer {$pptpcfg['remote']}
+
+EOD;
+
+ fwrite($fd, $mpdconf);
+ fclose($fd);
+
+ /* configure interface */
+ mwexec("/sbin/ifconfig " . escapeshellarg($wancfg['if']) . " " .
+ escapeshellarg($pptpcfg['local'] . "/" . $pptpcfg['subnet']));
+
+ /* fire up mpd */
+ mwexec("/usr/local/sbin/mpd -b -d {$g['varetc_path']} -p {$g['varrun_path']}/mpd.pid pptp");
+
+ return 0;
+}
+
+function interfaces_wan_bigpond_configure($curwanip) {
+ global $config, $g;
+
+ $bpcfg = $config['bigpond'];
+
+ if (!$curwanip) {
+ /* IP address not configured yet, exit */
+ return 0;
+ }
+
+ /* kill bpalogin */
+ killbyname("bpalogin");
+
+ /* wait a moment */
+ sleep(1);
+
+ /* get the default domain */
+ $nfd = @fopen("{$g['varetc_path']}/defaultdomain.conf", "r");
+ if ($nfd) {
+ $defaultdomain = trim(fgets($nfd));
+ fclose($nfd);
+ }
+
+ /* generate bpalogin.conf */
+ $fd = fopen("{$g['varetc_path']}/bpalogin.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open bpalogin.conf in interfaces_wan_bigpond_configure().\n");
+ return 1;
+ }
+
+ if (!$bpcfg['authserver'])
+ $bpcfg['authserver'] = "dce-server";
+ if (!$bpcfg['authdomain'])
+ $bpcfg['authdomain'] = $defaultdomain;
+
+ $bpconf = <<<EOD
+username {$bpcfg['username']}
+password {$bpcfg['password']}
+authserver {$bpcfg['authserver']}
+authdomain {$bpcfg['authdomain']}
+localport 5050
+
+EOD;
+
+ if ($bpcfg['minheartbeatinterval'])
+ $bpconf .= "minheartbeatinterval {$bpcfg['minheartbeatinterval']}\n";
+
+ fwrite($fd, $bpconf);
+ fclose($fd);
+
+ /* fire up bpalogin */
+ mwexec("/usr/local/sbin/bpalogin -c {$g['varetc_path']}/bpalogin.conf");
+
+ return 0;
+}
+
+function get_real_wan_interface() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+
+ $wanif = $wancfg['if'];
+ if (($wancfg['ipaddr'] == "pppoe") || ($wancfg['ipaddr'] == "pptp")) {
+ $wanif = $g['pppoe_interface'];
+ }
+
+ return $wanif;
+}
+
+function get_current_wan_address() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+
+ if (in_array($wancfg['ipaddr'], array('pppoe','dhcp','pptp','bigpond'))) {
+ /* dynamic WAN IP address, find out which one */
+ $wanif = get_real_wan_interface();
+
+ /* get interface info with netstat */
+ exec("/usr/bin/netstat -nWI " . escapeshellarg($wanif) . " -f inet", $ifinfo);
+
+ if (isset($ifinfo[1])) {
+ $aif = preg_split("/\s+/", $ifinfo[1]);
+ $curwanip = chop($aif[3]);
+
+ if ($curwanip && is_ipaddr($curwanip) && ($curwanip != "0.0.0.0"))
+ return $curwanip;
+ }
+
+ return null;
+ } else {
+ /* static WAN IP address */
+ return $wancfg['ipaddr'];
+ }
+}
+
+?>
diff --git a/etc/inc/openvpn.inc b/etc/inc/openvpn.inc
new file mode 100644
index 0000000..2414ae0
--- /dev/null
+++ b/etc/inc/openvpn.inc
@@ -0,0 +1,559 @@
+<?php
+/*
+ openvpn.inc
+
+ Copyright (C) 2004 Peter Curran (peter@closeconsultants.com).
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("globals.inc");
+require_once("config.inc");
+require_once("functions.inc");
+
+function ovpn_configure() {
+ global $config;
+ if (is_array($config['ovpn']['server']))
+ ovpn_config_server();
+ if (is_array($config['ovpn']['client']))
+ ovpn_config_client();
+ return;
+}
+
+function ovpn_link_tap() {
+ /* Add a reference to the tap KLM. If ref count = 1, load it */
+ global $g;
+
+ if (!is_file($g['vardb_path'] ."/ovpn_tap_link")){
+ $link_count = 1;
+ mwexec("/sbin/kldload if_tap");
+ $fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'w');
+ }
+ else {
+ $fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'r+');
+ $link_count = fread($fd);
+ $link_count ++;
+ }
+ fwrite($fd, $link_count);
+ fclose($fd);
+ return true;
+}
+
+function ovpn_unlink_tap() {
+ /* Remove a reference to the tap KLM. If ref count = 0, unload it */
+ global $g;
+
+ if (!is_file($g['vardb_path'] ."/ovpn_tap_link"))
+ return false; //no file, no links so why are we called?
+
+ $fd = fopen($g['vardb_path'] ."/ovpn_tap_link", 'r+');
+ $link_count = fread($fd);
+ $link_count --;
+ fwrite($fd, $link_count);
+ fclose($fd);
+
+ if ($link_count == 0)
+ mwexec("/sbin/kldunload if_tap");
+ return true;
+}
+
+/*****************************/
+/* Server-related functions */
+
+/* Configure the server */
+function ovpn_config_server() {
+ global $config, $g;
+
+ if (isset($config['ovpn']['server']['enable'])) {
+
+ if ($g['booting'])
+ echo "Starting OpenVPN server... ";
+
+ /* kill any running openvpn daemon */
+ killbypid($g['varrun_path']."/ovpn_srv.pid");
+
+ /* Remove old certs & keys */
+ unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_srv_cert.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_srv_key.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_dh.pem");
+
+ /* Copy the TLS-Server certs & keys to disk */
+ $fd = @fopen("{$g['vardb_path']}/ovpn_ca_cert.pem", "w");
+ if ($fd) {
+ fwrite($fd, base64_decode($config['ovpn']['server']['ca_cert'])."\n");
+ fclose($fd);
+ }
+ $fd = @fopen("{$g['vardb_path']}/ovpn_srv_cert.pem", "w");
+ if ($fd) {
+ fwrite($fd, base64_decode($config['ovpn']['server']['srv_cert'])."\n");
+ fclose($fd);
+ }
+ $fd = @fopen("{$g['vardb_path']}/ovpn_srv_key.pem", "w");
+ if ($fd) {
+ fwrite($fd, base64_decode($config['ovpn']['server']['srv_key'])."\n");
+ fclose($fd);
+ }
+ $fd = @fopen("{$g['vardb_path']}/ovpn_dh.pem", "w");
+ if ($fd) {
+ fwrite($fd, base64_decode($config['ovpn']['server']['dh_param'])."\n");
+ fclose($fd);
+ }
+
+ /* Start the openvpn daemon */
+ mwexec("/usr/local/sbin/openvpn " . ovpn_srv_config_generate());
+
+ if ($g['booting'])
+ /* Send the boot message */
+ echo "done\n";
+ }
+ else {
+ if (!$g['booting']){
+ /* stop any processes, unload the tap module */
+ /* Remove old certs & keys */
+ unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_srv_cert.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_srv_key.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_dh.pem");
+ killbypid("{$g['varrun_path']}/ovpn_srv.pid");
+ if ($config['ovpn']['server']['tun_iface'] == 'tap0')
+ ovpn_unlink_tap();
+ }
+ }
+ return 0;
+}
+
+/* Generate the config for a OpenVPN server */
+function ovpn_srv_config_generate() {
+ global $config, $g;
+ $server = $config['ovpn']['server'];
+
+ /* First the generic stuff:
+ - We are a server
+ - We are a TLS Server (for authentication)
+ - We will run without privilege
+ */
+ $ovpn_config = "--daemon --user nobody --group nobody --verb {$server['verb']} ";
+
+ /* pid file */
+ $ovpn_config .= "--writepid {$g['varrun_path']}/ovpn_srv.pid ";
+
+ /* interface */
+ $ovpn_config .= "--dev {$server['tun_iface']} ";
+
+ /* port */
+ $ovpn_config .= "--port {$server['port']} ";
+
+ /* Interface binding - 1 or all */
+ if ($server['bind_iface'] != 'all') {
+ if ($ipaddr = ovpn_get_ip($server['bind_iface']))
+ $ovpn_config .= "--local $ipaddr ";
+ else
+ return "Interface bridged";
+
+ }
+
+ /* Client to client routing (off by default) */
+ if (isset($server['cli2cli']))
+ $ovpn_config .= "--client-to-client ";
+
+ /* Set maximum simultaneous clients */
+ $ovpn_config .= "--max-clients {$server['maxcli']} ";
+
+ /* New --server macro simplifies config */
+ $mask = ovpn_calc_mask($server['prefix']);
+ $ovpn_config .= "--server {$server['ipblock']} {$mask} ";
+
+ /* TLS-Server params */
+ $ovpn_config .= "--ca {$g['vardb_path']}/ovpn_ca_cert.pem ";
+ $ovpn_config .= "--cert {$g['vardb_path']}/ovpn_srv_cert.pem ";
+ $ovpn_config .= "--key {$g['vardb_path']}/ovpn_srv_key.pem ";
+ $ovpn_config .= "--dh {$g['vardb_path']}/ovpn_dh.pem ";
+
+ /* Data channel encryption cipher*/
+ $ovpn_config .= "--cipher {$server['crypto']} ";
+
+ /* Duplicate CNs */
+ if (isset($server['dupcn']))
+ $ovpn_config .= "--duplicate-cn ";
+
+ /* Client push - redirect gateway */
+ if (isset($server['psh_options']['redir'])){
+ if (isset($server['psh_options']['redir_loc']))
+ $ovpn_config .= "--push \"redirect-gateway 'local'\" ";
+ else
+ $ovpn_config .= "--push \"redirect-gateway\" ";
+ }
+
+ /* Client push - route delay */
+ if (isset($server['psh_options']['rte_delay']))
+ $ovpn_config .= "--push \"route-delay {$server['psh_options']['rte_delay']}\" ";
+
+ /* Client push - ping (note we set both server and client) */
+ if (isset ($server['psh_options']['ping'])){
+ $ovpn_config .= "--ping {$server['psh_options']['ping']} ";
+ $ovpn_config .= "--push \"ping {$server['psh_options']['ping']}\" ";
+ }
+
+ /* Client push - ping-restart (note server uses 2 x client interval) */
+ if (isset ($server['psh_options']['pingrst'])){
+ $interval = $server['psh_options']['pingrst'];
+ $ovpn_config .= "--ping-restart " . ($interval * 2) . " ";
+ $ovpn_config .= "--push \"ping-restart $interval\" ";
+ }
+
+ /* Client push - ping-exit (set on client) */
+ if (isset ($server['psh_options']['pingexit'])){
+ $ovpn_config .= "--ping-exit {$server['psh_options']['pingexit']} ";
+ $ovpn_config .= "--push \"ping-exit {$server['psh_options']['pingexit']}\" ";
+ }
+
+ /* Client push - inactive (set on client) */
+ if (isset ($server['psh_options']['inact'])){
+ $ovpn_config .= "--inactive {$server['psh_options']['pingexit']} ";
+ $ovpn_config .= "--push \"inactive {$server['psh_options']['inact']}\" ";
+ }
+
+ //trigger_error("OVPN: $ovpn_config", E_USER_NOTICE);
+ return $ovpn_config;
+}
+
+/* Define an OVPN Server tunnel interface in the interfaces array and assign a name */
+function ovpn_server_iface(){
+ global $config, $g;
+
+ $i = 1;
+ while (true) {
+ $ifname = 'opt' . $i;
+ if (is_array($config['interfaces'][$ifname])) {
+ if ((isset($config['interfaces'][$ifname]['ovpn']))
+ && ($config['interfaces'][$ifname]['ovpn'] == 'server'))
+ /* Already an interface defined - overwrite */
+ break;
+ }
+ else {
+ /* No existing entry, this is first unused */
+ $config['interfaces'][$ifname] = array();
+ break;
+ }
+ $i++;
+ }
+ $config['interfaces'][$ifname]['descr'] = "OVPN server";
+ $config['interfaces'][$ifname]['if'] = $config['ovpn']['server']['tun_iface'];
+ $config['interfaces'][$ifname]['ipaddr'] = long2ip( ip2long($config['ovpn']['server']['ipblock']) + 1);
+ $config['interfaces'][$ifname]['subnet'] = $config['ovpn']['server']['prefix'];
+ $config['interfaces'][$ifname]['enable'] = isset($config['ovpn']['server']['enable']) ? true : false;
+ $config['interfaces'][$ifname]['ovpn'] = 'server';
+
+ write_config();
+
+ return "OpenVPN server interface defined";
+}
+
+/********************************************************/
+/* Client related functions */
+function ovpn_config_client() {
+ /* Boot time configuration */
+ global $config, $g;
+
+ foreach ($config['ovpn']['client']['tunnel'] as $id => $client) {
+ if (isset($client['enable'])) {
+
+ if ($g['booting'])
+ echo "Starting OpenVPN client $id... ";
+
+ /* kill any running openvpn daemon */
+ killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid");
+
+ /* Remove old certs & keys */
+ unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_cli_cert_{$id}.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_cli_key_{$id}.pem");
+
+ /* Copy the TLS-Client certs & keys to disk */
+ /*$fd = @fopen("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem", "w");*/
+ $fd = fopen("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem", "w");
+ if ($fd) {
+ fwrite($fd, base64_decode($client['ca_cert'])."\n");
+ fclose($fd);
+ }
+ else
+ trigger_error("OVPN: No open for CA", E_USER_NOTICE);
+ $fd = fopen($g['vardb_path']."/ovpn_cli_cert_".$id.".pem", "w");
+ if ($fd) {
+ fwrite($fd, base64_decode($client['cli_cert'])."\n");
+ fclose($fd);
+ }
+ $fd = fopen($g['vardb_path']."/ovpn_cli_key_".$id.".pem", "w");
+ if ($fd) {
+ fwrite($fd, base64_decode($client['cli_key'])."\n");
+ fclose($fd);
+ }
+
+ /* Start openvpn for this client */
+ mwexec("/usr/local/sbin/openvpn " . ovpn_cli_config_generate($id));
+
+ if ($g['booting'])
+ /* Send the boot message */
+ echo "done\n";
+ }
+ else {
+ if (!$g['booting']){
+ /* stop any processes, unload the tap module */
+ /* Remove old certs & keys */
+ unlink_if_exists("{$g['vardb_path']}/ovpn_ca_cert_{$id}.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_cli_cert_{$id}.pem");
+ unlink_if_exists("{$g['vardb_path']}/ovpn_cli_key_{$id}.pem");
+ killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid");
+ if ($client['type'] == "tap")
+ ovpn_unlink_tap();
+ }
+ }
+ }
+ return 0;
+
+}
+
+/* Kill off a running client process */
+function ovpn_client_kill($id) {
+ global $g;
+
+ killbypid("{$g['varrun_path']}/ovpn_client{$id}.pid");
+ return 0;
+}
+
+function ovpn_cli_config_generate($id) {
+ /* configure the named client */
+ global $config, $g;
+ $client = $config['ovpn']['client']['tunnel'];
+
+ /* Client support in 2.0 is very simple */
+
+ $ovpn_config = "--client --daemon --verb 1 ";
+
+ /* pid file */
+ $ovpn_config .= "--writepid {$g['varrun_path']}/ovpn_client{$id}.pid ";
+
+ /* interface */
+ $ovpn_config .= "--dev {$client[$id]['if']} ";
+
+ /* protocol */
+ $ovpn_config .= "--proto {$client[$id]['proto']} ";
+
+ /* port */
+ $ovpn_config .= "--lport {$client[$id]['cport']} ";
+
+ /* server location */
+ $ovpn_config .= "--remote {$client[$id]['saddr']} {$client[$id]['sport']} ";
+
+ /* TLS-Server params */
+ $ovpn_config .= "--ca {$g['vardb_path']}/ovpn_ca_cert_{$id}.pem ";
+ $ovpn_config .= "--cert {$g['vardb_path']}/ovpn_cli_cert_{$id}.pem ";
+ $ovpn_config .= "--key {$g['vardb_path']}/ovpn_cli_key_{$id}.pem ";
+
+ /* Data channel encryption cipher*/
+ $ovpn_config .= "--cipher {$client[$id]['crypto']} ";
+
+ //trigger_error("OVPN: $ovpn_config", E_USER_NOTICE);
+ return $ovpn_config;
+}
+
+/* Define an OVPN tunnel interface in the interfaces array for each client */
+function ovpn_client_iface(){
+ global $config;
+
+ foreach ($config['ovpn']['client']['tunnel'] as $id => $client) {
+ if (isset($client['enable'])) {
+ $i = 1;
+ while (true) {
+ $ifname = 'opt' . $i;
+ if (is_array($config['interfaces'][$ifname])) {
+ if ((isset($config['interfaces'][$ifname]['ovpn']))
+ && ($config['interfaces'][$ifname]['ovpn'] == "client{$id}"))
+ /* Already an interface defined - overwrite */
+ break;
+ }
+ else {
+ /* No existing entry, this is first unused */
+ $config['interfaces'][$ifname] = array();
+ break;
+ }
+ $i++;
+ }
+ if (isset($client['descr']))
+ $config['interfaces'][$ifname]['descr'] = $client['descr'];
+ else
+ $config['interfaces'][$ifname]['descr'] = "OVPN client-{$id}";
+ $config['interfaces'][$ifname]['if'] = $client['if'];
+ $config['interfaces'][$ifname]['ipaddr'] = "0.0.0.0";
+ $config['interfaces'][$ifname]['subnet'] = "0";
+ $config['interfaces'][$ifname]['enable'] = isset($client['enable']) ? true : false;
+ $config['interfaces'][$ifname]['ovpn'] = "client{$id}";
+ write_config();
+ }
+ }
+ return "OpenVPN client interfaces defined";
+}
+
+/* Delete a client interface definition */
+function ovpn_client_iface_del($id) {
+ global $config;
+
+ $i = 1;
+ while (true) {
+ $ifname = 'opt' . $i;
+ if (is_array($config['interfaces'][$ifname])) {
+ if ((isset($config['interfaces'][$ifname]['ovpn']))
+ && ($config['interfaces'][$ifname]['ovpn'] == "client{$id}"))
+ unset($config['interfaces'][$ifname]);
+ }
+ }
+}
+
+/******************/
+/* Misc functions */
+
+/* Calculate the last address in a range given the start and /prefix */
+function ovpn_calc_end($start, $prefix){
+
+ $first = ip2long($start);
+ $last = pow(2,(32 - $prefix)) - 1 + $first;
+ return long2ip($last);
+}
+
+/* Calculate a mask given a /prefix */
+function ovpn_calc_mask($prefix){
+
+ return long2ip(ip2long("255.255.255.255") - (pow( 2, (32 - $prefix)) - 1));
+}
+
+/* Read in a file from the $_FILES array */
+function ovpn_get_file($file){
+ global $g;
+
+ if (!is_uploaded_file($_FILES[$file]['tmp_name'])){
+ trigger_error("Bad file upload".$_FILES[$file]['error'], E_USER_NOTICE);
+ return NULL;
+ }
+ $contents = file_get_contents($_FILES[$file]['tmp_name']);
+ return $contents;
+}
+
+
+/* Get the IP address of a specified interface */
+function ovpn_get_ip($iface){
+ global $config;
+
+ if ($iface == 'wan')
+ return get_current_wan_address();
+
+ if ($config['interfaces'][$iface]['bridge'])
+ /* No bridging (yet) */
+ return false;
+ return $config['interfaces'][$iface]['ipaddr'];
+}
+
+/* Get a list of the cipher options supported by OpenVPN */
+function ovpn_get_cipher_list(){
+
+/* exec("/usr/local/sbin/openvpn --show-ciphers", $raw);
+ print_r ($raw);
+
+ $ciphers = preg_grep('/ bit default key /', $raw);
+
+ for($i = 0; $i <count($ciphers); $i++){
+ $tmp = explode(' ',$ciphers[$i]);
+ $cipher_list["$tmp[0]"] = "{$tmp[0]} ({$tmp[1]} {$tmp[2]})";
+ }
+*/
+ $cipher_list = array('DES-CBC' => 'DES-CBC (64 bit)',
+ 'RC2-CBC' => 'RC2-CBC (128 bit)',
+ 'DES-EDE-CBC' => 'DES-EDE-CBC (128 bit)',
+ 'DES-EDE3-CBC' => 'DES-EDE3-CBC (192 bit)',
+ 'DESX-CBC' => 'DESX-CBC (192 bit)',
+ 'BF-CBC' => 'BF-CBC (128 bit)',
+ 'RC2-40-CBC' => 'RC2-40-CBC (40 bit)',
+ 'CAST5-CBC' => 'CAST5-CBC (128 bit)',
+ 'RC5-CBC' => 'RC5-CBC (128 bit)',
+ 'RC2-64-CBC' => 'RC2-64-CBC (64 bit)',
+ 'AES-128-CBC' => 'AES-128-CBC (128 bit)',
+ 'AES-192-CBC' => 'AES-192-CBC (192 bit)',
+ 'AES-256-CBC' => 'AES-256-CBC (256 bit)');
+ return $cipher_list;
+}
+
+
+/* Build a list of the current real interfaces */
+function ovpn_real_interface_list(){
+ global $config;
+
+ $interfaces = array('all' => 'ALL',
+ 'lan' => 'LAN',
+ 'wan' => 'WAN');
+ for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
+ if (isset($config['interfaces']['opt' . $i]['ovpn']))
+ /* Hide our own interface */
+ break;
+ if (isset($config['interfaces']['opt' . $i]['enable']))
+ $interfaces['opt' . $i] = $config['interfaces']['opt' . $i]['descr'];
+ }
+ return $interfaces;
+}
+
+
+/* lock openvpn information, decide that the lock file is stale after
+ 10 seconds */
+function ovpn_lock() {
+
+ global $g;
+
+ $lockfile = "{$g['varrun_path']}/ovpn.lock";
+
+ $n = 0;
+ while ($n < 10) {
+ /* open the lock file in append mode to avoid race condition */
+ if ($fd = @fopen($lockfile, "x")) {
+ /* succeeded */
+ fclose($fd);
+ return;
+ } else {
+ /* file locked, wait and try again */
+ sleep(1);
+ $n++;
+ }
+ }
+}
+
+/* unlock configuration file */
+function ovpn_unlock() {
+
+ global $g;
+
+ $lockfile = "{$g['varrun_path']}/ovpn.lock";
+
+ if (file_exists($lockfile))
+ unlink($lockfile);
+}
+
+?>
diff --git a/etc/inc/services.inc b/etc/inc/services.inc
new file mode 100644
index 0000000..17bc959
--- /dev/null
+++ b/etc/inc/services.inc
@@ -0,0 +1,440 @@
+<?php
+/*
+ services.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("functions.inc");
+
+function services_dhcpd_configure() {
+ global $config, $g;
+
+ /* kill any running dhcpd */
+ killbypid("{$g['varrun_path']}/dhcpd.pid");
+
+ $syscfg = $config['system'];
+ $dhcpdcfg = $config['dhcpd'];
+
+ /* DHCP enabled on any interfaces? */
+ $dhcpdenable = false;
+ foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
+ if (isset($dhcpifconf['enable']) &&
+ (($dhcpif == "lan") ||
+ (isset($config['interfaces'][$dhcpif]['enable']) &&
+ $config['interfaces'][$dhcpif]['if'] && (!$config['interfaces'][$dhcpif]['bridge']))))
+ $dhcpdenable = true;
+ }
+
+ if (!$dhcpdenable)
+ return 0;
+
+ if ($g['booting'])
+ echo "Starting DHCP service... ";
+ else
+ sleep(1);
+
+ /* write dhcpd.conf */
+ $fd = fopen("{$g['varetc_path']}/dhcpd.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open dhcpd.conf in services_dhcpd_configure().\n");
+ return 1;
+ }
+
+ $dhcpdconf = <<<EOD
+option domain-name "{$syscfg['domain']}";
+default-lease-time 7200;
+max-lease-time 86400;
+authoritative;
+log-facility local7;
+ddns-update-style none;
+
+EOD;
+
+ $dhcpdifs = array();
+ foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
+
+ $ifcfg = $config['interfaces'][$dhcpif];
+
+ if (!isset($dhcpifconf['enable']) ||
+ (($dhcpif != "lan") &&
+ (!isset($ifcfg['enable']) || !$ifcfg['if'] || $ifcfg['bridge'])))
+ continue;
+
+ $subnet = gen_subnet($ifcfg['ipaddr'], $ifcfg['subnet']);
+ $subnetmask = gen_subnet_mask($ifcfg['subnet']);
+
+ $dnscfg = "";
+
+ if ($dhcpifconf['domain']) {
+ $dnscfg .= " option domain-name \"{$dhcpifconf['domain']}\";\n";
+ }
+
+ if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
+ $dnscfg .= " option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";";
+ } else if (isset($config['dnsmasq']['enable'])) {
+ $dnscfg .= " option domain-name-servers " . $ifcfg['ipaddr'] . ";";
+ } else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
+ $dnscfg .= " option domain-name-servers " . join(",", $syscfg['dnsserver']) . ";";
+ }
+
+ $dhcpdconf .= "subnet $subnet netmask $subnetmask {\n";
+ $dhcpdconf .= " pool {\n";
+ if (isset($dhcpifconf['denyunknown']))
+ $dhcpdconf .= " deny unknown clients;\n";
+
+ if ($dhcpifconf['gateway'])
+ $routers = $dhcpifconf['gateway'];
+ else
+ $routers = $ifcfg['ipaddr'];
+
+ $dhcpdconf .= <<<EOD
+ range {$dhcpifconf['range']['from']} {$dhcpifconf['range']['to']};
+ }
+ option routers {$routers};
+$dnscfg
+
+EOD;
+
+ if ($dhcpifconf['defaultleasetime'])
+ $dhcpdconf .= " default-lease-time {$dhcpifconf['defaultleasetime']};\n";
+ if ($dhcpifconf['maxleasetime'])
+ $dhcpdconf .= " max-lease-time {$dhcpifconf['maxleasetime']};\n";
+
+ if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
+ $dhcpdconf .= " option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
+ $dhcpdconf .= " option netbios-node-type 8;\n";
+ }
+
+ if ($dhcpifconf['next-server'])
+ $dhcpdconf .= " next-server {$dhcpifconf['next-server']};\n";
+ if ($dhcpifconf['filename'])
+ $dhcpdconf .= " filename \"{$dhcpifconf['filename']}\";\n";
+
+ $dhcpdconf .= <<<EOD
+}
+
+EOD;
+
+ /* add static mappings */
+ if (is_array($dhcpifconf['staticmap'])) {
+
+ $i = 0;
+ foreach ($dhcpifconf['staticmap'] as $sm) {
+ $dhcpdconf .= <<<EOD
+host s_{$dhcpif}_{$i} {
+ hardware ethernet {$sm['mac']};
+
+EOD;
+ if ($sm['ipaddr'])
+ $dhcpdconf .= " fixed-address {$sm['ipaddr']};\n";
+
+ $dhcpdconf .= "}\n";
+ $i++;
+ }
+ }
+
+ $dhcpdifs[] = $ifcfg['if'];
+ }
+
+ fwrite($fd, $dhcpdconf);
+ fclose($fd);
+
+ /* create an empty leases database */
+ touch("{$g['vardb_path']}/dhcpd.leases");
+
+ /* fire up dhcpd */
+ mwexec("/usr/local/sbin/dhcpd -cf {$g['varetc_path']}/dhcpd.conf " .
+ join(" ", $dhcpdifs));
+
+ if (!$g['booting']) {
+ filter_configure();
+ } else
+ echo "done\n";
+
+ return 0;
+}
+
+function services_dhcrelay_configure() {
+ global $config, $g;
+
+ /* kill any running dhcrelay */
+ killbypid("{$g['varrun_path']}/dhcrelay.pid");
+
+ $dhcrelaycfg = $config['dhcrelay'];
+
+ /* DHCPRelay enabled on any interfaces? */
+ $dhcrelayenable = false;
+ foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) {
+ if (isset($dhcrelayifconf['enable']) &&
+ (($dhcrelayif == "lan") ||
+ (isset($config['interfaces'][$dhcrelayif]['enable']) &&
+ $config['interfaces'][$dhcrelayif]['if'] && (!$config['interfaces'][$dhcrelayif]['bridge']))))
+ $dhcrelayenable = true;
+ }
+
+ if (!$dhcrelayenable)
+ return 0;
+
+ if ($g['booting'])
+ echo "Starting DHCP relay service... ";
+ else
+ sleep(1);
+
+ $dhcrelayifs = array();
+ foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) {
+
+ $ifcfg = $config['interfaces'][$dhcrelayif];
+
+ if (!isset($dhcrelayifconf['enable']) ||
+ (($dhcrelayif != "lan") &&
+ (!isset($ifcfg['enable']) || !$ifcfg['if'] || $ifcfg['bridge'])))
+ continue;
+
+ $dhcrelayifs[] = $ifcfg['if'];
+ }
+
+ /* In order for the relay to work, it needs to be active on the
+ interface in which the destination server sits */
+ foreach ($config['interfaces'] as $ifname) {
+ $subnet = $ifname['ipaddr'] . "/" . $ifname['subnet'];
+ if (ip_in_subnet($dhcrelaycfg['server'],$subnet))
+ $destif = $ifname['if'];
+ }
+
+ if (!isset($destif))
+ $destif = $config['interfaces']['wan']['if'];
+
+ $dhcrelayifs[] = $destif;
+ $dhcrelayifs = array_unique($dhcrelayifs);
+
+ /* fire up dhcrelay */
+ $cmd = "/usr/local/sbin/dhcrelay -i " . join(" -i ", $dhcrelayifs);
+
+ if (isset($dhcrelaycfg['agentoption']))
+ $cmd .= " -a -m replace";
+
+ $cmd .= " {$dhcrelaycfg['server']}";
+ mwexec($cmd);
+
+ if (!$g['booting']) {
+ filter_configure();
+ } else
+ echo "done\n";
+
+ return 0;
+}
+
+function services_dyndns_reset() {
+ global $config, $g;
+
+ if (file_exists("{$g['vardb_path']}/ez-ipupdate.cache")) {
+ unlink("{$g['vardb_path']}/ez-ipupdate.cache");
+ }
+
+ if (file_exists("{$g['conf_path']}/ez-ipupdate.cache")) {
+ conf_mount_rw();
+ unlink("{$g['conf_path']}/ez-ipupdate.cache");
+ conf_mount_ro();
+ }
+
+ return 0;
+}
+
+function services_dyndns_configure() {
+ global $config, $g;
+
+ /* kill any running ez-ipupdate */
+ /* ez-ipupdate needs SIGQUIT instead of SIGTERM */
+ sigkillbypid("{$g['varrun_path']}/ez-ipupdate.pid", "QUIT");
+
+ $dyndnscfg = $config['dyndns'];
+ $wancfg = $config['interfaces']['wan'];
+
+ if (isset($dyndnscfg['enable'])) {
+
+ if ($g['booting'])
+ echo "Starting DynDNS client... ";
+ else
+ sleep(1);
+
+ /* determine WAN interface name */
+ $wanif = get_real_wan_interface();
+
+ /* write ez-ipupdate.conf */
+ $fd = fopen("{$g['varetc_path']}/ez-ipupdate.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open ez-ipupdate.conf in services_dyndns_configure().\n");
+ return 1;
+ }
+
+ $ezipupdateconf = <<<EOD
+service-type={$dyndnscfg['type']}
+user={$dyndnscfg['username']}:{$dyndnscfg['password']}
+host={$dyndnscfg['host']}
+interface=$wanif
+max-interval=2073600
+pid-file={$g['varrun_path']}/ez-ipupdate.pid
+cache-file={$g['vardb_path']}/ez-ipupdate.cache
+execute=/etc/rc.dyndns.storecache
+daemon
+
+EOD;
+
+ /* enable MX? */
+ if ($dyndnscfg['mx']) {
+ $ezipupdateconf .= "mx={$dyndnscfg['mx']}\n";
+ }
+
+ /* enable wildcards? */
+ if (isset($dyndnscfg['wildcard'])) {
+ $ezipupdateconf .= "wildcard\n";
+ }
+
+ fwrite($fd, $ezipupdateconf);
+ fclose($fd);
+
+ /* if we're booting, copy the cache file from /conf */
+ if ($g['booting']) {
+ if (file_exists("{$g['conf_path']}/ez-ipupdate.cache")) {
+ copy("{$g['conf_path']}/ez-ipupdate.cache", "{$g['vardb_path']}/ez-ipupdate.cache");
+ }
+ }
+
+ /* run ez-ipupdate */
+ mwexec("/usr/local/bin/ez-ipupdate -c {$g['varetc_path']}/ez-ipupdate.conf");
+
+ if ($g['booting'])
+ echo "done\n";
+ }
+
+ return 0;
+}
+
+function services_dnsmasq_configure() {
+ global $config, $g;
+
+ /* kill any running dnsmasq */
+ sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
+
+ if (isset($config['dnsmasq']['enable'])) {
+
+ if ($g['booting'])
+ echo "Starting DNS forwarder... ";
+ else
+ sleep(1);
+
+ /* generate hosts file */
+ system_hosts_generate();
+
+ $args = "";
+
+ if (isset($config['dnsmasq']['regdhcp'])) {
+
+ $args .= " -l {$g['vardb_path']}/dhcpd.leases" .
+ " -s {$config['system']['domain']}";
+ }
+
+ /* run dnsmasq */
+ mwexec("/usr/local/sbin/dnsmasq {$args}");
+
+ if ($g['booting'])
+ echo "done\n";
+ }
+
+ if (!$g['booting']) {
+ services_dhcpd_configure();
+ }
+
+ return 0;
+}
+
+function services_snmpd_configure() {
+ global $config, $g;
+
+ /* kill any running snmpd */
+ sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
+
+ if (isset($config['snmpd']['enable'])) {
+
+ if ($g['booting'])
+ echo "Starting SNMP agent... ";
+
+ /* generate snmpd.conf */
+ $fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open snmpd.conf in services_snmpd_configure().\n");
+ return 1;
+ }
+
+ $snmpdconf = <<<EOD
+syslocation "{$config['snmpd']['syslocation']}"
+syscontact "{$config['snmpd']['syscontact']}"
+rocommunity "{$config['snmpd']['rocommunity']}"
+
+EOD;
+
+ fwrite($fd, $snmpdconf);
+ fclose($fd);
+
+ /* run snmpd */
+ mwexec("/usr/local/sbin/snmpd -c {$g['varetc_path']}/snmpd.conf" .
+ " -P {$g['varrun_path']}/snmpd.pid");
+
+ if ($g['booting'])
+ echo "done\n";
+ }
+
+ return 0;
+}
+
+function services_proxyarp_configure() {
+ global $config, $g;
+
+ /* kill any running choparp */
+ killbyname("choparp");
+
+ if (is_array($config['proxyarp']) && count($config['proxyarp']) &&
+ (is_ipaddr($config['interfaces']['wan']['ipaddr']) ||
+ ($config['interfaces']['wan']['ipaddr'] == "dhcp") ||
+ ($config['interfaces']['wan']['ipaddr'] == "bigpond"))) {
+
+ $args = $config['interfaces']['wan']['if'] . " auto";
+
+ foreach ($config['proxyarp']['proxyarpnet'] as $paent) {
+ if (isset($paent['network']))
+ $args .= " " . escapeshellarg($paent['network']);
+ else if (isset($paent['range']))
+ $args .= " " . escapeshellarg($paent['range']['from'] . "-" .
+ $paent['range']['to']);
+ }
+
+ mwexec_bg("/usr/local/sbin/choparp " . $args);
+ }
+}
+
+?>
diff --git a/etc/inc/shaper.inc b/etc/inc/shaper.inc
new file mode 100644
index 0000000..71e0575
--- /dev/null
+++ b/etc/inc/shaper.inc
@@ -0,0 +1,403 @@
+<?php
+/*
+ shaper.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("functions.inc");
+
+function shaper_configure() {
+ global $config, $g;
+
+ if (isset($config['pfqueueing']['enable'])) {
+
+ if ($g['booting'])
+ echo "Starting traffic shaper... ";
+
+ /* generate shaper rules */
+ $shaperrules = shaper_rules_generate();
+
+ /* make sure ipfw and dummynet are loaded */
+ mwexec("/sbin/kldload ipfw");
+ mwexec("/sbin/kldload dummynet");
+
+ /* change one_pass to 1 so ipfw stops checking after
+ a rule has matched */
+ mwexec("/sbin/sysctl net.inet.ip.fw.one_pass=1");
+
+ /* load shaper rules */
+ mwexec("/sbin/ipfw -f delete set 4");
+ mwexec("/sbin/ipfw -f pipe flush");
+
+ /* XXX - seems like ipfw cannot accept rules directly on stdin,
+ so we have to write them to a temporary file first */
+ $fd = fopen("{$g['tmp_path']}/ipfw.rules", "w");
+ if (!$fd) {
+ printf("Cannot open ipfw.rules in shaper_configure()\n");
+ return 1;
+ }
+
+ fwrite($fd, $shaperrules);
+ fclose($fd);
+
+ mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.rules");
+
+ unlink("{$g['tmp_path']}/ipfw.rules");
+
+ /* make sure bridged packets are shaped as well */
+ mwexec("/sbin/sysctl net.link.ether.bridge_ipfw=1");
+
+ if ($g['booting'])
+ echo "done\n";
+
+ } else {
+ mwexec("/sbin/sysctl net.link.ether.bridge_ipfw=0");
+ if (!isset($config['captiveportal']['enable'])) {
+ /* unload ipfw and dummynet */
+ #mwexec("/sbin/kldunload dummynet");
+ #mwexec("/sbin/kldunload ipfw");
+ } else {
+ /* captive portal is on - just remove our rules */
+ mwexec("/sbin/ipfw -f delete set 4");
+ mwexec("/sbin/ipfw -f pipe flush");
+ }
+ }
+
+ return 0;
+}
+
+function shaper_rules_generate() {
+ global $config, $g;
+
+ $wancfg = $config['interfaces']['wan'];
+ $lancfg = $config['interfaces']['lan'];
+ $pptpdcfg = $config['pptpd'];
+
+ $lanif = $lancfg['if'];
+ $wanif = get_real_wan_interface();
+
+ $lanip = $lancfg['ipaddr'];
+ $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']);
+ $lansn = $lancfg['subnet'];
+
+ /* optional interfaces */
+ $optcfg = array();
+
+ for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
+ $oc = $config['interfaces']['opt' . $i];
+
+ if (isset($oc['enable']) && $oc['if']) {
+ $oic = array();
+ $oic['ip'] = $oc['ipaddr'];
+ $oic['if'] = $oc['if'];
+ $oic['sa'] = gen_subnet($oc['ipaddr'], $oc['subnet']);
+ $oic['sn'] = $oc['subnet'];
+
+ $optcfg['opt' . $i] = $oic;
+ }
+ }
+
+ if ($pptpdcfg['mode'] == "server") {
+ $pptpip = $pptpdcfg['localip'];
+ $pptpsa = $pptpdcfg['remoteip'];
+ $pptpsn = $g['pptp_subnet'];
+ }
+
+ $rulei = 50000;
+
+ /* add a rule to pass all traffic from/to the firewall,
+ so the user cannot lock himself out of the webGUI */
+ $shaperrules = "add $rulei set 4 pass all from $lanip to any\n"; $rulei++;
+ $shaperrules .= "add $rulei set 4 pass all from any to $lanip\n"; $rulei++;
+
+ /* generate rules */
+ if (isset($config['pfqueueing']['rule']))
+ foreach ($config['pfqueueing']['rule'] as $rule) {
+
+ /* don't include disabled rules */
+ if (isset($rule['disabled'])) {
+ $i++;
+ continue;
+ }
+
+ /* does the rule deal with a PPTP interface? */
+ if ($rule['interface'] == "pptp") {
+
+ if ($pptpdcfg['mode'] != "server") {
+ $i++;
+ continue;
+ }
+
+ $nif = $g['n_pptp_units'];
+ $ispptp = true;
+ } else {
+
+ if (strstr($rule['interface'], "opt")) {
+ if (!array_key_exists($rule['interface'], $optcfg)) {
+ $i++;
+ continue;
+ }
+ }
+
+ $nif = 1;
+ $ispptp = false;
+ }
+
+ if ($pptpdcfg['mode'] != "server") {
+ if (($rule['source']['network'] == "pptp") ||
+ ($rule['destination']['network'] == "pptp")) {
+ $i++;
+ continue;
+ }
+ }
+
+ if (strstr($rule['source']['network'], "opt")) {
+ if (!array_key_exists($rule['source']['network'], $optcfg)) {
+ $i++;
+ continue;
+ }
+ }
+ if (strstr($rule['destination']['network'], "opt")) {
+ if (!array_key_exists($rule['destination']['network'], $optcfg)) {
+ $i++;
+ continue;
+ }
+ }
+
+ /* check for unresolvable aliases */
+ if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) {
+ $i++;
+ continue;
+ }
+ if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) {
+ $i++;
+ continue;
+ }
+
+ for ($iif = 0; $iif < $nif; $iif++) {
+
+ /* pipe or queue? */
+ if (isset($rule['targetpipe']) && isset($config['pfqueueing']['pipe'][$rule['targetpipe']])) {
+ $pipen = $rule['targetpipe'] + 1;
+ $line = "add $rulei set 4 pipe $pipen "; $rulei++;
+ } else if (isset($rule['targetqueue']) && isset($config['pfqueueing']['queue'][$rule['targetqueue']])) {
+ $queuen = $rule['targetqueue'] + 1;
+ $line = "add $rulei set 4 queue $queuen "; $rulei++;
+ } else {
+ printf("Neither existing pipe nor queue found in rule $i\n");
+ break;
+ }
+
+ if (isset($rule['protocol'])) {
+ $line .= "{$rule['protocol']} ";
+ } else {
+ $line .= "all ";
+ }
+
+ /* source address */
+ if (isset($rule['source']['any'])) {
+ $src = "any";
+ } else if ($rule['source']['network']) {
+
+ if (strstr($rule['source']['network'], "opt")) {
+ $src = $optcfg[$rule['source']['network']]['sa'] . "/" .
+ $optcfg[$rule['source']['network']]['sn'];
+ } else {
+ switch ($rule['source']['network']) {
+ case 'lan':
+ $src = "$lansa/$lansn";
+ break;
+ case 'pptp':
+ $src = "$pptpsa/$pptpsn";
+ break;
+ }
+ }
+ } else if ($rule['source']['address']) {
+ $src = alias_expand($rule['source']['address']);
+ }
+
+ if (!$src) {
+ printf("No source address found in rule $i\n");
+ break;
+ }
+
+ if (isset($rule['source']['not'])) {
+ $line .= "from not $src ";
+ } else {
+ $line .= "from $src ";
+ }
+
+ if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
+
+ if ($rule['source']['port']) {
+ $srcport = explode("-", $rule['source']['port']);
+
+ if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) {
+ $line .= "{$srcport[0]} ";
+ } else {
+ $line .= "{$srcport[0]}-{$srcport[1]} ";
+ }
+ }
+ }
+
+ /* destination address */
+ if (isset($rule['destination']['any'])) {
+ $dst = "any";
+ } else if ($rule['destination']['network']) {
+
+ if (strstr($rule['destination']['network'], "opt")) {
+ $dst = $optcfg[$rule['destination']['network']]['sa'] . "/" .
+ $optcfg[$rule['destination']['network']]['sn'];
+ } else {
+ switch ($rule['destination']['network']) {
+ case 'lan':
+ $dst = "$lansa/$lansn";
+ break;
+ case 'pptp':
+ $dst = "$pptpsa/$pptpsn";
+ break;
+ }
+ }
+ } else if ($rule['destination']['address']) {
+ $dst = alias_expand($rule['destination']['address']);
+ }
+
+ if (!$dst) {
+ printf("No destination address found in rule $i\n");
+ break;
+ }
+
+ if (isset($rule['destination']['not'])) {
+ $line .= "to not $dst ";
+ } else {
+ $line .= "to $dst ";
+ }
+
+ if (!isset($rule['protocol']) || in_array($rule['protocol'], array("tcp","udp"))) {
+
+ if ($rule['destination']['port']) {
+ $dstport = explode("-", $rule['destination']['port']);
+
+ if ((!$dstport[1]) || ($dstport[0] == $dstport[1])) {
+ $line .= "{$dstport[0]} ";
+ } else {
+ $line .= "{$dstport[0]}-{$dstport[1]} ";
+ }
+ }
+ }
+
+ if ($rule['iplen'])
+ $line .= "iplen {$rule['iplen']} ";
+
+ if ($rule['iptos'])
+ $line .= "iptos {$rule['iptos']} ";
+
+ if ($rule['tcpflags'])
+ $line .= "tcpflags {$rule['tcpflags']} ";
+
+ if ($rule['direction'] == "in")
+ $line .= "in ";
+ else if ($rule['direction'] == "out")
+ $line .= "out ";
+
+ if ($ispptp) {
+ $line .= "via ng" . ($iif+1);
+ } else {
+ if ($rule['interface'] == "wan")
+ $if = $wanif;
+ else
+ $if = $config['interfaces'][$rule['interface']]['if'];
+
+ $line .= "via {$if}";
+ }
+
+ $line .= "\n";
+ $shaperrules .= $line;
+ }
+
+ $i++;
+ }
+
+ /* generate pipes */
+ if (isset($config['pfqueueing']['pipe'])) {
+ $pipei = 1;
+ foreach ($config['pfqueueing']['pipe'] as $pipe) {
+ $line = "pipe $pipei config bw {$pipe['bandwidth']}Kbit/s ";
+
+ if ($pipe['delay']) {
+ $line .= "delay {$pipe['delay']} ";
+ }
+
+ switch ($pipe['mask']) {
+ case 'source':
+ $line .= "mask src-ip 0xffffffff ";
+ break;
+ case 'destination':
+ $line .= "mask dst-ip 0xffffffff ";
+ break;
+ }
+
+ $line .= "\n";
+ $shaperrules .= $line;
+ $pipei++;
+ }
+ }
+
+ /* generate queues */
+ if (isset($config['pfqueueing']['queue'])) {
+ $queuei = 1;
+ foreach ($config['pfqueueing']['queue'] as $queue) {
+
+ $pipen = $queue['targetpipe'] + 1;
+ if (!isset($queue['targetpipe']) || !isset($config['pfqueueing']['pipe'][$queue['targetpipe']])) {
+ printf("Pipe $pipen for queue $queuei not found!\n");
+ continue;
+ }
+
+ $line = "queue $queuei config pipe {$pipen}";
+ $line .= " weight {$queue['weight']}";
+
+ switch ($queue['mask']) {
+ case 'source':
+ $line .= " mask src-ip 0xffffffff ";
+ break;
+ case 'destination':
+ $line .= " mask dst-ip 0xffffffff ";
+ break;
+ }
+
+ $line .= "\n";
+ $shaperrules .= $line;
+ $queuei++;
+ }
+ }
+
+ return $shaperrules;
+}
+
+?>
diff --git a/etc/inc/system.inc b/etc/inc/system.inc
new file mode 100644
index 0000000..d2c0b33
--- /dev/null
+++ b/etc/inc/system.inc
@@ -0,0 +1,563 @@
+<?php
+/*
+ system.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("functions.inc");
+
+function system_resolvconf_generate($dynupdate = false) {
+ global $config, $g;
+
+ $syscfg = $config['system'];
+
+ $fd = fopen("{$g['varetc_path']}/resolv.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open resolv.conf in system_resolvconf_generate().\n");
+ return 1;
+ }
+
+ $resolvconf = "domain {$syscfg['domain']}\n";
+
+ $havedns = false;
+
+ if (isset($syscfg['dnsallowoverride'])) {
+ /* get dynamically assigned DNS servers (if any) */
+ $nfd = @fopen("{$g['varetc_path']}/nameservers.conf", "r");
+ if ($nfd) {
+ while (!feof($nfd)) {
+ $dnss = trim(fgets($nfd));
+ if ($dnss) {
+ $resolvconf .= "nameserver $dnss\n";
+ $havedns = true;
+ }
+ }
+ fclose($nfd);
+ }
+ }
+ if (!$havedns && is_array($syscfg['dnsserver'])) {
+ foreach ($syscfg['dnsserver'] as $ns) {
+ if ($ns)
+ $resolvconf .= "nameserver $ns\n";
+ $havedns = true;
+ }
+ }
+
+ fwrite($fd, $resolvconf);
+ fclose($fd);
+
+ if (!$g['booting']) {
+ /* restart dhcpd (nameservers may have changed) */
+ if (!$dynupdate)
+ services_dhcpd_configure();
+ }
+
+ return 0;
+}
+
+function system_hosts_generate() {
+ global $config, $g;
+
+ $syscfg = $config['system'];
+ $lancfg = $config['interfaces']['lan'];
+ $dnsmasqcfg = $config['dnsmasq'];
+
+ if (!is_array($dnsmasqcfg['hosts'])) {
+ $dnsmasqcfg['hosts'] = array();
+ }
+ $hostscfg = $dnsmasqcfg['hosts'];
+
+ $fd = fopen("{$g['varetc_path']}/hosts", "w");
+ if (!$fd) {
+ printf("Error: cannot open hosts file in system_hosts_generate().\n");
+ return 1;
+ }
+
+ $hosts = <<<EOD
+127.0.0.1 localhost localhost.{$syscfg['domain']}
+{$lancfg['ipaddr']} {$syscfg['hostname']}.{$syscfg['domain']} {$syscfg['hostname']}
+
+EOD;
+
+ foreach ($hostscfg as $host) {
+ if ($host['host'])
+ $hosts .= "{$host['ip']} {$host['host']}.{$host['domain']} {$host['host']}\n";
+ else
+ $hosts .= "{$host['ip']} {$host['domain']}\n";
+ }
+ fwrite($fd, $hosts);
+ fclose($fd);
+
+ return 0;
+}
+
+function system_hostname_configure() {
+ global $config, $g;
+
+ $syscfg = $config['system'];
+
+ /* set hostname */
+ return mwexec("/bin/hostname " .
+ escapeshellarg("{$syscfg['hostname']}.{$syscfg['domain']}"));
+}
+
+function system_routing_configure() {
+ global $config, $g;
+
+ /* clear out old routes, if necessary */
+ if (file_exists("{$g['vardb_path']}/routes.db")) {
+ $fd = fopen("{$g['vardb_path']}/routes.db", "r");
+ if (!$fd) {
+ printf("Error: cannot open routes DB file in system_routing_configure().\n");
+ return 1;
+ }
+ while (!feof($fd)) {
+ $oldrt = fgets($fd);
+ if ($oldrt)
+ mwexec("/sbin/route delete " . escapeshellarg($oldrt));
+ }
+ fclose($fd);
+ unlink("{$g['vardb_path']}/routes.db");
+ }
+
+ if (is_array($config['staticroutes']['route'])) {
+
+ $fd = fopen("{$g['vardb_path']}/routes.db", "w");
+ if (!$fd) {
+ printf("Error: cannot open routes DB file in system_routing_configure().\n");
+ return 1;
+ }
+
+ foreach ($config['staticroutes']['route'] as $rtent) {
+ mwexec("/sbin/route add " . escapeshellarg($rtent['network']) .
+ " " . escapeshellarg($rtent['gateway']));
+
+ /* record route so it can be easily removed later (if necessary) */
+ fwrite($fd, $rtent['network'] . "\n");
+ }
+
+ fclose($fd);
+ }
+
+ return 0;
+}
+
+function system_routing_enable() {
+ global $config, $g;
+
+ return mwexec("/sbin/sysctl net.inet.ip.forwarding=1");
+}
+
+function system_syslogd_start() {
+ global $config, $g;
+
+ $syslogcfg = $config['syslog'];
+
+ if ($g['booting'])
+ echo "Starting syslog service... ";
+ else
+ killbypid("{$g['varrun_path']}/syslog.pid");
+
+ if (isset($syslogcfg['enable'])) {
+
+ /* write syslog.conf */
+ $fd = fopen("{$g['varetc_path']}/syslog.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open syslog.conf in system_syslogd_start().\n");
+ return 1;
+ }
+
+ $syslogconf = <<<EOD
+local0.* %/var/log/filter.log
+local3.* %/var/log/vpn.log
+local7.* %/var/log/dhcpd.log
+*.notice;kern.debug;lpr.info;mail.crit;news.err;local0.none;local3.none;local7.none %/var/log/system.log
+security.* %/var/log/system.log
+auth.info;authpriv.info;daemon.info %/var/log/system.log
+*.emerg *
+
+EOD;
+
+ if (isset($syslogcfg['filter'])) {
+ $syslogconf .= <<<EOD
+local0.* @{$syslogcfg['remoteserver']}
+
+EOD;
+ }
+
+ if (isset($syslogcfg['vpn'])) {
+ $syslogconf .= <<<EOD
+local3.* @{$syslogcfg['remoteserver']}
+
+EOD;
+ }
+
+ if (isset($syslogcfg['dhcp'])) {
+ $syslogconf .= <<<EOD
+local7.* @{$syslogcfg['remoteserver']}
+
+EOD;
+ }
+
+ if (isset($syslogcfg['system'])) {
+ $syslogconf .= <<<EOD
+*.notice;kern.debug;lpr.info;mail.crit;news.err;local0.none;local7.none @{$syslogcfg['remoteserver']}
+security.* @{$syslogcfg['remoteserver']}
+auth.info;authpriv.info;daemon.info @{$syslogcfg['remoteserver']}
+*.emerg @{$syslogcfg['remoteserver']}
+
+EOD;
+ }
+
+ fwrite($fd, $syslogconf);
+ fclose($fd);
+
+ $retval = mwexec("/usr/sbin/syslogd -s -f {$g['varetc_path']}/syslog.conf");
+
+ } else {
+ $retval = mwexec("/usr/sbin/syslogd -ss");
+ }
+
+ if ($g['booting'])
+ echo "done\n";
+
+ return $retval;
+}
+
+function system_pccard_start() {
+ global $config, $g;
+
+ if ($g['booting'])
+ echo "Initializing PC cards... ";
+
+ /* kill any running pccardd */
+ killbypid("{$g['varrun_path']}/pccardd.pid");
+
+ /* fire up pccardd */
+ $res = mwexec("/usr/sbin/pccardd -z -f {$g['etc_path']}/pccard.conf");
+
+ if ($g['booting']) {
+ if ($res == 0)
+ echo "done\n";
+ else
+ echo "failed (probably no PC card controller present)\n";
+ }
+
+ return $res;
+}
+
+function system_webgui_start() {
+ global $config, $g;
+
+ if ($g['booting'])
+ echo "Starting webGUI... ";
+
+ /* kill any running mini_httpd */
+ killbypid("{$g['varrun_path']}/mini_httpd.pid");
+
+ /* generate password file */
+ system_password_configure();
+
+ chdir($g['www_path']);
+
+ /* non-standard port? */
+ if ($config['system']['webgui']['port'])
+ $portarg = "-p {$config['system']['webgui']['port']}";
+ else
+ $portarg = "";
+
+ if ($config['system']['webgui']['protocol'] == "https") {
+
+ if ($config['system']['webgui']['certificate'] && $config['system']['webgui']['private-key']) {
+ $cert = base64_decode($config['system']['webgui']['certificate']);
+ $key = base64_decode($config['system']['webgui']['private-key']);
+ } else {
+ /* default certificate/key */
+ $cert = <<<EOD
+-----BEGIN CERTIFICATE-----
+MIIBlDCB/gIBADANBgkqhkiG9w0BAQQFADATMREwDwYDVQQKEwhtMG4wd2FsbDAe
+Fw0wMzA5MDgxNzAzNDZaFw0wNDA5MDcxNzAzNDZaMBMxETAPBgNVBAoTCG0wbjB3
+YWxsMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAShszhFz+o8lsMWTGgTxs
+TMPR+v4+qL5jXDyY97MLTGFK7aqQOtpIQc+TcTc4jklgOVlHoR7oBXrsi8YrbCd+
+83LPQmQoSPC0VqhfU3uYf3NzxiK8r97aPCsmWgwT2pQ6TcESTm6sF7nLprOf/zFP
+C4jE2fvjkbzyVolPywBuewIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAK2D8NqQSlUs
+pFCe5J9ue1LrjfGHHy4HE9zA9avgrz3Qju+1JOshEwy/1BJjZ93tQUbiRS7RwvDO
+4crGG4IejjhFczzA2CIX3rd2rYM2oGpojKgm5YuuhV5lYPwAHUOLbBaLOVqlLhzw
+VqjD7R2DkXUIfhJ5ZekqK5ZwzqJXta8U
+-----END CERTIFICATE-----
+
+EOD;
+
+ $key = <<<EOD
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDAShszhFz+o8lsMWTGgTxsTMPR+v4+qL5jXDyY97MLTGFK7aqQ
+OtpIQc+TcTc4jklgOVlHoR7oBXrsi8YrbCd+83LPQmQoSPC0VqhfU3uYf3NzxiK8
+r97aPCsmWgwT2pQ6TcESTm6sF7nLprOf/zFPC4jE2fvjkbzyVolPywBuewIDAQAB
+AoGAbJJrQW9fQrggJuLMz/hwsYW2m31oyOBmf5u463YQtjRuSuxe/gj87weZuNqY
+H2rXq2k2K+ehl8hgW+egASyUL3L7kCkEAsVREujKTEyhSqqIRDPWTxo9S/YA9Gvn
+2ZnJvkrcKjqCO9aHX3rvJOK/ErYI6akctgI3KmgkYw5XNmECQQDuZU97RTWH9rmP
+aQr57ysNXxgFsyhetOOqeYkPtIVwpOiNbfwE1zi5RGdtO4Ku3fG1lV4J2UoWJ9yD
+awdoyYIHAkEAzn0xJ90IjPsHk+8SODEj5JGdHSZPNu1tgtrbjEi9sfGWg4K7XTxr
+QW90pWb1bKKU1uh5FzW6OhnFfuQXt1kC7QJAPSthqY+onKqCEnoxhtAHi/bKgyvl
+P+fKQwPMV2tKkgy+XwvJjrRqqZ8TqsOKVLQ+QQmCh6RpjiXMPyxHSmvqIQJBAKLR
+HF1ucDuaBROkwx0DwmWMW/KMLpIFDQDNSaiIAuu4rxHrl4mhBoGGPNffI04RtILw
+s+qVNs5xW8T+XaT4ztECQQDFHPnZeoPWE5z+AX/UUQIUWaDExz3XRzmIxRbOrlFi
+CsF1s0TdJLi/wzNQRAL37A8vqCeVFR/ng3Xpg96Yg+8Z
+-----END RSA PRIVATE KEY-----
+
+EOD;
+ }
+
+ $fd = fopen("{$g['varetc_path']}/cert.pem", "w");
+ if (!$fd) {
+ printf("Error: cannot open cert.pem in system_webgui_start().\n");
+ return 1;
+ }
+ chmod("{$g['varetc_path']}/cert.pem", 0600);
+ fwrite($fd, $cert);
+ fwrite($fd, "\n");
+ fwrite($fd, $key);
+ fclose($fd);
+
+ $res = mwexec("/usr/local/sbin/mini_httpd -S -E {$g['varetc_path']}/cert.pem" .
+ " -c \"**.php|**.cgi\" -u root -maxproc 16 $portarg" .
+ " -i {$g['varrun_path']}/mini_httpd.pid");
+ } else {
+ $res = mwexec("/usr/local/sbin/mini_httpd -c \"**.php|**.cgi\" -u root" .
+ " -maxproc 16 $portarg -i {$g['varrun_path']}/mini_httpd.pid");
+ }
+
+ if ($g['booting']) {
+ if ($res == 0)
+ echo "done\n";
+ else
+ echo "failed\n";
+ }
+
+ return $res;
+}
+
+function system_password_configure() {
+ global $config, $g;
+
+ $fd = fopen("{$g['varrun_path']}/htpasswd", "w");
+ if (!$fd) {
+ printf("Error: cannot open htpasswd in system_password_configure().\n");
+ return 1;
+ }
+
+ if ($config['system']['username'])
+ $username = $config['system']['username'];
+ else
+ $username = "admin";
+
+ fwrite($fd, $username . ":" . $config['system']['password'] . "\n");
+ fclose($fd);
+ chmod("{$g['varrun_path']}/htpasswd", 0600);
+
+ return 0;
+}
+
+function system_timezone_configure() {
+ global $config, $g;
+
+ $syscfg = $config['system'];
+
+ if ($g['booting'])
+ echo "Initializing timezone... ";
+
+ /* extract appropriate timezone file */
+ $timezone = $syscfg['timezone'];
+ if (!$timezone)
+ $timezone = "Etc/UTC";
+
+ exec("/usr/bin/tar xzfO /usr/share/zoneinfo.tgz " .
+ escapeshellarg($timezone) . " > /etc/localtime");
+
+ if ($g['booting'])
+ echo "done\n";
+}
+
+function system_ntp_configure() {
+ global $config, $g;
+
+ $syscfg = $config['system'];
+
+ if ($g['booting'])
+ echo "Starting NTP client... ";
+ else {
+ killbypid("{$g['varrun_path']}/runmsntp.pid");
+ killbypid("{$g['varrun_path']}/msntp.pid");
+ }
+
+ /* start ntp client if needed - needs to be forced into background */
+ $updateinterval = $syscfg['time-update-interval'];
+
+ if ($updateinterval > 0) {
+ if ($updateinterval < 6)
+ $updateinterval = 6;
+
+ $timeservers = "";
+ foreach (explode(' ', $syscfg['timeservers']) as $ts)
+ $timeservers .= " " . $ts;
+
+ mwexec_bg("/usr/local/bin/runmsntp.sh " .
+ escapeshellarg("{$g['varrun_path']}/runmsntp.pid") . " " .
+ escapeshellarg("{$g['varrun_path']}/msntp.pid") . " " .
+ escapeshellarg($updateinterval) . " " .
+ escapeshellarg($timeservers));
+ }
+
+ if ($g['booting'])
+ echo "done\n";
+}
+
+function system_reboot() {
+ global $g;
+
+ system_reboot_cleanup();
+
+ mwexec("nohup /etc/rc.reboot > /dev/null 2>&1 &");
+}
+
+function system_reboot_sync() {
+ global $g;
+
+ system_reboot_cleanup();
+
+ mwexec("/etc/rc.reboot > /dev/null 2>&1");
+}
+
+function system_reboot_cleanup() {
+ captiveportal_radius_stop_all();
+}
+
+function system_do_shell_commands($early = 0) {
+ global $config, $g;
+
+ if ($early)
+ $cmdn = "earlyshellcmd";
+ else
+ $cmdn = "shellcmd";
+
+ if (is_array($config['system'][$cmdn])) {
+
+ foreach ($config['system'][$cmdn] as $cmd) {
+ exec($cmd);
+ }
+ }
+}
+
+function system_do_extensions() {
+ global $config, $g;
+
+ if (!is_dir("{$g['etc_path']}/inc/ext"))
+ return;
+
+ $dh = @opendir("{$g['etc_path']}/inc/ext");
+ if ($dh) {
+ while (($extd = readdir($dh)) !== false) {
+ if (($extd === ".") || ($extd === ".."))
+ continue;
+ $rcfile = "{$g['etc_path']}/inc/ext/" . $extd . "/rc";
+ if (file_exists($rcfile))
+ passthru($rcfile);
+ }
+ closedir($dh);
+ }
+}
+
+function system_console_configure() {
+ global $config, $g;
+
+ if (isset($config['system']['disableconsolemenu'])) {
+ touch("{$g['varetc_path']}/disableconsole");
+ } else {
+ unlink_if_exists("{$g['varetc_path']}/disableconsole");
+ }
+}
+
+function system_dmesg_save() {
+ global $g;
+
+ exec("/sbin/dmesg", $dmesg);
+
+ /* find last copyright line (output from previous boots may be present) */
+ $lastcpline = 0;
+
+ for ($i = 0; $i < count($dmesg); $i++) {
+ if (strstr($dmesg[$i], "Copyright (c) 1992-"))
+ $lastcpline = $i;
+ }
+
+ $fd = fopen("{$g['varlog_path']}/dmesg.boot", "w");
+ if (!$fd) {
+ printf("Error: cannot open dmesg.boot in system_dmesg_save().\n");
+ return 1;
+ }
+
+ for ($i = $lastcpline; $i < count($dmesg); $i++)
+ fwrite($fd, $dmesg[$i] . "\n");
+
+ fclose($fd);
+
+ return 0;
+}
+
+function system_set_harddisk_standby() {
+ global $g, $config;
+
+ if ($g['platform'] != "generic-pc")
+ return;
+
+ if (isset($config['system']['harddiskstandby'])) {
+ if ($g['booting']) {
+ echo 'Setting harddisk standby time... ';
+ }
+
+ $standby = $config['system']['harddiskstandby'];
+ // Check for a numeric value
+ if (is_numeric($standby)) {
+ // Sync the disk(s)
+ mwexec('/bin/sync');
+ if (!mwexec('/sbin/sysctl hw.ata.standby=' . ((int)$standby))) {
+ // Reinitialize ATA-drives
+ mwexec('/usr/local/sbin/atareinit');
+ if ($g['booting']) {
+ echo "done\n";
+ }
+ } else if ($g['booting']) {
+ echo "failed\n";
+ }
+ } else if ($g['booting']) {
+ echo "failed\n";
+ }
+ }
+}
+
+?>
diff --git a/etc/inc/util.inc b/etc/inc/util.inc
new file mode 100644
index 0000000..2b3fa67
--- /dev/null
+++ b/etc/inc/util.inc
@@ -0,0 +1,421 @@
+<?php
+/*
+ util.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* kill a process by pid file */
+function killbypid($pidfile) {
+ sigkillbypid($pidfile, "TERM");
+}
+
+/* sigkill a process by pid file */
+function sigkillbypid($pidfile, $sig) {
+ if (file_exists($pidfile)) {
+ mwexec("/bin/kill -s $sig `/bin/cat " . $pidfile . "`");
+ }
+}
+
+/* kill a process by name */
+function killbyname($procname) {
+ mwexec("/usr/bin/killall " . escapeshellarg($procname));
+}
+
+/* return the subnet address given a host address and a subnet bit count */
+function gen_subnet($ipaddr, $bits) {
+ if (!is_ipaddr($ipaddr) || !is_numeric($bits))
+ return "";
+
+ return long2ip(ip2long($ipaddr) & gen_subnet_mask_long($bits));
+}
+
+/* return the highest (broadcast) address in the subnet given a host address and a subnet bit count */
+function gen_subnet_max($ipaddr, $bits) {
+ if (!is_ipaddr($ipaddr) || !is_numeric($bits))
+ return "";
+
+ return long2ip(ip2long($ipaddr) | ~gen_subnet_mask_long($bits));
+}
+
+/* returns a subnet mask (long given a bit count) */
+function gen_subnet_mask_long($bits) {
+ $sm = 0;
+ for ($i = 0; $i < $bits; $i++) {
+ $sm >>= 1;
+ $sm |= 0x80000000;
+ }
+ return $sm;
+}
+
+/* same as above but returns a string */
+function gen_subnet_mask($bits) {
+ return long2ip(gen_subnet_mask_long($bits));
+}
+
+function is_numericint($arg) {
+ return (preg_match("/[^0-9]/", $arg) ? false : true);
+}
+
+/* returns true if $ipaddr is a valid dotted IPv4 address */
+function is_ipaddr($ipaddr) {
+ if (!is_string($ipaddr))
+ return false;
+
+ $ip_long = ip2long($ipaddr);
+ $ip_reverse = long2ip($ip_long);
+
+ if ($ipaddr == $ip_reverse)
+ return true;
+ else
+ return false;
+}
+
+/* returns true if $ipaddr is a valid dotted IPv4 address or an alias thereof */
+function is_ipaddroralias($ipaddr) {
+
+ global $aliastable;
+
+ if (isset($aliastable[$ipaddr]) && is_ipaddr($aliastable[$ipaddr]))
+ return true;
+ else
+ return is_ipaddr($ipaddr);
+}
+
+/* returns true if $ipaddr is a valid dotted IPv4 address or any alias */
+function is_ipaddroranyalias($ipaddr) {
+
+ global $aliastable;
+
+ if (isset($aliastable[$ipaddr]))
+ return true;
+ else
+ return is_ipaddr($ipaddr);
+}
+
+/* returns true if $subnet is a valid subnet in CIDR format */
+function is_subnet($subnet) {
+ if (!is_string($subnet))
+ return false;
+
+ list($hp,$np) = explode('/', $subnet);
+
+ if (!is_ipaddr($hp))
+ return false;
+
+ if (!is_numeric($np) || ($np < 1) || ($np > 32))
+ return false;
+
+ return true;
+}
+
+/* returns true if $subnet is a valid subnet in CIDR format or an alias thereof */
+function is_subnetoralias($subnet) {
+
+ global $aliastable;
+
+ if (isset($aliastable[$subnet]) && is_subnet($aliastable[$subnet]))
+ return true;
+ else
+ return is_subnet($subnet);
+}
+
+/* returns true if $hostname is a valid hostname */
+function is_hostname($hostname) {
+ if (!is_string($hostname))
+ return false;
+
+ if (preg_match("/^[a-z0-9\-]+$/i", $hostname))
+ return true;
+ else
+ return false;
+}
+
+/* returns true if $domain is a valid domain name */
+function is_domain($domain) {
+ if (!is_string($domain))
+ return false;
+
+ if (preg_match("/^([a-z0-9\-]+\.?)*$/i", $domain))
+ return true;
+ else
+ return false;
+}
+
+/* returns true if $uname is a valid DynDNS username */
+function is_dyndns_username($uname) {
+ if (!is_string($uname))
+ return false;
+
+ if (preg_match("/[^a-z0-9\-.@_]/i", $uname))
+ return false;
+ else
+ return true;
+}
+
+/* returns true if $macaddr is a valid MAC address */
+function is_macaddr($macaddr) {
+ if (!is_string($macaddr))
+ return false;
+
+ $maca = explode(":", $macaddr);
+ if (count($maca) != 6)
+ return false;
+
+ foreach ($maca as $macel) {
+ if (($macel === "") || (strlen($macel) > 2))
+ return false;
+ if (preg_match("/[^0-9a-f]/i", $macel))
+ return false;
+ }
+
+ return true;
+}
+
+/* returns true if $name is a valid name for an alias */
+function is_validaliasname($name) {
+ if (!preg_match("/[^a-zA-Z0-9]/", $name))
+ return true;
+ else
+ return false;
+}
+
+/* returns true if $port is a valid TCP/UDP port */
+function is_port($port) {
+ if (!is_numericint($port))
+ return false;
+
+ if (($port < 1) || ($port > 65535))
+ return false;
+ else
+ return true;
+}
+
+/* returns a list of interfaces with MAC addresses
+ (skips VLAN and other virtual interfaces) */
+function get_interface_list() {
+
+ global $g;
+
+ /* build interface list with netstat */
+ exec("/usr/bin/netstat -inW -f link", $linkinfo);
+ array_shift($linkinfo);
+
+ $iflist = array();
+
+ foreach ($linkinfo as $link) {
+ $alink = preg_split("/\s+/", $link);
+ $ifname = chop($alink[0]);
+
+ if (substr($ifname, -1) == "*")
+ $ifname = substr($ifname, 0, strlen($ifname) - 1);
+
+ if (!preg_match("/^(ppp|sl|gif|faith|lo|ng|vlan)/", $ifname)) {
+ $iflist[$ifname] = array();
+
+ $iflist[$ifname]['mac'] = chop($alink[3]);
+ $iflist[$ifname]['up'] = false;
+
+ /* find out if the link on this interface is up */
+ unset($ifinfo);
+ exec("/sbin/ifconfig {$ifname}", $ifinfo);
+
+ foreach ($ifinfo as $ifil) {
+ if (preg_match("/status: (.*)$/", $ifil, $matches)) {
+ if ($matches[1] == "active")
+ $iflist[$ifname]['up'] = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return $iflist;
+}
+
+/* wrapper for exec() */
+function mwexec($command) {
+
+ global $g;
+
+ if ($g['debug']) {
+ if (!$_SERVER['REMOTE_ADDR'])
+ echo "mwexec(): $command\n";
+ passthru($command, $retval);
+ } else {
+ exec("$command > /dev/null 2>&1", $oarr, $retval);
+ }
+
+ return $retval;
+}
+
+/* wrapper for exec() in background */
+function mwexec_bg($command) {
+
+ global $g;
+
+ if ($g['debug']) {
+ if (!$_SERVER['REMOTE_ADDR'])
+ echo "mwexec(): $command\n";
+ }
+
+ exec("nohup $command > /dev/null 2>&1 &");
+}
+
+/* unlink a file, if it exists */
+function unlink_if_exists($fn) {
+ if (file_exists($fn))
+ unlink($fn);
+}
+
+/* make a global alias table (for faster lookups) */
+function alias_make_table() {
+
+ global $config, $g, $aliastable;
+
+ $aliastable = array();
+
+ if (is_array($config['aliases']['alias'])) {
+ foreach ($config['aliases']['alias'] as $alias) {
+ if ($alias['name'])
+ $aliastable[$alias['name']] = $alias['address'];
+ }
+ }
+}
+
+/* check if an alias exists */
+function is_alias($name) {
+
+ global $aliastable;
+
+ return isset($aliastable[$name]);
+}
+
+/* expand a host or network alias, if necessary */
+function alias_expand($name) {
+
+ global $aliastable;
+
+ if (isset($aliastable[$name]))
+ return $aliastable[$name];
+ else if (is_ipaddr($name) || is_subnet($name))
+ return $name;
+ else
+ return null;
+}
+
+/* expand a host alias, if necessary */
+function alias_expand_host($name) {
+
+ global $aliastable;
+
+ if (isset($aliastable[$name]) && is_ipaddr($aliastable[$name]))
+ return $aliastable[$name];
+ else if (is_ipaddr($name))
+ return $name;
+ else
+ return null;
+}
+
+/* expand a network alias, if necessary */
+function alias_expand_net($name) {
+
+ global $aliastable;
+
+ if (isset($aliastable[$name]) && is_subnet($aliastable[$name]))
+ return $aliastable[$name];
+ else if (is_subnet($name))
+ return $name;
+ else
+ return null;
+}
+
+/* find out whether two subnets overlap */
+function check_subnets_overlap($subnet1, $bits1, $subnet2, $bits2) {
+
+ if (!is_numeric($bits1))
+ $bits1 = 32;
+ if (!is_numeric($bits2))
+ $bits2 = 32;
+
+ if ($bits1 < $bits2)
+ $relbits = $bits1;
+ else
+ $relbits = $bits2;
+
+ $sn1 = gen_subnet_mask_long($relbits) & ip2long($subnet1);
+ $sn2 = gen_subnet_mask_long($relbits) & ip2long($subnet2);
+
+ if ($sn1 == $sn2)
+ return true;
+ else
+ return false;
+}
+
+/* compare two IP addresses */
+function ipcmp($a, $b) {
+ if (ip2long($a) < ip2long($b))
+ return -1;
+ else if (ip2long($a) > ip2long($b))
+ return 1;
+ else
+ return 0;
+}
+
+/* return true if $addr is in $subnet, false if not */
+function ip_in_subnet($addr,$subnet) {
+ list($ip, $mask) = explode('/', $subnet);
+ $mask = 0xffffffff << (32 - $mask);
+ return ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
+}
+
+/* verify (and remove) the digital signature on a file - returns 0 if OK */
+function verify_digital_signature($fname) {
+
+ global $g;
+
+ return mwexec("/usr/local/bin/verifysig " .
+ escapeshellarg("{$g['etc_path']}/pubkey.pem") . " " .
+ escapeshellarg($fname));
+}
+
+/* obtain MAC address given an IP address by looking at the ARP table */
+function arp_get_mac_by_ip($ip) {
+ exec("/usr/sbin/arp -n {$ip}", $arpoutput);
+
+ if ($arpoutput[0]) {
+ $arpi = explode(" ", $arpoutput[0]);
+ $macaddr = $arpi[3];
+ if (is_macaddr($macaddr))
+ return $macaddr;
+ else
+ return false;
+ }
+
+ return false;
+}
+
+?>
diff --git a/etc/inc/vpn.inc b/etc/inc/vpn.inc
new file mode 100644
index 0000000..b73af46
--- /dev/null
+++ b/etc/inc/vpn.inc
@@ -0,0 +1,559 @@
+<?php
+/*
+ vpn.inc
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* include all configuration functions */
+require_once("functions.inc");
+
+function vpn_ipsec_configure($ipchg = false) {
+ global $config, $g;
+
+ $curwanip = get_current_wan_address();
+
+ $syscfg = $config['system'];
+ $ipseccfg = $config['ipsec'];
+ $lancfg = $config['interfaces']['lan'];
+ $lanip = $lancfg['ipaddr'];
+ $lansa = gen_subnet($lancfg['ipaddr'], $lancfg['subnet']);
+ $lansn = $lancfg['subnet'];
+
+ if ($g['booting']) {
+ if (!isset($ipseccfg['enable']))
+ return 0;
+
+ echo "Configuring IPsec VPN... ";
+ } else {
+ /* kill racoon */
+ killbypid("{$g['varrun_path']}/racoon.pid");
+
+ /* wait for process to die */
+ sleep(2);
+
+ /* send a SIGKILL to be sure */
+ sigkillbypid("{$g['varrun_path']}/racoon.pid", "KILL");
+ }
+
+ /* flush SPD and SAD */
+ mwexec("/usr/sbin/setkey -FP");
+ mwexec("/usr/sbin/setkey -F");
+
+ /* prefer old SAs only for 30 seconds, then use the new one */
+ mwexec("/sbin/sysctl -w net.key.preferred_oldsa=-30");
+
+ if (isset($ipseccfg['enable'])) {
+
+ if (!$curwanip) {
+ /* IP address not configured yet, exit */
+ if ($g['booting'])
+ echo "done\n";
+ return 0;
+ }
+
+ if ((is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) ||
+ isset($ipseccfg['mobileclients']['enable'])) {
+
+ if (is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel'])) {
+
+ /* generate spd.conf */
+ $fd = fopen("{$g['varetc_path']}/spd.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open spd.conf in vpn_ipsec_configure().\n");
+ return 1;
+ }
+
+ $spdconf = "";
+
+ $spdconf .= "spdadd {$lansa}/{$lansn} {$lanip}/32 any -P in none;\n";
+ $spdconf .= "spdadd {$lanip}/32 {$lansa}/{$lansn} any -P out none;\n";
+
+ foreach ($ipseccfg['tunnel'] as $tunnel) {
+
+ if (isset($tunnel['disabled']))
+ continue;
+
+ $ep = vpn_endpoint_determine($tunnel, $curwanip);
+ if (!$ep)
+ continue;
+
+ vpn_localnet_determine($tunnel['local-subnet'], $sa, $sn);
+
+ $spdconf .= "spdadd {$sa}/{$sn} " .
+ "{$tunnel['remote-subnet']} any -P out ipsec " .
+ "{$tunnel['p2']['protocol']}/tunnel/{$ep}-" .
+ "{$tunnel['remote-gateway']}/unique;\n";
+
+ $spdconf .= "spdadd {$tunnel['remote-subnet']} " .
+ "{$sa}/{$sn} any -P in ipsec " .
+ "{$tunnel['p2']['protocol']}/tunnel/{$tunnel['remote-gateway']}-" .
+ "{$ep}/unique;\n";
+ }
+
+ fwrite($fd, $spdconf);
+ fclose($fd);
+
+ /* load SPD */
+ mwexec("/usr/sbin/setkey -c < {$g['varetc_path']}/spd.conf");
+ }
+
+ /* generate racoon.conf */
+ $fd = fopen("{$g['varetc_path']}/racoon.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open racoon.conf in vpn_ipsec_configure().\n");
+ return 1;
+ }
+
+ $racoonconf = "path pre_shared_key \"{$g['varetc_path']}/psk.txt\";\n\n";
+
+ if (is_array($ipseccfg['tunnel']) && count($ipseccfg['tunnel']))
+ foreach ($ipseccfg['tunnel'] as $tunnel) {
+
+ if (isset($tunnel['disabled']))
+ continue;
+
+ $ep = vpn_endpoint_determine($tunnel, $curwanip);
+ if (!$ep)
+ continue;
+
+ vpn_localnet_determine($tunnel['local-subnet'], $sa, $sn);
+
+ if (isset($tunnel['p1']['myident']['myaddress'])) {
+ $myidentt = "address";
+ $myident = $ep;
+ } else if (isset($tunnel['p1']['myident']['address'])) {
+ $myidentt = "address";
+ $myident = $tunnel['p1']['myident']['address'];
+ } else if (isset($tunnel['p1']['myident']['fqdn'])) {
+ $myidentt = "fqdn";
+ $myident = $tunnel['p1']['myident']['fqdn'];
+ } else if (isset($tunnel['p1']['myident']['ufqdn'])) {
+ $myidentt = "user_fqdn";
+ $myident = $tunnel['p1']['myident']['ufqdn'];
+ }
+
+ $racoonconf .= <<<EOD
+remote {$tunnel['remote-gateway']} \{
+ exchange_mode {$tunnel['p1']['mode']};
+ my_identifier {$myidentt} "{$myident}";
+ peers_identifier address {$tunnel['remote-gateway']};
+ initial_contact on;
+ support_proxy on;
+ proposal_check obey;
+
+ proposal \{
+ encryption_algorithm {$tunnel['p1']['encryption-algorithm']};
+ hash_algorithm {$tunnel['p1']['hash-algorithm']};
+ authentication_method pre_shared_key;
+ dh_group {$tunnel['p1']['dhgroup']};
+
+EOD;
+ if ($tunnel['p1']['lifetime'])
+ $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n";
+
+ $racoonconf .= " }\n";
+
+ if ($tunnel['p1']['lifetime'])
+ $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n";
+
+ $racoonconf .= "}\n\n";
+
+ $p2ealgos = join(",", $tunnel['p2']['encryption-algorithm-option']);
+ $p2halgos = join(",", $tunnel['p2']['hash-algorithm-option']);
+
+ $racoonconf .= <<<EOD
+sainfo address {$sa}/{$sn} any address {$tunnel['remote-subnet']} any \{
+ encryption_algorithm {$p2ealgos};
+ authentication_algorithm {$p2halgos};
+ compression_algorithm deflate;
+
+EOD;
+
+ if ($tunnel['p2']['pfsgroup'])
+ $racoonconf .= " pfs_group {$tunnel['p2']['pfsgroup']};\n";
+
+ if ($tunnel['p2']['lifetime'])
+ $racoonconf .= " lifetime time {$tunnel['p2']['lifetime']} secs;\n";
+
+ $racoonconf .= "}\n\n";
+ }
+
+ /* mobile clients? */
+ if (isset($ipseccfg['mobileclients']['enable'])) {
+
+ $tunnel = $ipseccfg['mobileclients'];
+
+ if (isset($tunnel['p1']['myident']['myaddress'])) {
+ $myidentt = "address";
+ $myident = $curwanip;
+ } else if (isset($tunnel['p1']['myident']['address'])) {
+ $myidentt = "address";
+ $myident = $tunnel['p1']['myident']['address'];
+ } else if (isset($tunnel['p1']['myident']['fqdn'])) {
+ $myidentt = "fqdn";
+ $myident = $tunnel['p1']['myident']['fqdn'];
+ } else if (isset($tunnel['p1']['myident']['ufqdn'])) {
+ $myidentt = "user_fqdn";
+ $myident = $tunnel['p1']['myident']['ufqdn'];
+ }
+
+ $racoonconf .= <<<EOD
+remote anonymous \{
+ exchange_mode {$tunnel['p1']['mode']};
+ my_identifier {$myidentt} "{$myident}";
+ initial_contact on;
+ passive on;
+ generate_policy on;
+ support_proxy on;
+ proposal_check obey;
+
+ proposal \{
+ encryption_algorithm {$tunnel['p1']['encryption-algorithm']};
+ hash_algorithm {$tunnel['p1']['hash-algorithm']};
+ authentication_method pre_shared_key;
+ dh_group {$tunnel['p1']['dhgroup']};
+
+EOD;
+ if ($tunnel['p1']['lifetime'])
+ $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n";
+
+ $racoonconf .= " }\n";
+
+ if ($tunnel['p1']['lifetime'])
+ $racoonconf .= " lifetime time {$tunnel['p1']['lifetime']} secs;\n";
+
+ $racoonconf .= "}\n\n";
+
+ $p2ealgos = join(",", $tunnel['p2']['encryption-algorithm-option']);
+ $p2halgos = join(",", $tunnel['p2']['hash-algorithm-option']);
+
+ $racoonconf .= <<<EOD
+sainfo anonymous \{
+ encryption_algorithm {$p2ealgos};
+ authentication_algorithm {$p2halgos};
+ compression_algorithm deflate;
+
+EOD;
+
+ if ($tunnel['p2']['pfsgroup'])
+ $racoonconf .= " pfs_group {$tunnel['p2']['pfsgroup']};\n";
+
+ if ($tunnel['p2']['lifetime'])
+ $racoonconf .= " lifetime time {$tunnel['p2']['lifetime']} secs;\n";
+
+ $racoonconf .= "}\n\n";
+ }
+
+ fwrite($fd, $racoonconf);
+ fclose($fd);
+
+ /* generate psk.txt */
+ $fd = fopen("{$g['varetc_path']}/psk.txt", "w");
+ if (!$fd) {
+ printf("Error: cannot open psk.txt in vpn_ipsec_configure().\n");
+ return 1;
+ }
+
+ $pskconf = "";
+
+ if (is_array($ipseccfg['tunnel'])) {
+ foreach ($ipseccfg['tunnel'] as $tunnel) {
+ if (isset($tunnel['disabled']))
+ continue;
+ $pskconf .= "{$tunnel['remote-gateway']} {$tunnel['p1']['pre-shared-key']}\n";
+ }
+ }
+
+ /* add PSKs for mobile clients */
+ if (is_array($ipseccfg['mobilekey'])) {
+ foreach ($ipseccfg['mobilekey'] as $key) {
+ $pskconf .= "{$key['ident']} {$key['pre-shared-key']}\n";
+ }
+ }
+
+ fwrite($fd, $pskconf);
+ fclose($fd);
+ chmod("{$g['varetc_path']}/psk.txt", 0600);
+
+ /* start racoon */
+ mwexec("/usr/local/sbin/racoon -d -f {$g['varetc_path']}/racoon.conf");
+
+ foreach ($ipseccfg['tunnel'] as $tunnel) {
+ if (isset($tunnel['auto'])) {
+ $remotehost = substr($tunnel['remote-subnet'],0,strpos($tunnel['remote-subnet'],"/"));
+ $srchost = vpn_endpoint_determine($tunnel, $curwanip);
+ if ($srchost)
+ mwexec_bg("/sbin/ping -c 1 -S {$srchost} {$remotehost}");
+ }
+ }
+ }
+ }
+
+ if (!$g['booting']) {
+ /* reload the filter */
+ filter_configure();
+ }
+
+ if ($g['booting'])
+ echo "done\n";
+
+ return 0;
+}
+
+function vpn_pptpd_configure() {
+ global $config, $g;
+
+ $syscfg = $config['system'];
+ $pptpdcfg = $config['pptpd'];
+
+ if ($g['booting']) {
+ if (!$pptpdcfg['mode'] || ($pptpdcfg['mode'] == "off"))
+ return 0;
+
+ echo "Configuring PPTP VPN service... ";
+ } else {
+ /* kill mpd */
+ killbypid("{$g['varrun_path']}/mpd-vpn.pid");
+
+ /* wait for process to die */
+ sleep(2);
+
+ /* remove mpd.conf, if it exists */
+ unlink_if_exists("{$g['varetc_path']}/mpd-vpn/mpd.conf");
+ unlink_if_exists("{$g['varetc_path']}/mpd-vpn/mpd.links");
+ unlink_if_exists("{$g['varetc_path']}/mpd-vpn/mpd.secret");
+ }
+
+ /* make sure mpd-vpn directory exists */
+ if (!file_exists("{$g['varetc_path']}/mpd-vpn"))
+ mkdir("{$g['varetc_path']}/mpd-vpn");
+
+ switch ($pptpdcfg['mode']) {
+
+ case 'server':
+
+ /* write mpd.conf */
+ $fd = fopen("{$g['varetc_path']}/mpd-vpn/mpd.conf", "w");
+ if (!$fd) {
+ printf("Error: cannot open mpd.conf in vpn_pptpd_configure().\n");
+ return 1;
+ }
+
+ $mpdconf = <<<EOD
+pptpd:
+
+EOD;
+
+ for ($i = 0; $i < $g['n_pptp_units']; $i++) {
+ $mpdconf .= " load pt{$i}\n";
+ }
+
+ for ($i = 0; $i < $g['n_pptp_units']; $i++) {
+
+ $clientip = long2ip(ip2long($pptpdcfg['remoteip']) + $i);
+ $ngif = "ng" . ($i+1);
+
+ $mpdconf .= <<<EOD
+
+pt{$i}:
+ new -i {$ngif} pt{$i} pt{$i}
+ set ipcp ranges {$pptpdcfg['localip']}/32 {$clientip}/32
+ load pts
+
+EOD;
+ }
+
+ $mpdconf .= <<<EOD
+
+pts:
+ set iface disable on-demand
+ set iface enable proxy-arp
+ set iface enable tcpmssfix
+ set iface idle 1800
+ set iface up-script /usr/local/sbin/vpn-linkup
+ set iface down-script /usr/local/sbin/vpn-linkdown
+ set bundle enable multilink
+ set bundle enable crypt-reqd
+ set link yes acfcomp protocomp
+ set link no pap chap
+ set link enable chap-msv2
+ set link mtu 1460
+ set link keep-alive 10 60
+ set ipcp yes vjcomp
+ set bundle enable compression
+ set ccp yes mppc
+ set ccp yes mpp-e128
+ set ccp yes mpp-stateless
+
+EOD;
+
+ if (!isset($pptpdcfg['req128'])) {
+ $mpdconf .= <<<EOD
+ set ccp yes mpp-e40
+ set ccp yes mpp-e56
+
+EOD;
+ }
+
+ if (isset($config['dnsmasq']['enable'])) {
+ $mpdconf .= " set ipcp dns " . $config['interfaces']['lan']['ipaddr'];
+ if ($syscfg['dnsserver'][0])
+ $mpdconf .= " " . $syscfg['dnsserver'][0];
+ $mpdconf .= "\n";
+ } else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
+ $mpdconf .= " set ipcp dns " . join(" ", $syscfg['dnsserver']) . "\n";
+ }
+
+ if (isset($pptpdcfg['radius']['enable'])) {
+ $mpdconf .= <<<EOD
+ set radius server {$pptpdcfg['radius']['server']} "{$pptpdcfg['radius']['secret']}"
+ set radius retries 3
+ set radius timeout 10
+ set bundle enable radius-auth
+ set bundle disable radius-fallback
+
+EOD;
+
+ if (isset($pptpdcfg['radius']['accounting'])) {
+ $mpdconf .= <<<EOD
+ set bundle enable radius-acct
+
+EOD;
+ }
+ }
+
+ fwrite($fd, $mpdconf);
+ fclose($fd);
+
+ /* write mpd.links */
+ $fd = fopen("{$g['varetc_path']}/mpd-vpn/mpd.links", "w");
+ if (!$fd) {
+ printf("Error: cannot open mpd.links in vpn_pptpd_configure().\n");
+ return 1;
+ }
+
+ $mpdlinks = "";
+
+ for ($i = 0; $i < $g['n_pptp_units']; $i++) {
+ $mpdlinks .= <<<EOD
+
+pt{$i}:
+ set link type pptp
+ set pptp enable incoming
+ set pptp disable originate
+ set pptp disable windowing
+ set pptp self 127.0.0.1
+
+EOD;
+ }
+
+ fwrite($fd, $mpdlinks);
+ fclose($fd);
+
+ /* write mpd.secret */
+ $fd = fopen("{$g['varetc_path']}/mpd-vpn/mpd.secret", "w");
+ if (!$fd) {
+ printf("Error: cannot open mpd.secret in vpn_pptpd_configure().\n");
+ return 1;
+ }
+
+ $mpdsecret = "";
+
+ if (is_array($pptpdcfg['user'])) {
+ foreach ($pptpdcfg['user'] as $user)
+ $mpdsecret .= "{$user['name']} \"{$user['password']}\" {$user['ip']}\n";
+ }
+
+ fwrite($fd, $mpdsecret);
+ fclose($fd);
+ chmod("{$g['varetc_path']}/mpd-vpn/mpd.secret", 0600);
+
+ /* fire up mpd */
+ mwexec("/usr/local/sbin/mpd -b -d {$g['varetc_path']}/mpd-vpn -p {$g['varrun_path']}/mpd-vpn.pid pptpd");
+
+ break;
+
+ case 'redir':
+ break;
+ }
+
+ if (!$g['booting']) {
+ /* reload the filter */
+ filter_configure();
+ }
+
+ if ($g['booting'])
+ echo "done\n";
+
+ return 0;
+}
+
+function vpn_localnet_determine($adr, &$sa, &$sn) {
+ global $config, $g;
+
+ if (isset($adr)) {
+ if ($adr['network']) {
+ switch ($adr['network']) {
+ case 'lan':
+ $sn = $config['interfaces']['lan']['subnet'];
+ $sa = gen_subnet($config['interfaces']['lan']['ipaddr'], $sn);
+ break;
+ }
+ } else if ($adr['address']) {
+ list($sa,$sn) = explode("/", $adr['address']);
+ if (is_null($sn))
+ $sn = 32;
+ }
+ } else {
+ $sn = $config['interfaces']['lan']['subnet'];
+ $sa = gen_subnet($config['interfaces']['lan']['ipaddr'], $sn);
+ }
+}
+
+function vpn_endpoint_determine($tunnel, $curwanip) {
+
+ global $g, $config;
+
+ if ((!$tunnel['interface']) || ($tunnel['interface'] == "wan")) {
+ if ($curwanip)
+ return $curwanip;
+ else
+ return null;
+ } else if ($tunnel['interface'] == "lan") {
+ return $config['interfaces']['lan']['ipaddr'];
+ } else {
+ $oc = $config['interfaces'][$tunnel['interface']];
+
+ if (isset($oc['enable']) && $oc['if']) {
+ return $oc['ipaddr'];
+ }
+ }
+
+ return null;
+}
+
+?>
diff --git a/etc/inc/xmlparse.inc b/etc/inc/xmlparse.inc
new file mode 100644
index 0000000..1fdadac
--- /dev/null
+++ b/etc/inc/xmlparse.inc
@@ -0,0 +1,205 @@
+<?php
+/*
+ xmlparse.inc
+ functions to parse/dump configuration files in XML format
+ part of m0n0wall (http://m0n0.ch/wall)
+
+ Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* tags that are always to be handled as lists */
+$listtags = explode(" ", "rule user key subqueue dnsserver winsserver " .
+ "encryption-algorithm-option hash-algorithm-option hosts tunnel onetoone " .
+ "staticmap route alias pipe queue shellcmd earlyshellcmd mobilekey " .
+ "servernat proxyarpnet passthrumac allowedip wolentry vlan");
+
+function startElement($parser, $name, $attrs) {
+ global $depth, $curpath, $config, $havedata, $listtags;
+
+ array_push($curpath, strtolower($name));
+
+ $ptr =& $config;
+ foreach ($curpath as $path) {
+ $ptr =& $ptr[$path];
+ }
+
+ /* is it an element that belongs to a list? */
+ if (in_array(strtolower($name), $listtags)) {
+
+ /* is there an array already? */
+ if (!is_array($ptr)) {
+ /* make an array */
+ $ptr = array();
+ }
+
+ array_push($curpath, count($ptr));
+
+ } else if (isset($ptr)) {
+ /* multiple entries not allowed for this element, bail out */
+ die(sprintf("XML error: %s at line %d cannot occur more than once\n",
+ $name,
+ xml_get_current_line_number($parser)));
+ }
+
+ $depth++;
+ $havedata = $depth;
+}
+
+function endElement($parser, $name) {
+ global $depth, $curpath, $config, $havedata, $listtags;
+
+ if ($havedata == $depth) {
+ $ptr =& $config;
+ foreach ($curpath as $path) {
+ $ptr =& $ptr[$path];
+ }
+ $ptr = "";
+ }
+
+ array_pop($curpath);
+
+ if (in_array(strtolower($name), $listtags))
+ array_pop($curpath);
+
+ $depth--;
+}
+
+function cData($parser, $data) {
+ global $depth, $curpath, $config, $havedata;
+
+ $data = trim($data, "\t\n\r");
+
+ if ($data != "") {
+ $ptr =& $config;
+ foreach ($curpath as $path) {
+ $ptr =& $ptr[$path];
+ }
+
+ if (is_string($ptr)) {
+ $ptr .= $data;
+ } else {
+ if (trim($data, " ") != "") {
+ $ptr = $data;
+ $havedata++;
+ }
+ }
+ }
+}
+
+function parse_xml_config($cffile, $rootobj) {
+
+ global $depth, $curpath, $config, $havedata, $listtags;
+
+ $config = array();
+ $curpath = array();
+ $depth = 0;
+ $havedata = 0;
+
+ $xml_parser = xml_parser_create();
+
+ xml_set_element_handler($xml_parser, "startElement", "endElement");
+ xml_set_character_data_handler($xml_parser, "cdata");
+
+ if (!($fp = fopen($cffile, "r"))) {
+ die("Error: could not open XML input\n");
+ }
+
+ while ($data = fread($fp, 4096)) {
+ if (!xml_parse($xml_parser, $data, feof($fp))) {
+ die(sprintf("XML error: %s at line %d\n",
+ xml_error_string(xml_get_error_code($xml_parser)),
+ xml_get_current_line_number($xml_parser)));
+ }
+ }
+ xml_parser_free($xml_parser);
+
+ if (!$config[$rootobj]) {
+ die("XML error: no $rootobj object found!\n");
+ }
+
+ return $config[$rootobj];
+}
+
+function dump_xml_config_sub($arr, $indent) {
+
+ global $listtags;
+
+ $xmlconfig = "";
+
+ foreach ($arr as $ent => $val) {
+ if (is_array($val)) {
+ /* is it just a list of multiple values? */
+ if (in_array(strtolower($ent), $listtags)) {
+ foreach ($val as $cval) {
+ if (is_array($cval)) {
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "<$ent>\n";
+ $xmlconfig .= dump_xml_config_sub($cval, $indent + 1);
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "</$ent>\n";
+ } else {
+ $xmlconfig .= str_repeat("\t", $indent);
+ if ((is_bool($cval) && ($cval == true)) ||
+ ($cval === ""))
+ $xmlconfig .= "<$ent/>\n";
+ else if (!is_bool($cval))
+ $xmlconfig .= "<$ent>" . htmlspecialchars($cval) . "</$ent>\n";
+ }
+ }
+ } else {
+ /* it's an array */
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "<$ent>\n";
+ $xmlconfig .= dump_xml_config_sub($val, $indent + 1);
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "</$ent>\n";
+ }
+ } else {
+ if ((is_bool($val) && ($val == true)) || ($val === "")) {
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "<$ent/>\n";
+ } else if (!is_bool($val)) {
+ $xmlconfig .= str_repeat("\t", $indent);
+ $xmlconfig .= "<$ent>" . htmlspecialchars($val) . "</$ent>\n";
+ }
+ }
+ }
+
+ return $xmlconfig;
+}
+
+function dump_xml_config($arr, $rootobj) {
+
+ $xmlconfig = "<?xml version=\"1.0\"?" . ">\n";
+ $xmlconfig .= "<$rootobj>\n";
+
+ $xmlconfig .= dump_xml_config_sub($arr, 1);
+
+ $xmlconfig .= "</$rootobj>\n";
+
+ return $xmlconfig;
+}
+
+?>
OpenPOWER on IntegriCloud