From 46bc6e545a17e77202aaf01ec0cd8d5a46567525 Mon Sep 17 00:00:00 2001 From: Renato Botelho Date: Tue, 25 Aug 2015 08:08:24 -0300 Subject: Move main pfSense content to src/ --- src/etc/inc/filter.inc | 4228 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4228 insertions(+) create mode 100644 src/etc/inc/filter.inc (limited to 'src/etc/inc/filter.inc') diff --git a/src/etc/inc/filter.inc b/src/etc/inc/filter.inc new file mode 100644 index 0000000..36bbe2b --- /dev/null +++ b/src/etc/inc/filter.inc @@ -0,0 +1,4228 @@ +. + 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. + + pfSense_BUILDER_BINARIES: /sbin/kldload /usr/sbin/tcpdump /sbin/pfctl /bin/rm + pfSense_BUILDER_BINARIES: /usr/sbin/inetd + pfSense_MODULE: filter +*/ + + +/* holds the items that will be executed *AFTER* the filter is fully loaded */ +$after_filter_configure_run = array(); + +/* For installing cron job of schedules */ +$time_based_rules = false; + +/* Used to hold the interface list that will be used on ruleset creation. */ +$FilterIflist = array(); + +/* Create a global array to avoid errors on rulesets. */ +$GatewaysList = array(); + +/* Used for the hostname dns resolver */ +$filterdns = array(); + +/* Used for aliases and interface macros */ +$aliases = ""; + +/* ICMP v4 types */ +$icmptypes = array( + "" => gettext("any"), + "echoreq" => gettext("Echo request"), + "echorep" => gettext("Echo reply"), + "unreach" => gettext("Destination unreachable"), + "squench" => gettext("Source quench"), + "redir" => gettext("Redirect"), + "althost" => gettext("Alternate Host"), + "routeradv" => gettext("Router advertisement"), + "routersol" => gettext("Router solicitation"), + "timex" => gettext("Time exceeded"), + "paramprob" => gettext("Invalid IP header"), + "timereq" => gettext("Timestamp"), + "timerep" => gettext("Timestamp reply"), + "inforeq" => gettext("Information request"), + "inforep" => gettext("Information reply"), + "maskreq" => gettext("Address mask request"), + "maskrep" => gettext("Address mask reply"), + "trace" => gettext("Traceroute"), + "dataconv" => gettext("Datagram conversion error"), + "mobredir" => gettext("Mobile host redirect"), + "ipv6-where" => gettext("IPv6 where-are-you"), + "ipv6-here" => gettext("IPv6 I-am-here"), + "mobregreq" => gettext("Mobile registration request"), + "mobregrep" => gettext("Mobile registration reply"), + "skip" => gettext("SKIP"), + "photuris" => gettext("Photuris") +); + +/* ICMP v6 types */ +$icmp6types = array( + "" => gettext("any"), + "unreach" => gettext("Destination unreachable"), + "toobig" => gettext("Packet too big"), + "timex" => gettext("Time exceeded"), + "paramprob" => gettext("Parameter problem"), + "echoreq" => gettext("Echo request"), + "echorep" => gettext("Echo reply"), + "groupqry" => gettext("Group membership query"), + "listqry" => gettext("Multicast listener query"), + "grouprep" => gettext("Group membership report"), + "listenrep" => gettext("Multicast listener report"), + "groupterm" => gettext("Group membership termination"), + "listendone" => gettext("Multicast listener done"), + "routersol" => gettext("Router solicitation"), + "routeradv" => gettext("Router advertisement"), + "neighbrsol" => gettext("Neighbor solicitation"), + "neighbradv" => gettext("Neighbor advertisement"), + "redir" => gettext("Redirect"), + "routrrenum" => gettext("Router renumbering"), + "wrureq" => gettext("Who are you request"), + "wrurep" => gettext("Who are you reply"), + "fqdnreq" => gettext("FQDN query"), + "fqdnrep" => gettext("FQDN reply"), + "niqry" => gettext("Node information request"), + "nirep" => gettext("Node information reply"), + "mtraceresp" => gettext("mtrace resp"), + "mtrace" => gettext("mtrace messages") +); + +global $tracker; +global $negate_tracker; +$tracker = 1000000000; +$negate_tracker = 10000000; + +function filter_rule_tracker($tracker) { + global $tracker; + + return (++$tracker); +} + +function filter_negaterule_tracker() { + global $negate_tracker; + + ++$negate_tracker; + return "tracker {$negate_tracker} "; +} + +function fix_rule_label($descr) { + $descr = str_replace('"', '', $descr); + if (strlen($descr) > 63) { + return substr($descr, 0, 60) . "..."; + } else { + return $descr; + } +} + +function is_bogonsv6_used() { + global $config, $g; + # Only use bogonsv6 table if IPv6 Allow is on, and at least 1 enabled interface also has "blockbogons" enabled. + $usebogonsv6 = false; + if (isset($config['system']['ipv6allow'])) { + foreach ($config['interfaces'] as $ifacedata) { + if (isset($ifacedata['enable']) && isset($ifacedata['blockbogons'])) { + $usebogonsv6 = true; + break; + } + } + } + return $usebogonsv6; +} + +function filter_pflog_start($kill_first = false) { + global $config, $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_pflog_start() being called $mt\n"; + } + if ((!file_exists("{$g['varrun_path']}/filterlog.pid")) || + (!isvalidpid("{$g['varrun_path']}/filterlog.pid"))) { + mwexec("/usr/local/sbin/filterlog -i pflog0 -p {$g['varrun_path']}/filterlog.pid"); + } +} + +/* reload filter async */ +function filter_configure() { + global $g; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_configure() being called $mt\n"; + } + + /* + * NOTE: Check here for bootup status since this should not be triggered during bootup. + * The reason is that rc.bootup calls filter_configure_sync directly which does this too. + */ + if (!platform_booting()) { + send_event("filter reload"); + } +} + +function filter_delete_states_for_down_gateways() { + global $config, $GatewaysList; + + if (isset($config['system']['kill_states'])) { + return; + } + + $any_gateway_down = false; + $a_gateways = return_gateways_status(); + if (is_array($GatewaysList)) { + foreach ($GatewaysList as $gwname => $gateway) { + if (empty($gateway['monitor'])) { + continue; + } + if (!is_ipaddr($gateway['monitor'])) { + continue; + } + if (strstr($gateway['monitor'], "127.0.0.")) { + continue; + } + if (empty($a_gateways[$gateway['monitor']])) { + continue; + } + $gwstatus =& $a_gateways[$gateway['monitor']]; + if (strstr($gwstatus['status'], "down")) { + $any_gateway_down = true; + break; + } + } + } + if ($any_gateway_down == true) { + mwexec("/sbin/pfctl -Fs"); + } +} + +/* reload filter sync */ +function filter_configure_sync($delete_states_if_needed = true) { + global $config, $g, $after_filter_configure_run, $FilterIflist; + global $time_based_rules, $filterdns, $aliases, $dummynet_name_list; + + /* Use filter lock to not allow concurrent filter reloads during this run. */ + $filterlck = lock('filter', LOCK_EX); + + filter_pflog_start(); + update_filter_reload_status(gettext("Initializing")); + + /* invalidate interface cache */ + get_interface_arr(true); + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_configure_sync() being called $mt\n"; + } + /* Get interface list to work with. */ + filter_generate_optcfg_array(); + if (platform_booting() == true) { + echo gettext("Configuring firewall"); + } + + /* generate aliases */ + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Creating aliases")); + $aliases = filter_generate_aliases(); + $gateways = filter_generate_gateways(); + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Generating Limiter rules")); + $dummynet_rules = filter_generate_dummynet_rules(); + $dummynet_name_list = get_unique_dnqueue_list(); + update_filter_reload_status(gettext("Generating NAT rules")); + /* generate nat rules */ + $natrules = filter_nat_rules_generate(); + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Generating filter rules")); + /* generate pfctl rules */ + $pfrules = filter_rules_generate(); + /* generate altq, limiter */ + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Generating ALTQ queues")); + $altq_queues = filter_generate_altq_queues(); + update_filter_reload_status(gettext("Generating Layer7 rules")); + generate_layer7_files(); + if (platform_booting() == true) { + echo "."; + } + update_filter_reload_status(gettext("Loading filter rules")); + /* enable pf if we need to, otherwise disable */ + if (!isset ($config['system']['disablefilter'])) { + mwexec("/sbin/pfctl -e", true); + } else { + mwexec("/sbin/pfctl -d", true); + unlink_if_exists("{$g['tmp_path']}/filter_loading"); + update_filter_reload_status(gettext("Filter is disabled. Not loading rules.")); + if (platform_booting() == true) { + echo gettext("done.") . "\n"; + } + unlock($filterlck); + return; + } + + $limitrules = ""; + /* User defined maximum table entries in Advanced menu. */ + if ($config['system']['maximumtableentries'] <> "" && is_numeric($config['system']['maximumtableentries'])) { + $limitrules .= "set limit table-entries {$config['system']['maximumtableentries']}\n"; + } + + if ($config['system']['optimization'] <> "") { + $limitrules .= "set optimization {$config['system']['optimization']}\n"; + if ($config['system']['optimization'] == "conservative") { + $limitrules .= "set timeout { udp.first 300, udp.single 150, udp.multiple 900 }\n"; + } + } else { + $limitrules .= "set optimization normal\n"; + } + + $timeoutlist = ""; + if (isset($config['system']['tcpfirsttimeout']) && is_numericint($config['system']['tcpfirsttimeout'])) { + $timeoutlist .= " tcp.first {$config['system']['tcpfirsttimeout']} "; + } + if (isset($config['system']['tcpopeningtimeout']) && is_numericint($config['system']['tcpopeningtimeout'])) { + $timeoutlist .= " tcp.opening {$config['system']['tcpopeningtimeout']} "; + } + if (isset($config['system']['tcpestablishedtimeout']) && is_numericint($config['system']['tcpestablishedtimeout'])) { + $timeoutlist .= " tcp.established {$config['system']['tcpestablishedtimeout']} "; + } + if (isset($config['system']['tcpclosingtimeout']) && is_numericint($config['system']['tcpclosingtimeout'])) { + $timeoutlist .= " tcp.closing {$config['system']['tcpclosingtimeout']} "; + } + if (isset($config['system']['tcpfinwaittimeout']) && is_numericint($config['system']['tcpfinwaittimeout'])) { + $timeoutlist .= " tcp.finwait {$config['system']['tcpfinwaittimeout']} "; + } + if (isset($config['system']['tcpclosedtimeout']) && is_numericint($config['system']['tcpclosedtimeout'])) { + $timeoutlist .= " tcp.closed {$config['system']['tcpclosedtimeout']} "; + } + if (isset($config['system']['udpfirsttimeout']) && is_numericint($config['system']['udpfirsttimeout'])) { + $timeoutlist .= " udp.first {$config['system']['udpfirsttimeout']} "; + } + if (isset($config['system']['udpsingletimeout']) && is_numericint($config['system']['udpsingletimeout'])) { + $timeoutlist .= " udp.single {$config['system']['udpsingletimeout']} "; + } + if (isset($config['system']['udpmultipletimeout']) && is_numericint($config['system']['udpmultipletimeout'])) { + $timeoutlist .= " udp.multiple {$config['system']['udpmultipletimeout']} "; + } + if (isset($config['system']['icmpfirsttimeout']) && is_numericint($config['system']['icmpfirsttimeout'])) { + $timeoutlist .= " icmp.first {$config['system']['icmpfirsttimeout']} "; + } + if (isset($config['system']['icmperrortimeout']) && is_numericint($config['system']['icmperrortimeout'])) { + $timeoutlist .= " icmp.error {$config['system']['icmperrortimeout']} "; + } + if (isset($config['system']['otherfirsttimeout']) && is_numericint($config['system']['otherfirsttimeout'])) { + $timeoutlist .= " other.first {$config['system']['otherfirsttimeout']} "; + } + if (isset($config['system']['othersingletimeout']) && is_numericint($config['system']['othersingletimeout'])) { + $timeoutlist .= " other.single {$config['system']['othersingletimeout']} "; + } + if (isset($config['system']['othermultipletimeout']) && is_numericint($config['system']['othermultipletimeout'])) { + $timeoutlist .= " other.multiple {$config['system']['othermultipletimeout']} "; + } + + if ($timeoutlist <> "") { + $limitrules .= "set timeout { $timeoutlist }\n"; + } + + if (!empty($config['system']['adaptivestart']) && !empty($config['system']['adaptiveend'])) { + $limitrules .= "set timeout { adaptive.start {$config['system']['adaptivestart']}, adaptive.end {$config['system']['adaptiveend']} }\n"; + } + + if ($config['system']['maximumstates'] <> "" && is_numeric($config['system']['maximumstates'])) { + /* User defined maximum states in Advanced menu. */ + $limitrules .= "set limit states {$config['system']['maximumstates']}\n"; + $limitrules .= "set limit src-nodes {$config['system']['maximumstates']}\n"; + } else { + $max_states = pfsense_default_state_size(); + $limitrules .= "set limit states {$max_states}\n"; + $limitrules .= "set limit src-nodes {$max_states}\n"; + } + + /* Frag limit. pf default is 5000 */ + if ($config['system']['maximumfrags'] <> "" && is_numeric($config['system']['maximumfrags'])) { + $limitrules .= "set limit frags {$config['system']['maximumfrags']}\n"; + } + + if (isset($config['system']['lb_use_sticky']) && is_numeric($config['system']['srctrack']) && ($config['system']['srctrack'] > 0)) { + $limitrules .= "set timeout src.track {$config['system']['srctrack']}\n"; + } + + $rules = ""; + $rules = "{$limitrules}\n"; + $rules .= "{$aliases} \n"; + $rules .= "{$gateways} \n"; + update_filter_reload_status(gettext("Setting up logging information")); + $rules .= filter_setup_logging_interfaces(); + $rules .= "\n"; + $rules .= "set skip on pfsync0\n"; + $rules .= "\n"; + update_filter_reload_status(gettext("Setting up SCRUB information")); + $rules .= filter_generate_scrubing(); + $rules .= "\n"; + $rules .= "{$altq_queues}\n"; + $rules .= "{$natrules}\n"; + $rules .= "{$pfrules}\n"; + $rules .= discover_pkg_rules("filter"); + + unset($aliases, $gateways, $altq_queues, $natrules, $pfrules); + + // Copy rules.debug to rules.debug.old + if (file_exists("{$g['tmp_path']}/rules.debug")) { + @copy("{$g['tmp_path']}/rules.debug", "{$g['tmp_path']}/rules.debug.old"); + } + + if (!@file_put_contents("{$g['tmp_path']}/rules.debug", $rules, LOCK_EX)) { + log_error("WARNING: Could not write new rules!"); + unlock($filterlck); + return; + } + + @file_put_contents("{$g['tmp_path']}/rules.limits", $limitrules); + mwexec("/sbin/pfctl -Of {$g['tmp_path']}/rules.limits"); + unset($rules, $limitrules); + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "pfctl being called at $mt\n"; + } + unset($rules_loading, $rules_error); + $_grbg = exec("/sbin/pfctl -o basic -f {$g['tmp_path']}/rules.debug 2>&1", $rules_error, $rules_loading); + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "pfctl done at $mt\n"; + } + /* + * check for a error while loading the rules file. if an error has occurred + * then output the contents of the error to the caller + */ + if ($rules_loading <> 0) { + $saved_line_error = $rules_error[0]; + $line_error = explode(":", $rules_error[0]); + $line_number = $line_error[1]; + $line_split = file("{$g['tmp_path']}/rules.debug"); + if (is_array($line_split)) { + $line_error = sprintf(gettext('The line in question reads [%1$d]: %2$s'), $line_number, $line_split[$line_number-1]); + } + unset($line_split); + + /* Brutal ugly hack but required -- PF is stuck, unwedge */ + if (strstr("$rules_error[0]", "busy")) { + exec("/sbin/pfctl -d; /sbin/pfctl -e; /sbin/pfctl -f {$g['tmp_path']}/rules.debug"); + $error_msg = gettext("PF was wedged/busy and has been reset."); + file_notice("pf_busy", $error_msg, "pf_busy", ""); + } else { + $_grbg = exec("/sbin/pfctl -o basic -f {$g['tmp_path']}/rules.debug.old 2>&1"); + } + unset($rules_loading, $rules_error); + + if ($line_error and $line_number) { + file_notice("filter_load", sprintf(gettext('There were error(s) loading the rules: %1$s - %2$s'), $saved_line_error, $line_error), "Filter Reload", ""); + update_filter_reload_status(sprintf(gettext('There were error(s) loading the rules: %1$s - %2$s'), $saved_line_error, $line_error)); + unlock($filterlck); + return; + } + } + + # If we are not using bogonsv6 then we can remove any bogonsv6 table from the running pf (if the table is not there, the kill is still fine). + if (!is_bogonsv6_used()) { + $_grbg = exec("/sbin/pfctl -t bogonsv6 -T kill 2>/dev/null"); + } + + update_filter_reload_status(gettext("Starting up layer7 daemon")); + layer7_start_l7daemon(); + + if (!platform_booting()) { + if (!empty($filterdns)) { + @file_put_contents("{$g['varetc_path']}/filterdns.conf", implode("", $filterdns)); + unset($filterdns); + if (isvalidpid("{$g['varrun_path']}/filterdns.pid")) { + sigkillbypid("{$g['varrun_path']}/filterdns.pid", "HUP"); + } else { + /* + * FilterDNS has three debugging levels. The default chosen is 1. + * Available are level 2 and greater then 2. + */ + if (isset($config['system']['aliasesresolveinterval']) && is_numeric($config['system']['aliasesresolveinterval'])) { + $resolve_interval = $config['system']['aliasesresolveinterval']; + } else { + $resolve_interval = 300; + } + mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns.pid -i {$resolve_interval} -c {$g['varetc_path']}/filterdns.conf -d 1"); + } + } else { + killbypid("{$g['varrun_path']}/filterdns.pid"); + @unlink("{$g['varrun_path']}/filterdns.pid"); + } + } + + /* run items scheduled for after filter configure run */ + $fda = fopen("{$g['tmp_path']}/commands.txt", "w"); + if ($fda) { + if ($after_filter_configure_run) { + foreach ($after_filter_configure_run as $afcr) { + fwrite($fda, $afcr . "\n"); + } + unset($after_filter_configure_run); + } + + /* + * we need a way to let a user run a shell cmd after each + * filter_configure() call. run this xml command after + * each change. + */ + if ($config['system']['afterfilterchangeshellcmd'] <> "") { + fwrite($fda, $config['system']['afterfilterchangeshellcmd'] . "\n"); + } + + fclose($fda); + } + + if (file_exists("{$g['tmp_path']}/commands.txt")) { + mwexec("sh {$g['tmp_path']}/commands.txt &"); + unlink("{$g['tmp_path']}/commands.txt"); + } + + /* if time based rules are enabled then swap in the set */ + if ($time_based_rules == true) { + filter_tdr_install_cron(true); + } else { + filter_tdr_install_cron(false); + } + + if (platform_booting() == true) { + echo "."; + } + + if ($delete_states_if_needed) { + update_filter_reload_status(gettext("Processing down interface states")); + filter_delete_states_for_down_gateways(); + } + + update_filter_reload_status(gettext("Running plugins")); + + if (is_dir("/usr/local/pkg/pf/")) { + /* process packager manager custom rules */ + update_filter_reload_status(gettext("Running plugins (pf)")); + run_plugins("/usr/local/pkg/pf/"); + update_filter_reload_status(gettext("Plugins completed.")); + } + + update_filter_reload_status(gettext("Done")); + if (platform_booting() == true) { + echo gettext("done.") . "\n"; + } + + unlock($filterlck); + return 0; +} + +function filter_generate_scrubing() { + global $config, $FilterIflist; + $scrubrules = ""; + + if (isset($config['system']['maxmss_enable'])) { + $maxmss = 1400; + if (!empty($config['system']['maxmss'])) { + $maxmss = $config['system']['maxmss']; + } + + $scrubrules .= "scrub from any to max-mss {$maxmss}\n"; + $scrubrules .= "scrub from to any max-mss {$maxmss}\n"; + } + /* disable scrub option */ + foreach ($FilterIflist as $scrubif => $scrubcfg) { + if (isset($scrubcfg['virtual']) || empty($scrubcfg['descr'])) { + continue; + } + /* set up MSS clamping */ + if (($scrubcfg['mss'] <> "") && + (is_numeric($scrubcfg['mss'])) && + ($scrubcfg['if'] != "pppoe") && + ($scrubcfg['if'] != "pptp") && + ($scrubif['if'] != "l2tp")) { + $mssclamp = "max-mss " . (intval($scrubcfg['mss'] - 40)); + } else { + $mssclamp = ""; + } + /* configure no-df for linux nfs and others */ + if ($config['system']['scrubnodf']) { + $scrubnodf = "no-df"; + } else { + $scrubnodf = ""; + } + if ($config['system']['scrubrnid']) { + $scrubrnid = "random-id"; + } else { + $scrubrnid = ""; + } + if (!isset($config['system']['disablescrub'])) { + $scrubrules .= "scrub on \${$scrubcfg['descr']} all {$scrubnodf} {$scrubrnid} {$mssclamp} fragment reassemble\n"; // reassemble all directions + } else if (!empty($mssclamp)) { + $scrubrules .= "scrub on \${$scrubcfg['descr']} {$mssclamp}\n"; + } + } + return $scrubrules; +} + +function filter_generate_nested_alias($name, $alias, &$aliasnesting, &$aliasaddrnesting) { + global $aliastable, $filterdns; + + $addresses = explode(" ", $alias); + $use_filterdns = false; + $finallist = ""; + $builtlist = ""; + $urltable_nesting = ""; + $aliasnesting[$name] = $name; + $alias_type = alias_get_type($name); + foreach ($addresses as $address) { + if (empty($address)) { + continue; + } + $linelength = strlen($builtlist); + $tmpline = ""; + if (is_alias($address)) { + if (alias_get_type($address) == 'urltable') { + // Feature#1603. For this type of alias we do not need to recursively call filter_generate_nested_alias. Just load IPs from the file. + $urltable_nesting = alias_expand_urltable($address); + if (!empty($urltable_nesting)) { + $urlfile_as_arr = file($urltable_nesting); + foreach ($urlfile_as_arr as $line) { + $address= rtrim($line); + if ((strlen($tmpline) + $linelength) > 4036) { + $finallist .= "{$tmpline} \\\n"; + $tmpline = ""; + } + $tmpline .= " {$address}"; + } + } + } + /* We already expanded this alias so there is no necessity to do it again. */ + else if (!isset($aliasnesting[$address])) { + $tmpline = filter_generate_nested_alias($name, $aliastable[$address], $aliasnesting, $aliasaddrnesting); + } + } else if (!isset($aliasaddrnesting[$address])) { + if (!is_ipaddr($address) && !is_subnet($address) && !((($alias_type == 'port') || ($alias_type == 'url_ports')) && (is_port($address) || is_portrange($address))) && is_hostname($address)) { + if (!isset($filterdns["{$address}{$name}"])) { + $use_filterdns = true; + $filterdns["{$address}{$name}"] = "pf {$address} {$name}\n"; + } + continue; + } + $aliasaddrnesting[$address] = $address; + $tmpline = " {$address}"; + } + if ((strlen($tmpline)+ $linelength) > 4036) { + $finallist .= "{$builtlist} \\\n"; + $builtlist = ""; + } + if (!empty($tmpline)) { + $builtlist .= " {$tmpline}"; + } + } + $finallist .= $builtlist; + + if ($use_filterdns === true && !empty($finallist)) { + foreach (explode(" ", $finallist) as $address) { + if (empty($address)) { + continue; + } + if ((is_ipaddr($address) || is_subnet($address)) && !isset($filterdns["{$address}{$name}"])) { + $filterdns["{$address}{$name}"] = "pf {$address} {$name}\n"; + } + } + $finallist = ''; + } + + return $finallist; +} + +function filter_expand_alias($alias_name) { + global $config; + + if (isset($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $aliased) { + if ($aliased['name'] == $alias_name) { + $aliasnesting = array(); + $aliasaddrnesting = array(); + return filter_generate_nested_alias($aliased['name'], $aliased['address'], $aliasnesting, $aliasaddrnesting); + } + } + } +} + +function filter_expand_alias_array($alias_name) { + $expansion = filter_expand_alias($alias_name); + return explode(" ", preg_replace('/\s+/', ' ', trim($expansion))); +} + +function filter_generate_aliases() { + global $config, $FilterIflist, $after_filter_configure_run; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_aliases() being called $mt\n"; + } + + $alias = "#System aliases\n "; + $aliases = "loopback = \"{ lo0 }\"\n"; + + foreach ($FilterIflist as $if => $ifcfg) { + if (is_array($ifcfg[0])) { + if ($ifcfg[0]['if'] == 'pppoe') { + $aliases .= "{$ifcfg[0]['descr']} = \"{ {$ifcfg[0]['if']}"; + $aliases .= " }\"\n"; + } + } elseif (!empty($ifcfg['descr']) && !empty($ifcfg['if'])) { + if ($ifcfg['type6'] == '6rd') { + $aliases .= "{$ifcfg['descr']} = \"{ {$ifcfg['if']} {$if}_stf"; + } else if ($ifcfg['type6'] == '6to4') { + $aliases .= "{$ifcfg['descr']} = \"{ {$ifcfg['if']} {$if}_stf"; + } else { + $aliases .= "{$ifcfg['descr']} = \"{ {$ifcfg['if']}"; + + if ($ifcfg['type'] == 'pptp') { + foreach (get_parent_interface($ifcfg['if']) as $parent_if) { + if ($parent_if != $ifcfg['if']) { + $aliases .= " {$parent_if}"; + } + } + } + } + $aliases .= " }\"\n"; + } + } + + $aliases .= "\n#SSH Lockout Table\n"; + $aliases .= "table persist\n"; + $aliases .= "table persist\n"; + + $aliases .= "#Snort tables\n"; + $aliases .= "table \n"; + $aliases .= "table \n"; + if (!file_exists("/etc/bogons") || !file_exists("/etc/bogonsv6")) { + conf_mount_rw(); + if (!file_exists("/etc/bogons")) { + @file_put_contents("/etc/bogons", ""); + } + if (!file_exists("/etc/bogonsv6")) { + @file_put_contents("/etc/bogonsv6", ""); + } + conf_mount_ro(); + } + $aliases .= "table persist file \"/etc/bogons\"\n"; + if (is_bogonsv6_used()) { + $aliases .= "table persist file \"/etc/bogonsv6\"\n"; + } + + $vpns_list = filter_get_vpns_list(); + if ($vpns_list) { + $aliases .= "table { $vpns_list }\n"; + } + + /* add a Negate_networks table */ + $aliases .= "table "; + if ($vpns_list) { + $aliases .= "{ $vpns_list }"; + } + $aliases .= "\n"; + + $aliases .= "\n# User Aliases \n"; + /* Setup pf groups */ + if (isset($config['aliases']['alias'])) { + foreach ($config['aliases']['alias'] as $aliased) { + $extralias = ""; + $aliasnesting = array(); + $aliasaddrnesting = array(); + if (is_numericint($aliased['name'])) { + // skip aliases with numeric-only names. redmine #4289 + file_notice("Filter_Reload", "Aliases with numeric-only names are not valid. Skipping alias " . $aliased['name']); + continue; + } + $addrlist = filter_generate_nested_alias($aliased['name'], $aliased['address'], $aliasnesting, $aliasaddrnesting); + switch ($aliased['type']) { + case "host": + case "network": + case "url": + $tableaddrs = "{$addrlist}{$extralias}"; + if (empty($tableaddrs)) { + $aliases .= "table <{$aliased['name']}> persist\n"; + if (empty($aliased['address'])) { + $after_filter_configure_run[] = "/sbin/pfctl -T flush -t " . escapeshellarg($aliased['name']); + } + } else { + $aliases .= "table <{$aliased['name']}> { {$addrlist}{$extralias} } \n"; + } + + $aliases .= "{$aliased['name']} = \"<{$aliased['name']}>\"\n"; + break; + case "openvpn": + $openvpncfg = array(); + if ($config['openvpn']['user']) { + /* XXX: Check if we have a correct ip? */ + foreach ($config['openvpn']['user'] as $openvpn) { + $openvpncfg[$openvpn['name']] = $openvpn['ip']; + } + } + $vpn_lines = explode("\n", $addrlist); + foreach ($vpn_lines as $vpn_line) { + $vpn_address_split = explode(" ", $vpn_line); + foreach ($vpn_address_split as $vpnsplit) { + if (isset($openvpncfg[$vpnsplit])) { + $newaddress .= " "; + $newaddress .= $openvpn[$vpnsplit]; + break; + } + } + } + $aliases .= "table <{$aliased['name']}> { {$newaddress}{$extralias} } \n"; + $aliases .= "{$aliased['name']} = \"<{$aliased['name']}>\"\n"; + break; + case "urltable": + $urlfn = alias_expand_urltable($aliased['name']); + if ($urlfn) { + $aliases .= "table <{$aliased['name']}> persist file \"{$urlfn}\"\n"; + $aliases .= "{$aliased['name']} = \"<{$aliased['name']}>\"\n"; + } + break; + case "urltable_ports": + // TODO: Change it when pf supports tables with ports + $urlfn = alias_expand_urltable($aliased['name']); + if ($urlfn) { + $aliases .= "{$aliased['name']} = \"{ " . preg_replace("/\n/", " ", file_get_contents($urlfn)) . " }\"\n"; + } + break; + case "port": + case "url_ports": + $aliases .= "{$aliased['name']} = \"{ {$addrlist} }\"\n"; + break; + default: + $aliases .= "{$aliased['name']} = \"{ {$aliased['address']}{$extralias} }\"\n"; + break; + } + } + } + $result = "{$alias} \n"; + $result .= "{$aliases}"; + + return $result; +} + +function filter_generate_gateways() { + global $config, $g, $GatewaysList; + + $rules = "# Gateways\n"; + + update_filter_reload_status(gettext("Creating gateway group item...")); + + /* Lookup Gateways to be used in filter rules once */ + $GatewaysList = return_gateways_array(); + $GatewayGroupsList = return_gateway_groups_array(); + + if (is_array($GatewaysList)) { + foreach ($GatewaysList as $gwname => $gateway) { + $int = $gateway['interface']; + $gwip = $gateway['gateway']; + $route = ""; + if (!is_ipaddr($gwip)) { + $gwip = get_interface_gateway($gateway['friendlyiface']); + } + if (is_ipaddr($gwip) && !empty($int) && !isset($gateway['force_down'])) { + $route = "route-to ( {$int} {$gwip} )"; + } + if (($route === "") && isset($config['system']['skip_rules_gw_down'])) { + unset($GatewaysList[$gwname]); + } else { + $rules .= "GW{$gwname} = \" {$route} \"\n"; + } + } + } + + if (is_array($GatewayGroupsList)) { + foreach ($GatewayGroupsList as $gateway => $members) { + $route = ""; + /* hey, that's not a group member! */ + unset($members['ipprotocol']); + if (count($members) > 0) { + $foundlb = 0; + $routeto = ""; + foreach ($members as $idx => $member) { + $int = $member['int']; + $gatewayip = $member['gwip']; + if (($int <> "") && is_ipaddr($gatewayip)) { + if ($g['debug']) { + log_error(sprintf(gettext('Setting up route with %1$s on %2$s'), $gatewayip, $int)); + } + if ($member['weight'] > 1) { + $routeto .= str_repeat("( {$int} {$gatewayip} ) ", $member['weight']); + } else { + $routeto .= "( {$int} {$gatewayip} ) "; + } + $foundlb++; + } else { + log_error(sprintf(gettext("An error occurred while trying to find the interface got %s . The rule has not been added."), $gatewayip)); + } + } + $route = ""; + if ($foundlb > 0) { + $route = " route-to { {$routeto} } "; + if ($foundlb > 1) { + $route .= " round-robin "; + if (isset($config['system']['lb_use_sticky'])) { + $route .= " sticky-address "; + } + } + } + } + if (($route === "") && isset($config['system']['skip_rules_gw_down'])) { + unset($GatewayGroupsList[$gateway]); + } else { + $rules .= "GW{$gateway} = \" {$route} \"\n"; + } + } + } + + /* Create a global array to avoid errors on rulesets. */ + $GatewaysList = $GatewaysList + $GatewayGroupsList; + + $rules .= "\n"; + + return $rules; +} + +/* returns space separated list of vpn subnets */ +function filter_get_vpns_list() { + global $config; + + $vpns = ""; + $vpns_arr = array(); + + /* ipsec */ + if (isset($config['ipsec']['enable'])) { + if (is_array($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as $ph2ent) { + if ((!$ph2ent['mobile']) && ($ph2ent['mode'] != 'transport')) { + if (!function_exists('ipsec_idinfo_to_cidr')) { + require_once("ipsec.inc"); + } + if (!is_array($ph2ent['remoteid'])) { + continue; + } + $ph2ent['remoteid']['mode'] = $ph2ent['mode']; + $vpns_subnet = ipsec_idinfo_to_cidr($ph2ent['remoteid']); + if (!is_subnet($vpns_subnet) || $vpns_subnet == "0.0.0.0/0") { + continue; + } + $vpns_arr[] = $vpns_subnet; + } + } + } + } + + /* openvpn */ + foreach (array('client', 'server') as $type) { + if (is_array($config['openvpn']["openvpn-$type"])) { + foreach ($config['openvpn']["openvpn-$type"] as $settings) { + if (is_array($settings)) { + if (!isset($settings['disable'])) { + $remote_networks = explode(',', $settings['remote_network']); + foreach ($remote_networks as $remote_network) { + if (is_subnet($remote_network) && ($remote_network <> "0.0.0.0/0")) { + $vpns_arr[] = $remote_network; + } + } + if (is_subnet($settings['tunnel_network']) && $settings['tunnel_network'] <> "0.0.0.0/0") { + $vpns_arr[] = $settings['tunnel_network']; + } + } + } + } + } + } + /* pppoe */ + if (is_array($config['pppoes']['pppoe'])) { + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] == "server") { + if (is_ipaddr($pppoe['remoteip'])) { + $pppoesub = gen_subnet($pppoe['remoteip'], $pppoe['pppoe_subnet']); + if (is_subnet($pppoesub)) { + $vpns_arr[] = $pppoesub; + } + } + } + } + } + + if (!empty($vpns_arr)) { + $vpns = implode(" ", $vpns_arr); + } + + return $vpns; +} + +/* returns space separated list of directly connected networks + * optionally returns an array instead, including friendly interface and gateway (if applicable) + */ +function filter_get_direct_networks_list($returnsubnetsonly = true) { + global $config, $FilterIflist, $GatewaysList; + /* build list of directly connected interfaces and networks */ + $networks = ""; + $networks_arr = array(); + if (empty($FilterIflist)) { + filter_generate_optcfg_array(); + } + foreach ($FilterIflist as $ifent => $ifcfg) { + $subnet = "{$ifcfg['sa']}/{$ifcfg['sn']}"; + if (is_subnet($subnet)) { + if ($returnsubnetsonly) { + $networks_arr[] = $subnet; + } else { + $networks_arr[] = array( + 'subnet' => $subnet, + 'if' => $ifent, + 'ip' => $ifcfg['ip']); + } + } + } + foreach (get_configured_ip_aliases_list(true) as $vip) { + $subnet = "{$vip['subnet']}/{$vip['subnet_bits']}"; + if (is_subnet($subnet) && !(is_subnetv4($subnet) && $vip['subnet_bits'] == 32) && !(is_subnetv6($subnet) && $vip['subnet_bits'] == 128)) { + if (is_subnetv4($subnet)) { + $subnet = gen_subnet($vip['subnet'], $vip['subnet_bits']) . "/{$vip['subnet_bits']}"; + } else if (is_subnetv6($subnet)) { + $subnet = gen_subnetv6($vip['subnet'], $vip['subnet_bits']) . "/{$vip['subnet_bits']}"; + } + if ($returnsubnetsonly) { + $networks_arr[] = $subnet; + } else { + $networks_arr[] = array( + 'subnet' => $subnet, + 'if' => $vip['interface'], + 'ip' => $vip['subnet']); + } + } + } + foreach (get_staticroutes() as $netent) { + if (is_subnet($netent['network'])) { + if ($returnsubnetsonly) { + $networks_arr[] = $netent['network']; + } else if (isset($GatewaysList[$netent['gateway']])) { + $networks_arr[] = array( + 'subnet' => $netent['network'], + 'if' => $GatewaysList[$netent['gateway']]['friendlyiface'], + 'gateway' => $GatewaysList[$netent['gateway']]['gateway']); + } + } + } + if ($returnsubnetsonly) { + if (!empty($networks_arr)) { + $networks = implode(" ", $networks_arr); + } + return $networks; + } else { + return $networks_arr; + } +} + +function filter_generate_optcfg_array() { + global $config, $FilterIflist; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_optcfg_array() being called $mt\n"; + } + + read_layer7_config(); + /* if list */ + $iflist = get_configured_interface_with_descr(); + foreach ($iflist as $if => $ifdetail) { + $oc = $config['interfaces'][$if]; + $oic = array(); + $oic['if'] = get_real_interface($if); + if (!does_interface_exist($oic['if'])) { + continue; + } + $oic['ifv6'] = get_real_interface($if, "inet6"); + $oic['ip'] = get_interface_ip($if); + $oic['ipv6'] = get_interface_ipv6($if); + if (!is_ipaddrv4($oc['ipaddr']) && !empty($oc['ipaddr'])) { + $oic['type'] = $oc['ipaddr']; + } + if (!is_ipaddrv6($oc['ipaddrv6']) && !empty($oc['ipaddrv6'])) { + $oic['type6'] = $oc['ipaddrv6']; + } + if (!empty($oc['track6-interface'])) { + $oic['track6-interface'] = $oc['track6-interface']; + } + $oic['sn'] = get_interface_subnet($if); + $oic['snv6'] = get_interface_subnetv6($if); + $oic['mtu'] = empty($oc['mtu']) ? 1500 : $oc['mtu']; + $oic['mss'] = empty($oc['mss']) ? '' : $oc['mss']; + $oic['descr'] = $ifdetail; + $oic['sa'] = gen_subnet($oic['ip'], $oic['sn']); + $oic['sav6'] = gen_subnetv6($oic['ipv6'], $oic['snv6']); + $oic['nonat'] = $oc['nonat']; + $oic['alias-address'] = $oc['alias-address']; + $oic['alias-subnet'] = $oc['alias-subnet']; + $oic['gateway'] = $oc['gateway']; + $oic['gatewayv6'] = $oc['gatewayv6']; + $oic['spoofcheck'] = "yes"; + $oic['bridge'] = link_interface_to_bridge($if); + $vips = link_interface_to_vips($if); + if (!empty($vips)) { + foreach ($vips as $vipidx => $vip) { + if (is_ipaddrv4($vip['subnet'])) { + if (!is_array($oic['vips'])) { + $oic['vips'] = array(); + } + $oic['vips'][$vipidx]['ip'] = $vip['subnet']; + if (empty($vip['subnet_bits'])) { + $oic['vips'][$vipidx]['sn'] = 32; + } else { + $oic['vips'][$vipidx]['sn'] = $vip['subnet_bits']; + } + } else if (is_ipaddrv6($vip['subnet'])) { + if (!is_array($oic['vips6'])) { + $oic['vips6'] = array(); + } + $oic['vips6'][$vipidx]['ip'] = $vip['subnet']; + if (empty($vip['subnet_bits'])) { + $oic['vips6'][$vipidx]['sn'] = 128; + } else { + $oic['vips6'][$vipidx]['sn'] = $vip['subnet_bits']; + } + } + } + } + unset($vips); + $FilterIflist[$if] = $oic; + } + + if ($config['pptpd']['mode'] == "server" || $config['pptpd']['mode'] == "redir") { + $oic = array(); + $oic['if'] = 'pptp'; + $oic['descr'] = 'pptp'; + $oic['ip'] = $config['pptpd']['localip']; + $oic['sa'] = $config['pptpd']['remoteip']; + $oic['mode'] = $config['pptpd']['mode']; + $oic['virtual'] = true; + if ($config['pptpd']['pptp_subnet'] <> "") { + $oic['sn'] = $config['pptpd']['pptp_subnet']; + } else { + $oic['sn'] = "32"; + } + $FilterIflist['pptp'] = $oic; + } + if ($config['l2tp']['mode'] == "server") { + $oic = array(); + $oic['if'] = 'l2tp'; + $oic['descr'] = 'L2TP'; + $oic['ip'] = $config['l2tp']['localip']; + $oic['sa'] = $config['l2tp']['remoteip']; + if ($config['l2tp']['l2tp_subnet'] <> "") { + $oic['sn'] = $config['l2tp']['l2tp_subnet']; + } else { + $oic['sn'] = "32"; + } + $oic['mode'] = $config['l2tp']['mode']; + $oic['virtual'] = true; + $FilterIflist['l2tp'] = $oic; + } + if (is_array($config['pppoes']['pppoe']) && (count($config['pppoes']['pppoe']) > 0)) { + $pppoeifs = array(); + foreach ($config['pppoes']['pppoe'] as $pppoe) { + if ($pppoe['mode'] == "server") { + $oic = array(); + $oic['if'] = 'pppoe'; + $oic['descr'] = 'pppoe'; + $oic['ip'] = $pppoe['localip']; + $oic['sa'] = $pppoe['remoteip']; + $oic['mode'] = $pppoe['mode']; + $oic['virtual'] = true; + if ($pppoe['pppoe_subnet'] <> "") { + $oic['sn'] = $pppoe['pppoe_subnet']; + } else { + $oic['sn'] = "32"; + } + $pppoeifs[] = $oic; + } + } + if (count($pppoeifs)) { + $FilterIflist['pppoe'] = $pppoeifs; + } + } + /* add ipsec interfaces */ + if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) { + $oic = array(); + $oic['if'] = 'enc0'; + $oic['descr'] = 'IPsec'; + $oic['type'] = "none"; + $oic['virtual'] = true; + $FilterIflist['enc0'] = $oic; + } + /* add openvpn interfaces */ + if ($config['openvpn']['openvpn-server'] || $config['openvpn']['openvpn-client']) { + $oic = array(); + $oic['if'] = "openvpn"; + $oic['descr'] = 'OpenVPN'; + $oic['type'] = "none"; + $oic['virtual'] = true; + $FilterIflist['openvpn'] = $oic; + } + /* add interface groups */ + if (is_array($config['ifgroups']['ifgroupentry'])) { + foreach ($config['ifgroups']['ifgroupentry'] as $ifgen) { + $oc = array(); + $oc['if'] = $ifgen['ifname']; + $oc['descr'] = $ifgen['ifname']; + $oc['virtual'] = true; + $FilterIflist[$ifgen['ifname']] = $oc; + } + } +} + +function filter_flush_nat_table() { + global $config, $g; + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_flush_nat_table() being called $mt\n"; + } + return mwexec("/sbin/pfctl -F nat"); +} + +function filter_flush_state_table() { + return mwexec("/sbin/pfctl -F state"); +} + +function filter_get_reflection_interfaces($natif = "") { + global $FilterIflist; + + $nat_if_list = array(); + + foreach ($FilterIflist as $ifent => $ifname) { + if ($ifname['if'] == $natif) { + continue; + } + + /* Do not add reflection redirects for interfaces with gateways */ + if (interface_has_gateway($ifent)) { + continue; + } + + $nat_if_list[] = $ifname['if']; + } + + return $nat_if_list; +} + +function filter_generate_reflection_nat($rule, &$route_table, $nat_ifs, $protocol, $target, $target_ip, $target_subnet = "") { + global $config, $FilterIflist; + + if (!isset($config['system']['enablenatreflectionhelper'])) { + return ""; + } + + // Initialize natrules holder string + $natrules = ""; + + update_filter_reload_status(sprintf(gettext("Creating reflection NAT rule for %s..."), $rule['descr'])); + + /* TODO: Add this option to port forwards page. */ + if (isset($rule['staticnatport'])) { + $static_port = " static-port"; + } else { + $static_port = " port 1024:65535"; + } + + if (!empty($protocol)) { + $protocol_text = " proto {$protocol}"; + } else { + $protocol_text = ""; + } + + if (empty($target_subnet) || !is_numeric($target_subnet)) { + $target_subnet = 32; + } + + if (!is_array($route_table)) { + /* get a simulated IPv4-only route table based on the config */ + $route_table = filter_get_direct_networks_list(false); + foreach ($route_table as $rt_key => $rt_ent) { + if (!is_subnetv4($rt_ent['subnet'])) { + unset($route_table[$rt_key]); + } + if (isset($route_table[$rt_key]) && isset($FilterIflist[$rt_ent['if']]['if'])) { + $route_table[$rt_key]['if'] = $FilterIflist[$rt_ent['if']]['if']; + } + } + } + + /* Check if the target is accessed through a static route */ + foreach ($route_table as $route) { + if (isset($route['gateway']) && is_ipaddr($route['gateway'])) { + $subnet_split = explode("/", $route['subnet']); + if (in_array($route['if'], $nat_ifs) && check_subnets_overlap($target_ip, $target_subnet, $subnet_split[0], $subnet_split[1])) { + $target_ip = $route['gateway']; + $target_subnet = 32; + break; + } + } + } + + /* Search for matching subnets in the routing table */ + foreach ($route_table as $route) { + $subnet = $route['subnet']; + $subnet_split = explode("/", $subnet); + $subnet_if = $route['if']; + /* Blacklist invalid "from" sources since they can be picked up accidentally and cause rule errors. */ + $no_reflect_from = array("l2tp"); + if (in_array($subnet_if, $nat_ifs) && check_subnets_overlap($target_ip, $target_subnet, $subnet_split[0], $subnet_split[1])) { + $ifsubnet_ip = ""; + /* Find interface IP to use for NAT */ + foreach ($route_table as $ifnetwork) { + if (isset($ifnetwork['ip']) && is_ipaddr($ifnetwork['ip']) && $ifnetwork['if'] == $subnet_if && ip_in_subnet($ifnetwork['ip'], $subnet)) { + $ifsubnet_ip = $ifnetwork['ip']; + break; + } + } + if(!empty($ifsubnet_ip) && !in_array($subnet, $no_reflect_from)) { + $subnets = array($subnet); + /* Find static routes that also need to be referenced in the NAT rule */ + foreach ($route_table as $rtentry) { + if (isset($rtentry['gateway']) && is_ipaddr($rtentry['gateway']) && $rtentry['if'] == $subnet_if && ip_in_subnet($rtentry['gateway'], $subnet)) { + $subnets[] = $rtentry['subnet']; + } + } + if (count($subnets) > 1) { + $subnet = "{ " . implode(" ", $subnets) . " }"; + } + $natrules .= "no nat on {$subnet_if}{$protocol_text} from {$subnet_if} to {$target}\n"; + $natrules .= "nat on {$subnet_if}{$protocol_text} from {$subnet} to {$target} -> {$ifsubnet_ip}{$static_port}\n"; + } + } + } + + if (!empty($natrules)) { + $natrules .= "\n"; + } + + return $natrules; +} + +function filter_generate_reflection_proxy($rule, $nordr, $rdr_ifs, $srcaddr, $dstaddr_port, &$starting_localhost_port, &$reflection_txt) { + global $FilterIflist, $config; + + // Initialize natrules holder string + $natrules = ""; + $reflection_txt = array(); + + if (!empty($rdr_ifs)) { + if ($config['system']['reflectiontimeout']) { + $reflectiontimeout = $config['system']['reflectiontimeout']; + } else { + $reflectiontimeout = "2000"; + } + + update_filter_reload_status(sprintf(gettext("Creating reflection rule for %s..."), $rule['descr'])); + + $rdr_if_list = implode(" ", $rdr_ifs); + if (count($rdr_ifs) > 1) { + $rdr_if_list = "{ {$rdr_if_list} }"; + } + + $natrules .= "\n# Reflection redirects\n"; + + $localport = $rule['local-port']; + if (!empty($localport) && is_alias($localport)) { + $localport = filter_expand_alias($localport); + $localport = explode(" ", trim($localport)); + // The translation port for rdr, when specified, does not support more than one port or range. + // Emulating for behavior consistent with the original port forward. + $localport = $localport[0]; + } + + if (is_alias($rule['destination']['port'])) { + if (empty($localport) || $rule['destination']['port'] == $rule['local-port']) { + $dstport = filter_expand_alias($rule['destination']['port']); + $dstport = array_filter(explode(" ", trim($dstport))); + $localport = ""; + } else if (!empty($localport)) { + $dstport = array($localport); + } + } else { + $dstport = array(str_replace("-", ":", $rule['destination']['port'])); + $dstport_split = explode(":", $dstport[0]); + + if (!empty($localport) && $dstport_split[0] != $rule['local-port']) { + if (!is_alias($rule['local-port']) && $dstport_split[1] && $dstport_split[0] != $dstport_split[1]) { + $localendport = $localport + ($dstport_split[1] - $dstport_split[0]); + $localport .= ":$localendport"; + } + + $dstport = array($localport); + } else { + $localport = ""; + } + } + + $dstaddr = explode(" ", $dstaddr_port); + if ($dstaddr[2]) { + $rflctintrange = array_pop($dstaddr); + array_pop($dstaddr); + } else { + return ""; + } + $dstaddr = implode(" ", $dstaddr); + if (empty($dstaddr) || trim($dstaddr) == "0.0.0.0" || strtolower(trim($dstaddr)) == "port") { + return ""; + } + + if (isset($rule['destination']['any'])) { + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + + if (!isset($FilterIflist[$natif])) { + return ""; + } + if (is_ipaddr($FilterIflist[$natif]['ip'])) { + $dstaddr = $FilterIflist[$natif]['ip']; + } else { + return ""; + } + + if (!empty($FilterIflist[$natif]['sn'])) { + $dstaddr = gen_subnet($dstaddr, $FilterIflist[$natif]['sn']) . '/' . $FilterIflist[$natif]['sn']; + } + } + + switch ($rule['protocol']) { + case "tcp/udp": + $protocol = "{ tcp udp }"; + $reflect_protos = array('tcp', 'udp'); + break; + case "tcp": + case "udp": + $protocol = $rule['protocol']; + $reflect_protos = array($rule['protocol']); + break; + default: + return ""; + break; + } + + if (!empty($nordr)) { + $natrules .= "no rdr on {$rdr_if_list} proto {$protocol} from {$srcaddr} to {$dstaddr} port {$rflctintrange}\n"; + return $natrules; + } + + if (is_alias($rule['target'])) { + $target = filter_expand_alias($rule['target']); + } else if (is_ipaddr($rule['target'])) { + $target = $rule['target']; + } else if (is_ipaddr($FilterIflist[$rule['target']]['ip'])) { + $target = $FilterIflist[$rule['target']]['ip']; + } else { + return ""; + } + $starting_localhost_port_tmp = $starting_localhost_port; + $toomanyports = false; + /* only install reflection rules for < 19991 items */ + foreach ($dstport as $loc_pt) { + if ($starting_localhost_port < 19991) { + $toadd_array = array(); + $inetdport = $starting_localhost_port; + $rflctrange = $starting_localhost_port; + + $loc_pt = explode(":", $loc_pt); + if ($loc_pt[1] && $loc_pt[1] > $loc_pt[0]) { + $delta = $loc_pt[1] - $loc_pt[0]; + } else { + $delta = 0; + } + + if (($inetdport + $delta + 1) - $starting_localhost_port_tmp > 500) { + log_error("Not installing NAT reflection rules for a port range > 500"); + $inetdport = $starting_localhost_port; + $toadd_array = array(); + $toomanyports = true; + break; + } else if (($inetdport + $delta) > 19990) { + log_error("Installing partial NAT reflection rules. Maximum 1,000 reached."); + $delta = 19990 - $inetdport; + $loc_pt[1] = $loc_pt[0] + $delta; + if ($delta == 0) { + unset($loc_pt[1]); + } + $toomanyports = true; + + if (!empty($localport)) { + if (is_alias($rule['destination']['port'])) { + $rflctintrange = alias_expand($rule['destination']['port']); + } else { + if ($dstport_split[1]) { + $dstport_split[1] = $dstport_split[0] + $inetdport + $delta - $starting_localhost_port; + } + $rflctintrange = implode(":", $dstport_split); + } + } + } + + if (empty($localport)) { + $rflctintrange = implode(":", $loc_pt); + } + if ($inetdport + $delta > $starting_localhost_port) { + $rflctrange .= ":" . ($inetdport + $delta); + } + $starting_localhost_port = $inetdport + $delta + 1; + $toadd_array = array_merge($toadd_array, range($loc_pt[0], $loc_pt[0] + $delta)); + + if (!empty($toadd_array)) { + $rtarget = explode(" ", trim($target)); + foreach ($toadd_array as $tda) { + if (empty($tda)) { + continue; + } + foreach ($reflect_protos as $reflect_proto) { + if ($reflect_proto == "udp") { + $socktype = "dgram"; + $dash_u = "-u "; + $wait = "wait\t"; + } else { + $socktype = "stream"; + $dash_u = ""; + $wait = "nowait/0"; + } + foreach ($rtarget as $targip) { + if (empty($targip)) { + continue; + } + $reflection_txt[] = "{$inetdport}\t{$socktype}\t{$reflect_proto}\t{$wait}\tnobody\t/usr/bin/nc\tnc {$dash_u}-w {$reflectiontimeout} {$targip} {$tda}\n"; + } + } + $inetdport++; + } + $natrules .= "rdr on {$rdr_if_list} proto {$protocol} from {$srcaddr} to {$dstaddr} port {$rflctintrange} tag PFREFLECT -> 127.0.0.1 port {$rflctrange}\n"; + } + } + + if ($toomanyports) { + break; + } + } + + $reflection_txt = array_unique($reflection_txt); + } + + return $natrules; +} + +function filter_nat_rules_automatic_tonathosts($with_descr = false) { + global $config, $FilterIflist, $GatewaysList; + + $tonathosts = array("127.0.0.0/8"); + $descriptions = array(gettext("localhost")); + + foreach (get_staticroutes() as $route) { + $netip = explode("/", $route['network']); + if (isset($GatewaysList[$route['gateway']])) { + $gateway =& $GatewaysList[$route['gateway']]; + if (!interface_has_gateway($gateway['interface']) && is_private_ip($netip[0])) { + $tonathosts[] = $route['network']; + $descriptions[] = gettext("static route"); + } + } + } + + /* create outbound nat entries for all local networks */ + foreach ($FilterIflist as $ocname => $oc) { + if (interface_has_gateway($ocname)) { + continue; + } + if (is_ipaddr($oc['alias-address'])) { + $tonathosts[] = "{$oc['alias-address']}/{$oc['alias-subnet']}"; + $descriptions[] = $oc['descr'] . " " . gettext("DHCP alias address"); + } + if ($oc['sa']) { + $tonathosts[] = "{$oc['sa']}/{$oc['sn']}"; + $descriptions[] = $oc['descr']; + if (isset($oc['vips']) && is_array($oc['vips'])) { + $if_subnets = array("{$oc['sa']}/{$oc['sn']}"); + foreach ($oc['vips'] as $vip) { + if (!is_ipaddrv4($vip['ip'])) { + continue; + } + + foreach ($if_subnets as $subnet) { + if (ip_in_subnet($vip['ip'], $subnet)) { + continue 2; + } + } + + $network = gen_subnet($vip['ip'], $vip['sn']); + array_unshift($tonathosts, $network . '/' . $vip['sn']); + array_unshift($descriptions, "Virtual IP ({$oc['descr']})"); + $if_subnets[] = $network . '/' . $vip['sn']; + unset($network); + } + unset($if_subnets); + } + } + } + + /* PPTP subnet */ + if (($config['pptpd']['mode'] == "server") && is_private_ip($config['pptpd']['remoteip'])) { + if (isset($config['pptpd']['n_pptp_units']) && is_numeric($config['pptpd']['n_pptp_units'])) { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], + long2ip32(ip2long($config['pptpd']['remoteip'])+($config['pptpd']['n_pptp_units']-1))); + } else { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], + long2ip32(ip2long($config['pptpd']['remoteip']))); + } + + foreach ($pptp_subnets as $subnet) { + $tonathosts[] = $subnet; + $descriptions[] = gettext("PPTP server"); + } + } + + /* PPPoE subnet */ + if (is_array($FilterIflist['pppoe'])) { + foreach ($FilterIflist['pppoe'] as $pppoe) { + if (is_private_ip($pppoe['ip'])) { + $tonathosts[] = "{$pppoe['sa']}/{$pppoe['sn']}"; + $descriptions[] = gettext("PPPoE server"); + } + } + } + + /* L2TP subnet */ + if (isset($FilterIflist['l2tp']) && $FilterIflist['l2tp']['mode'] == "server") { + $l2tp_sa = $FilterIflist['l2tp']['sa']; + $l2tp_sn = $FilterIflist['l2tp']['sn']; + if (is_private_ip($l2tp_sa) && !empty($l2tp_sn)) { + $tonathosts[] = "{$l2tp_sa}/{$l2tp_sn}"; + $descriptions[] = gettext("L2TP server"); + } + } + + /* add openvpn interfaces */ + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $ovpnsrv) { + if (!isset($ovpnsrv['disable']) && !empty($ovpnsrv['tunnel_network'])) { + $tonathosts[] = $ovpnsrv['tunnel_network']; + $descriptions[] = gettext("OpenVPN server"); + } + } + } + + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as $ovpncli) { + if (!isset($ovpncli['disable']) && !empty($ovpncli['tunnel_network'])) { + $tonathosts[] = $ovpncli['tunnel_network']; + $descriptions[] = gettext("OpenVPN client"); + } + } + } + + /* IPsec mode_cfg subnet */ + if ((isset($config['ipsec']['client']['enable'])) && + (!empty($config['ipsec']['client']['pool_address'])) && + (!empty($config['ipsec']['client']['pool_netbits']))) { + $tonathosts[] = "{$config['ipsec']['client']['pool_address']}/{$config['ipsec']['client']['pool_netbits']}"; + $descriptions[] = gettext("IPsec client"); + } + + if ($with_descr) { + $combined = array(); + foreach ($tonathosts as $idx => $subnet) { + $combined[] = array( + "subnet" => $subnet, + "descr" => $descriptions[$idx]); + } + + return $combined; + } else { + return $tonathosts; + } +} + +function filter_nat_rules_outbound_automatic($src) { + global $config, $FilterIflist; + + $rules = array(); + foreach ($FilterIflist as $if => $ifcfg) { + if (substr($ifcfg['if'], 0, 4) == "ovpn") { + continue; + } + if (!interface_has_gateway($if)) { + continue; + } + + $natent = array(); + $natent['interface'] = $if; + $natent['source']['network'] = $src; + $natent['dstport'] = "500"; + $natent['target'] = ""; + $natent['destination']['any'] = true; + $natent['staticnatport'] = true; + $natent['descr'] = gettext('Auto created rule for ISAKMP'); + $rules[] = $natent; + + $natent = array(); + $natent['interface'] = $if; + $natent['source']['network'] = $src; + $natent['sourceport'] = ""; + $natent['target'] = ""; + $natent['destination']['any'] = true; + $natent['natport'] = ""; + $natent['descr'] = gettext('Auto created rule'); + if (isset($ifcfg['nonat'])) { + $natent['nonat'] = true; + } + $rules[] = $natent; + } + + return $rules; +} + +/* Generate a 'nat on' or 'no nat on' rule for given interface */ +function filter_nat_rules_generate_if ($if, $src = "any", $srcport = "", $dst = "any", $dstport = "", $natip = "", $natport = "", $nonat = false, $staticnatport = false, $proto = "", $poolopts = "") { + global $config, $FilterIflist; + /* XXX: billm - any idea if this code is needed? */ + if ($src == "/32" || $src{0} == "/") { + return "# src incorrectly specified\n"; + } + if ($natip != "") { + if (is_subnet($natip)) { + $tgt = $natip; + } elseif (is_alias($natip)) { + $tgt = "\${$natip}"; + } else { + $tgt = "{$natip}/32"; + } + } else { + $natip = get_interface_ip($if); + if (is_ipaddr($natip)) { + $tgt = "{$natip}/32"; + } else { + $tgt = "(" . $FilterIflist[$if]['if'] . ")"; + } + } + /* Add the protocol, if defined */ + if (!empty($proto) && $proto != "any") { + if ($proto == "tcp/udp") { + $protocol = " proto { tcp udp }"; + } else { + $protocol = " proto {$proto}"; + } + } else { + $protocol = ""; + } + /* Set tgt for IPv6 */ + if ($proto == "ipv6") { + $natip = get_interface_ipv6($if); + if (is_ipaddrv6($natip)) { + $tgt = "{$natip}/128"; + } + } + /* Add the hard set source port (useful for ISAKMP) */ + if ($natport != "") { + $tgt .= " port {$natport}"; + } + /* sometimes this gets called with "" instead of a value */ + if ($src == "") { + $src = "any"; + } + /* Match on this source port */ + if ($srcport != "") { + $srcportexpand = alias_expand($srcport); + if (!$srcportexpand) { + $srcportexpand = $srcport; + } + $src .= " port {$srcportexpand}"; + } + /* sometimes this gets called with "" instead of a value */ + if ($dst == "") { + $dst = "any"; + } + /* Match on this dest port */ + if ($dstport != "") { + $dstportexpand = alias_expand($dstport); + if (!$dstportexpand) { + $dstportexpand = $dstport; + } + $dst .= " port {$dstportexpand}"; + } + /* outgoing static-port option, hamachi, Grandstream, VOIP, etc */ + $staticnatport_txt = ""; + if ($staticnatport) { + $staticnatport_txt = "static-port"; + } elseif (!$natport) { + $tgt .= " port 1024:65535"; // set source port range + } + /* Allow for negating NAT entries */ + if ($nonat) { + $nat = "no nat"; + $target = ""; + $staticnatport_txt = ""; + $poolopts = ""; + } else { + $nat = "nat"; + $target = "-> {$tgt}"; + } + $if_friendly = $FilterIflist[$if]['descr']; + /* Put all the pieces together */ + if ($if_friendly) { + $natrule = "{$nat} on \${$if_friendly} {$protocol} from {$src} to {$dst} {$target} {$poolopts} {$staticnatport_txt}\n"; + } else { + $natrule .= "# Could not convert {$if} to friendly name(alias)\n"; + } + return $natrule; +} + +function filter_nat_rules_generate() { + global $config, $g, $after_filter_configure_run, $FilterIflist, $GatewaysList, $aliases; + + $natrules = "no nat proto carp\n"; + $natrules .= "no rdr proto carp\n"; + $natrules .= "nat-anchor \"natearly/*\"\n"; + + $natrules .= "nat-anchor \"natrules/*\"\n\n"; + update_filter_reload_status(gettext("Creating 1:1 rules...")); + + $reflection_txt = ""; + $route_table = ""; + + /* any 1:1 mappings? */ + if (is_array($config['nat']['onetoone'])) { + foreach ($config['nat']['onetoone'] as $rule) { + if (isset($rule['disabled'])) { + continue; + } + + $sn = ""; + $sn1 = ""; + $target = alias_expand($rule['external']); + if (!$target) { + $natrules .= "# Unresolvable alias {$rule['target']}\n"; + continue; /* unresolvable alias */ + } + + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + if (!isset($FilterIflist[$natif])) { + continue; + } + + $srcaddr = filter_generate_address($rule, 'source'); + $dstaddr = filter_generate_address($rule, 'destination'); + if (!$dstaddr) { + $dstaddr = $FilterIflist[$natif]['ip']; + } + + $srcaddr = trim($srcaddr); + $dstaddr = trim($dstaddr); + + $tmp = explode('/', $srcaddr); + $srcip = $tmp[0]; + if (!empty($tmp[1]) && is_numeric($tmp[1])) { + $sn = $tmp[1]; + $sn1 = "/{$sn}"; + } + + $natif = $FilterIflist[$natif]['if']; + + /* + * If reflection is enabled, turn on extra redirections + * for this rule by adding other interfaces to an rdr rule. + */ + if ((isset($config['system']['enablebinatreflection']) || $rule['natreflection'] == "enable") && + ($rule['natreflection'] != "disable")) { + $nat_if_list = filter_get_reflection_interfaces($natif); + } else { + $nat_if_list = array(); + } + + $natrules .= "binat on {$natif} from {$srcaddr} to {$dstaddr} -> {$target}{$sn1}\n"; + if (!empty($nat_if_list)) { + $binat_if_list = implode(" ", $nat_if_list); + $binat_if_list = "{ {$binat_if_list} }"; + $reflection_txt .= "rdr on {$binat_if_list} from {$dstaddr} to {$target}{$sn1} -> {$srcaddr} bitmask\n"; + } + + $nat_if_list = array_merge(array($natif), $nat_if_list); + $reflection_txt .= filter_generate_reflection_nat($rule, $route_table, $nat_if_list, "", $srcaddr, $srcip, $sn); + } + } + + /* Add binat rules for Network Prefix translation */ + if (is_array($config['nat']['npt'])) { + foreach ($config['nat']['npt'] as $rule) { + if (isset($rule['disabled'])) { + continue; + } + + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + if (!isset($FilterIflist[$natif])) { + continue; + } + + $srcaddr = filter_generate_address($rule, 'source'); + $dstaddr = filter_generate_address($rule, 'destination'); + + $srcaddr = trim($srcaddr); + $dstaddr = trim($dstaddr); + + $natif = $FilterIflist[$natif]['descr']; + + $natrules .= "binat on \${$natif} from {$srcaddr} to any -> {$dstaddr}\n"; + $natrules .= "binat on \${$natif} from any to {$dstaddr} -> {$srcaddr}\n"; + + } + } + + /* ipsec nat */ + if (is_array($config['ipsec']) && isset($config['ipsec']['enable'])) { + if (is_array($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as $ph2ent) { + if ($ph2ent['mode'] != 'transport' && !empty($ph2ent['natlocalid'])) { + if (!function_exists('ipsec_idinfo_to_cidr')) { + require_once("ipsec.inc"); + } + if (!is_array($ph2ent['localid'])) { + $ph2ent['localid'] = array(); + } + $ph2ent['localid']['mode'] = $ph2ent['mode']; + $local_subnet = ipsec_idinfo_to_cidr($ph2ent['localid']); + if (empty($local_subnet) || $local_subnet == "0.0.0.0/0") { + continue; + } + if (!is_subnet($local_subnet) && !is_ipaddr($local_subnet)) { + continue; + } + if (!is_array($ph2ent['natlocalid'])) { + $ph2ent['natlocalid'] = array(); + } + $ph2ent['natlocalid']['mode'] = $ph2ent['mode']; + $natlocal_subnet = ipsec_idinfo_to_cidr($ph2ent['natlocalid']); + if (empty($natlocal_subnet) || $natlocal_subnet == "0.0.0.0/0") { + continue; + } + if (!is_subnet($natlocal_subnet) && !is_ipaddr($natlocal_subnet)) { + continue; + } + if (!is_array($ph2ent['remoteid'])) { + $ph2ent['remoteid'] = array(); + } + $ph2ent['remoteid']['mode'] = $ph2ent['mode']; + $remote_subnet = ipsec_idinfo_to_cidr($ph2ent['remoteid']); + if (empty($remote_subnet)) { + continue; + } + if (!is_subnet($remote_subnet) && !is_ipaddr($remote_subnet)) { + continue; + } + if ($remote_subnet == "0.0.0.0/0") { + $remote_subnet = "any"; + } + if (is_ipaddr($natlocal_subnet) && !is_ipaddr($local_subnet)) { + $nattype = "nat"; + } else { + list($natnet, $natmask) = explode('/', $natlocal_subnet); + list($locnet, $locmask) = explode('/', $local_subnet); + if (intval($natmask) != intval($locmask)) { + $nattype = "nat"; + } else { + $nattype = "binat"; + } + unset($natnet, $natmask, $locnet, $locmask); + } + $natrules .= "{$nattype} on enc0 from {$local_subnet} to {$remote_subnet} -> {$natlocal_subnet}\n"; + } + } + } + } + + if ($config['nat']['outbound']['mode'] == "disabled") { + $natrules .= "\n# Outbound NAT rules are disabled\n"; + } + + if ($config['nat']['outbound']['mode'] == "advanced" || $config['nat']['outbound']['mode'] == "hybrid") { + $natrules .= "\n# Outbound NAT rules (manual)\n"; + /* advanced outbound rules */ + if (is_array($config['nat']['outbound']['rule'])) { + foreach ($config['nat']['outbound']['rule'] as $obent) { + if (isset($obent['disabled'])) { + continue; + } + update_filter_reload_status(sprintf(gettext("Creating advanced outbound rule %s"), $obent['descr'])); + $src = alias_expand($obent['source']['network']); + if (!$src) { + $src = $obent['source']['network']; + } + $dst = alias_expand($obent['destination']['address']); + if (!$dst) { + $dst = $obent['destination']['address']; + } + if (isset($obent['destination']['not']) && !isset($obent['destination']['any'])) { + $dst = "!" . $dst; + } + + if (!$obent['interface'] || !isset($FilterIflist[$obent['interface']])) { + continue; + } + + $obtarget = ($obent['target'] == "other-subnet") ? $obent['targetip'] . '/' . $obent['targetip_subnet']: $obent['target']; + $poolopts = (is_subnet($obtarget) || is_alias($obtarget)) ? $obent['poolopts'] : ""; + + $natrules .= filter_nat_rules_generate_if($obent['interface'], + $src, + $obent['sourceport'], + $dst, + $obent['dstport'], + $obtarget, + $obent['natport'], + isset($obent['nonat']), + isset($obent['staticnatport']), + $obent['protocol'], + $poolopts + ); + } + } + } + + /* outbound rules */ + if ((!isset($config['nat']['outbound']['mode'])) || + ($config['nat']['outbound']['mode'] == "automatic") || + ($config['nat']['outbound']['mode'] == "hybrid")) { + $natrules .= "\n# Outbound NAT rules (automatic)\n"; + /* standard outbound rules (one for each interface) */ + update_filter_reload_status(gettext("Creating outbound NAT rules")); + $tonathosts_array = filter_nat_rules_automatic_tonathosts(); + $tonathosts = implode(" ", $tonathosts_array); + $numberofnathosts = count($tonathosts_array); + + $natrules .= "\n# Subnets to NAT \n"; + if ($numberofnathosts > 0) { + update_filter_reload_status(gettext('Creating automatic outbound rules')); + + if ($numberofnathosts > 4) { + $natrules .= "table { {$tonathosts} }\n"; + $macroortable = ""; + } else { + $natrules .= "tonatsubnets = \"{ {$tonathosts} }\"\n"; + $macroortable = "\$tonatsubnets"; + } + + $a_outs = filter_nat_rules_outbound_automatic($macroortable); + foreach ($a_outs as $a_out) { + $natrules .= filter_nat_rules_generate_if($a_out['interface'], + $a_out['source']['network'], + $a_out['sourceport'], + $a_out['destination']['address'], + $a_out['dstport'], + $a_out['target'], + $a_out['natport'], + isset($a_out['nonat']), + isset($a_out['staticnatport'])); + } + } + unset($tonathosts, $tonathosts_array, $numberofnathosts); + } + + /* load balancer anchor */ + $natrules .= "\n# Load balancing anchor\n"; + $natrules .= "rdr-anchor \"relayd/*\"\n"; + + update_filter_reload_status(gettext("Setting up TFTP helper")); + $natrules .= "# TFTP proxy\n"; + $natrules .= "rdr-anchor \"tftp-proxy/*\"\n"; + + if (!empty($config['system']['tftpinterface'])) { + $tftpifs = explode(",", $config['system']['tftpinterface']); + foreach ($tftpifs as $tftpif) { + if ($FilterIflist[$tftpif]) { + $natrules .= "rdr pass on {$FilterIflist[$tftpif]['if']} proto udp from any to any port tftp -> 127.0.0.1 port 6969\n"; + } + } + } + + /* DIAG: add ipv6 NAT, if requested */ + if ((isset($config['diag']['ipv6nat']['enable'])) && + (is_ipaddr($config['diag']['ipv6nat']['ipaddr'])) && + (is_array($FilterIflist['wan']))) { + /* XXX: FIX ME! IPV6 */ + $natrules .= "rdr on \${$FilterIflist['wan']['descr']} proto ipv6 from any to any -> {$config['diag']['ipv6nat']['ipaddr']}\n"; + } + + if (file_exists("/var/etc/inetd.conf")) { + @unlink("/var/etc/inetd.conf"); + } + // Open inetd.conf write handle + $inetd_fd = fopen("/var/etc/inetd.conf", "w"); + /* add tftp protocol helper */ + fwrite($inetd_fd, "tftp-proxy\tdgram\tudp\twait\t\troot\t/usr/libexec/tftp-proxy\ttftp-proxy -v\n"); + + if (isset($config['nat']['rule'])) { + /* start reflection redirects on port 19000 of localhost */ + $starting_localhost_port = 19000; + $natrules .= "# NAT Inbound Redirects\n"; + foreach ($config['nat']['rule'] as $rule) { + update_filter_reload_status(sprintf(gettext("Creating NAT rule %s"), $rule['descr'])); + + if (isset($rule['disabled'])) { + continue; + } + + /* if item is an alias, expand */ + $dstport = ""; + $dstport[0] = alias_expand($rule['destination']['port']); + if (!$dstport[0]) { + $dstport = explode("-", $rule['destination']['port']); + } + + /* if item is an alias, expand */ + $localport = alias_expand($rule['local-port']); + if (!$localport || $dstport[0] == $localport) { + $localport = ""; + } else if (is_alias($rule['local-port'])) { + $localport = filter_expand_alias($rule['local-port']); + if ($localport) { + $localport = explode(" ", trim($localport)); + $localport = $localport[0]; + $localport = " port {$localport}"; + } + } else if (is_alias($rule['destination']['port'])) { + $localport = " port {$localport}"; + } else { + if (($dstport[1]) && ($dstport[0] != $dstport[1])) { + $localendport = $localport + ($dstport[1] - $dstport[0]); + + $localport .= ":$localendport"; + } + + $localport = " port {$localport}"; + } + + switch (strtolower($rule['protocol'])) { + case "tcp/udp": + $protocol = "{ tcp udp }"; + break; + case "tcp": + case "udp": + $protocol = strtolower($rule['protocol']); + break; + default: + $protocol = strtolower($rule['protocol']); + $localport = ""; + break; + } + + $target = alias_expand($rule['target']); + if (!$target && !isset($rule['nordr'])) { + $natrules .= "# Unresolvable alias {$rule['target']}\n"; + continue; /* unresolvable alias */ + } + + if (is_alias($rule['target'])) { + $target_ip = filter_expand_alias($rule['target']); + } else if (is_ipaddr($rule['target'])) { + $target_ip = $rule['target']; + } else if (is_ipaddr($FilterIflist[$rule['target']]['ip'])) { + $target_ip = $FilterIflist[$rule['target']]['ip']; + } else { + $target_ip = $rule['target']; + } + $target_ip = trim($target_ip); + + if ($rule['associated-rule-id'] == "pass") { + $rdrpass = "pass "; + } else { + $rdrpass = ""; + } + + if (isset($rule['nordr'])) { + $nordr = "no "; + $rdrpass = ""; + } else { + $nordr = ""; + } + + if (!$rule['interface']) { + $natif = "wan"; + } else { + $natif = $rule['interface']; + } + + if (!isset($FilterIflist[$natif])) { + continue; + } + + $srcaddr = filter_generate_address($rule, 'source', true); + $dstaddr = filter_generate_address($rule, 'destination', true); + $srcaddr = trim($srcaddr); + $dstaddr = trim($dstaddr); + + if (!$dstaddr) { + $dstaddr = $FilterIflist[$natif]['ip']; + } + + $dstaddr_port = explode(" ", $dstaddr); + if (empty($dstaddr_port[0]) || strtolower(trim($dstaddr_port[0])) == "port") { + continue; // Skip port forward if no destination address found + } + $dstaddr_reflect = $dstaddr; + if (isset($rule['destination']['any'])) { + /* With reflection enabled, destination of 'any' has side effects + * that most people would not expect, so change it on reflection rules. */ + + if (!empty($FilterIflist[$natif]['ip'])) { + $dstaddr_reflect = $FilterIflist[$natif]['ip']; + } else { + // no IP, bail + continue; + } + + if (!empty($FilterIflist[$natif]['sn'])) { + $dstaddr_reflect = gen_subnet($dstaddr_reflect, $FilterIflist[$natif]['sn']) . '/' . $FilterIflist[$natif]['sn']; + } + + if ($dstaddr_port[2]) { + $dstaddr_reflect .= " port " . $dstaddr_port[2]; + } + } + + $natif = $FilterIflist[$natif]['if']; + + $reflection_type = "none"; + if ($rule['natreflection'] != "disable" && $dstaddr_port[0] != "0.0.0.0") { + if ($rule['natreflection'] == "enable") { + $reflection_type = "proxy"; + } else if ($rule['natreflection'] == "purenat") { + $reflection_type = "purenat"; + } else if (!isset($config['system']['disablenatreflection'])) { + if (isset($config['system']['enablenatreflectionpurenat'])) { + $reflection_type = "purenat"; + } else { + $reflection_type = "proxy"; + } + } + } + + if ($reflection_type != "none") { + $nat_if_list = filter_get_reflection_interfaces($natif); + } else { + $nat_if_list = array(); + } + + if (empty($nat_if_list)) { + $reflection_type = "none"; + } + + $localport_nat = $localport; + if (empty($localport_nat) && $dstaddr_port[2]) { + $localport_nat = " port " . $dstaddr_port[2]; + } + + if ($srcaddr <> "" && $dstaddr <> "" && $natif) { + $natrules .= "{$nordr}rdr {$rdrpass}on {$natif} proto {$protocol} from {$srcaddr} to {$dstaddr}" . ($nordr == "" ? " -> {$target}{$localport}" : ""); + + /* Does this rule redirect back to a internal host? */ + if (isset($rule['destination']['any']) && !isset($rule['nordr']) && !isset($config['system']['enablenatreflectionhelper']) && !interface_has_gateway($rule['interface'])) { + $rule_interface_ip = find_interface_ip($natif); + $rule_interface_subnet = find_interface_subnet($natif); + if (!empty($rule_interface_ip) && !empty($rule_interface_subnet)) { + $rule_subnet = gen_subnet($rule_interface_ip, $rule_interface_subnet); + $natrules .= "\n"; + $natrules .= "no nat on {$natif} proto tcp from ({$natif}) to {$rule_subnet}/{$rule_interface_subnet}\n"; + $natrules .= "nat on {$natif} proto tcp from {$rule_subnet}/{$rule_interface_subnet} to {$target} port {$dstport[0]} -> ({$natif})\n"; + } + } + + if ($reflection_type != "none") { + if ($reflection_type == "proxy" && !isset($rule['nordr'])) { + $natrules .= filter_generate_reflection_proxy($rule, $nordr, $nat_if_list, $srcaddr, $dstaddr, $starting_localhost_port, $reflection_rules); + $nat_if_list = array($natif); + foreach ($reflection_rules as $txtline) { + fwrite($inetd_fd, $txtline); + } + } else if ($reflection_type == "purenat" || isset($rule['nordr'])) { + $rdr_if_list = implode(" ", $nat_if_list); + if (count($nat_if_list) > 1) { + $rdr_if_list = "{ {$rdr_if_list} }"; + } + $natrules .= "\n# Reflection redirect\n"; + $natrules .= "{$nordr}rdr {$rdrpass}on {$rdr_if_list} proto {$protocol} from {$srcaddr} to {$dstaddr_reflect}" . ($nordr == "" ? " -> {$target}{$localport}" : ""); + $nat_if_list = array_merge(array($natif), $nat_if_list); + } + } + + if (empty($nat_if_list)) { + $nat_if_list = array($natif); + } + + $natrules .= "\n"; + if (!isset($rule['nordr'])) { + $natrules .= filter_generate_reflection_nat($rule, $route_table, $nat_if_list, $protocol, "{$target}{$localport_nat}", $target_ip); + } + } + } + } + fclose($inetd_fd); // Close file handle + + if (isset($config['pptpd']['mode']) && ($config['pptpd']['mode'] != "off")) { + if ($config['pptpd']['mode'] == "redir") { + $pptpdtarget = $config['pptpd']['redir']; + $natrules .= "# PPTP\n"; + $natrules .= "rdr on \${$FilterIflist['wan']['descr']} proto gre from any to any -> {$pptpdtarget}\n"; + $natrules .= "rdr on \${$FilterIflist['wan']['descr']} proto tcp from any to any port 1723 -> {$pptpdtarget}\n"; + } + } + + $natrules .= discover_pkg_rules("nat"); + + $natrules .= "# UPnPd rdr anchor\n"; + $natrules .= "rdr-anchor \"miniupnpd\"\n"; + + if (!empty($reflection_txt)) { + $natrules .= "\n# Reflection redirects and NAT for 1:1 mappings\n" . $reflection_txt; + } + + // Check if inetd is running, if not start it. If so, restart it gracefully. + $helpers = isvalidproc("inetd"); + if (file_exists("/var/etc/inetd.conf")) { + if (!$helpers) { + mwexec("/usr/sbin/inetd -wW -R 0 -a 127.0.0.1 /var/etc/inetd.conf"); + } else { + sigkillbypid("/var/run/inetd.pid", "HUP"); + } + } + + return $natrules; +} + +function filter_generate_user_rule_arr($rule) { + global $config; + update_filter_reload_status(sprintf(gettext("Creating filter rule %s ..."), $rule['descr'])); + $ret = array(); + $line = filter_generate_user_rule($rule); + $ret['rule'] = $line; + $ret['interface'] = $rule['interface']; + if ($rule['descr'] != "" and $line != "") { + $ret['descr'] = "label \"" . fix_rule_label("USER_RULE: {$rule['descr']}") . "\""; + } else { + $ret['descr'] = "label \"USER_RULE\""; + } + + return $ret; +} + +function filter_generate_port(& $rule, $target = "source", $isnat = false) { + + $src = ""; + + $rule['protocol'] = strtolower($rule['protocol']); + if (in_array($rule['protocol'], array("tcp", "udp", "tcp/udp"))) { + if ($rule[$target]['port']) { + $srcport = explode("-", $rule[$target]['port']); + $srcporta = alias_expand($srcport[0]); + if (!$srcporta) { + log_error(sprintf(gettext("filter_generate_port: %s is not a valid {$target} port."), $srcport[0])); + } else if ((!$srcport[1]) || ($srcport[0] == $srcport[1])) { + $src .= " port {$srcporta} "; + } else if (($srcport[0] == 1) && ($srcport[1] == 65535)) { + /* no need for a port statement here */ + } else if ($isnat) { + $src .= " port {$srcport[0]}:{$srcport[1]}"; + } else { + if (is_port($srcporta) && $srcport[1] == 65535) { + $src .= " port >= {$srcporta} "; + } else if ($srcport[0] == 1) { + $src .= " port <= {$srcport[1]} "; + } else { + $srcport[0]--; + $srcport[1]++; + $src .= " port {$srcport[0]} >< {$srcport[1]} "; + } + } + } + } + + return $src; +} + +function filter_address_add_vips_subnets(&$subnets, $if, $not) { + global $FilterIflist; + + $if_subnets = array($subnets); + + if ($not == true) { + $subnets = "!{$subnets}"; + } + + if (!isset($FilterIflist[$if]['vips']) || !is_array($FilterIflist[$if]['vips'])) { + return; + } + + foreach ($FilterIflist[$if]['vips'] as $vip) { + foreach ($if_subnets as $subnet) { + if (ip_in_subnet($vip['ip'], $subnet)) { + continue 2; + } + } + + if (is_ipaddrv4($vip['ip'])) { + if (!is_subnetv4($if_subnets[0])) { + continue; + } + + $network = gen_subnet($vip['ip'], $vip['sn']); + } else if (is_ipaddrv6($vip['ip'])) { + if (!is_subnetv6($if_subnets[0])) { + continue; + } + + $network = gen_subnetv6($vip['ip'], $vip['sn']); + } else { + continue; + } + + $subnets .= ' ' . ($not == true ? '!' : '') . $network . '/' . $vip['sn']; + $if_subnets[] = $network . '/' . $vip['sn']; + } + unset($if_subnets); + + if (strpos($subnets, ' ') !== false) { + $subnets = "{ {$subnets} }"; + } +} + +function filter_generate_address(& $rule, $target = "source", $isnat = false) { + global $FilterIflist, $config; + $src = ""; + + if (isset($rule[$target]['any'])) { + $src = "any"; + } else if ($rule[$target]['network']) { + if (strstr($rule[$target]['network'], "opt")) { + $optmatch = ""; + $matches = ""; + if ($rule['ipprotocol'] == "inet6") { + if (preg_match("/opt([0-9]*)$/", $rule[$target]['network'], $optmatch)) { + $opt_sa = $FilterIflist["opt{$optmatch[1]}"]['sav6']; + if (!is_ipaddrv6($opt_sa)) { + return ""; + } + $src = $opt_sa . "/" . $FilterIflist["opt{$optmatch[1]}"]['snv6']; + /* check for opt$NUMip here */ + } else if (preg_match("/opt([0-9]*)ip/", $rule[$target]['network'], $matches)) { + $src = $FilterIflist["opt{$matches[1]}"]['ipv6']; + if (!is_ipaddrv6($src)) { + return ""; + } + if (isset($rule[$target]['not'])) { + $src = " !{$src}"; + } + } + } else { + if (preg_match("/opt([0-9]*)$/", $rule[$target]['network'], $optmatch)) { + $opt_sa = $FilterIflist["opt{$optmatch[1]}"]['sa']; + if (!is_ipaddrv4($opt_sa)) { + return ""; + } + $src = $opt_sa . "/" . $FilterIflist["opt{$optmatch[1]}"]['sn']; + /* check for opt$NUMip here */ + } else if (preg_match("/opt([0-9]*)ip/", $rule[$target]['network'], $matches)) { + $src = $FilterIflist["opt{$matches[1]}"]['ip']; + if (!is_ipaddrv4($src)) { + return ""; + } + if (isset($rule[$target]['not'])) { + $src = " !{$src}"; + } + } + } + } else { + if ($rule['ipprotocol'] == "inet6") { + switch ($rule[$target]['network']) { + case 'wan': + $wansa = $FilterIflist['wan']['sav6']; + if (!is_ipaddrv6($wansa)) { + return ""; + } + $wansn = $FilterIflist['wan']['snv6']; + $src = "{$wansa}/{$wansn}"; + break; + case 'wanip': + $src = $FilterIflist["wan"]['ipv6']; + if (!is_ipaddrv6($src)) { + return ""; + } + break; + case 'lanip': + $src = $FilterIflist["lan"]['ipv6']; + if (!is_ipaddrv6($src)) { + return ""; + } + break; + case 'lan': + $lansa = $FilterIflist['lan']['sav6']; + if (!is_ipaddrv6($lansa)) { + return ""; + } + $lansn = $FilterIflist['lan']['snv6']; + $src = "{$lansa}/{$lansn}"; + break; + case '(self)': + $src = "(self)"; + break; + case 'pptp': + $pptpsav6 = gen_subnetv6($FilterIflist['pptp']['sav6'], $FilterIflist['pptp']['snv6']); + $pptpsnv6 = $FilterIflist['pptp']['snv6']; + $src = "{$pptpsav6}/{$pptpsnv6}"; + break; + case 'pppoe': + if (is_array($FilterIflist['pppoe'])) { + $pppoesav6 = gen_subnetv6($FilterIflist['pppoe'][0]['ipv6'], $FilterIflist['pppoe'][0]['snv6']); + $pppoesnv6 = $FilterIflist['pppoe'][0]['snv6']; + $src = "{$pppoesav6}/{$pppoesnv6}"; + } + } + if (isset($rule[$target]['not']) && !is_subnet($src)) { + $src = " !{$src}"; + } + } else { + switch ($rule[$target]['network']) { + case 'wan': + $wansa = $FilterIflist['wan']['sa']; + if (!is_ipaddrv4($wansa)) { + return ""; + } + $wansn = $FilterIflist['wan']['sn']; + $src = "{$wansa}/{$wansn}"; + break; + case 'wanip': + $src = $FilterIflist["wan"]['ip']; + break; + case 'lanip': + $src = $FilterIflist["lan"]['ip']; + break; + case 'lan': + $lansa = $FilterIflist['lan']['sa']; + if (!is_ipaddrv4($lansa)) { + return ""; + } + $lansn = $FilterIflist['lan']['sn']; + $src = "{$lansa}/{$lansn}"; + break; + case '(self)': + $src = "(self)"; + break; + case 'pptp': + if (isset($config['pptpd']['n_pptp_units']) && is_numeric($config['pptpd']['n_pptp_units'])) { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], long2ip32(ip2long($config['pptpd']['remoteip'])+($config['pptpd']['n_pptp_units']-1))); + } else { + $pptp_subnets = ip_range_to_subnet_array($config['pptpd']['remoteip'], long2ip32(ip2long($config['pptpd']['remoteip']))); + } + if (empty($pptp_subnets)) { + return ""; + } + if (isset($rule[$target]['not'])) { + array_walk($pptp_subnets, function (&$value, $key) { + $value="!{$value}"; + }); + } + $src = "{ " . implode(" ", $pptp_subnets) . " }"; + break; + case 'pppoe': + /* XXX: This needs to be fixed somehow! */ + if (is_array($FilterIflist['pppoe'])) { + $pppoesa = gen_subnet($FilterIflist['pppoe'][0]['ip'], $FilterIflist['pppoe'][0]['sn']); + $pppoesn = $FilterIflist['pppoe'][0]['sn']; + $src = "{$pppoesa}/{$pppoesn}"; + } + break; + } + if ((isset($rule[$target]['not'])) && + (!is_subnet($src)) && + (strpos($src, '{') === false)) { + $src = " !{$src}"; + } + } + } + if (is_subnet($src)) { + filter_address_add_vips_subnets($src, $rule[$target]['network'], isset($rule[$target]['not'])); + } + } else if ($rule[$target]['address']) { + $expsrc = alias_expand($rule[$target]['address']); + if (isset($rule[$target]['not'])) { + $not = "!"; + } else { + $not = ""; + } + $src = " {$not} {$expsrc}"; + } + + if (empty($src)) { + return ''; + } + + $src .= filter_generate_port($rule, $target, $isnat); + + return $src; +} + +function filter_generate_user_rule($rule) { + global $config, $g, $FilterIflist, $GatewaysList; + global $layer7_rules_list, $dummynet_name_list; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_user_rule() being called $mt\n"; + } + /* don't include disabled rules */ + if (isset($rule['disabled'])) { + return "# rule " . $rule['descr'] . " disabled \n"; + } + update_filter_reload_status("Creating filter rules {$rule['descr']} ..."); + $pptpdcfg = $config['pptpd']; + $int = ""; + $aline = array(); + + /* Check to see if the interface is in our list */ + if (isset($rule['floating'])) { + if (isset($rule['interface']) && $rule['interface'] <> "") { + $interfaces = explode(",", $rule['interface']); + $ifliste = ""; + foreach ($interfaces as $iface) { + if (array_key_exists($iface, $FilterIflist)) { + $ifliste .= " " . $FilterIflist[$iface]['if'] . " "; + } + } + if ($ifliste <> "") { + $aline['interface'] = " on { {$ifliste} } "; + } else { + $aline['interface'] = ""; + } + } else { + $aline['interface'] = ""; + } + } else if (!array_key_exists($rule['interface'], $FilterIflist)) { + foreach ($FilterIflist as $oc) { + $items .= $oc['descr'] . " "; + } + return "# array key \"{$rule['interface']}\" does not exist for \"" . $rule['descr'] . "\" in array: {{$items}}"; + } else if ((array_key_exists($rule['interface'], $FilterIflist)) && + (is_array($FilterIflist[$rule['interface']])) && + (is_array($FilterIflist[$rule['interface']][0]))) { + /* Currently the only case for this is the pppoe server. There should be an existing macro with this name. */ + $aline['interface'] = " on \$" . $rule['interface'] . " "; + } else { + $aline['interface'] = " on \$" . $FilterIflist[$rule['interface']]['descr'] . " "; + } + $ifcfg = $FilterIflist[$rule['interface']]; + if ($pptpdcfg['mode'] != "server") { + if (($rule['source']['network'] == "pptp") || + ($rule['destination']['network'] == "pptp")) { + return "# source network or destination network == pptp on " . $rule['descr']; + } + } + + switch ($rule['ipprotocol']) { + case "inet": + $aline['ipprotocol'] = "inet"; + break; + case "inet6": + $aline['ipprotocol'] = "inet6"; + break; + default: + $aline['ipprotocol'] = ""; + break; + } + + /* check for unresolvable aliases */ + if ($rule['source']['address'] && !alias_expand($rule['source']['address'])) { + $error_text = "Unresolvable source alias '{$rule['source']['address']}' for rule '{$rule['descr']}'"; + file_notice("Filter_Reload", $error_text); + return "# {$error_text}"; + } + if ($rule['destination']['address'] && !alias_expand($rule['destination']['address'])) { + $error_text = "Unresolvable destination alias '{$rule['destination']['address']}' for rule '{$rule['descr']}'"; + file_notice("Filter_Reload", $error_text); + return "# {$error_text}"; + } + update_filter_reload_status("Setting up pass/block rules"); + $type = $rule['type']; + if ($type != "pass" && $type != "block" && $type != "reject" && $type != "match") { + /* default (for older rules) is pass */ + $type = "pass"; + } + if ($type == "reject") { + $aline['type'] = "block return "; + } else { + $aline['type'] = $type . " "; + } + if (isset($rule['floating']) && $rule['floating'] == "yes") { + if ($rule['direction'] != "any") { + $aline['direction'] = " " . $rule['direction'] . " "; + } + } else { + /* ensure the direction is in */ + $aline['direction'] = " in "; + } + if (isset($rule['log'])) { + $aline['log'] = "log "; + } + if (!isset($rule['floating']) || isset($rule['quick'])) { + $aline['quick'] = " quick "; + } + + /* set the gateway interface */ + update_filter_reload_status(sprintf(gettext("Setting up pass/block rules %s"), $rule['descr'])); + + /* do not process reply-to for gateway'd rules */ + if ($rule['gateway'] == "" && $aline['direction'] <> "" && (interface_has_gateway($rule['interface']) || interface_has_gatewayv6($rule['interface'])) && !isset($config['system']['disablereplyto']) && !isset($rule['disablereplyto']) && $type != "match") { + if ($rule['ipprotocol'] == "inet6") { + $rg = get_interface_gateway_v6($rule['interface']); + if (is_ipaddrv6($rg)) { + $aline['reply'] = "reply-to ( {$ifcfg['ifv6']} {$rg} ) "; + } else if ($rule['interface'] <> "pptp") { + log_error(sprintf(gettext("Could not find IPv6 gateway for interface (%s)."), $rule['interface'])); + } + } else { + $rg = get_interface_gateway($rule['interface']); + if (is_ipaddrv4($rg)) { + $aline['reply'] = "reply-to ( {$ifcfg['if']} {$rg} ) "; + } else if ($rule['interface'] <> "pptp") { + log_error(sprintf(gettext("Could not find IPv4 gateway for interface (%s)."), $rule['interface'])); + } + } + } + /* if user has selected a custom gateway, lets work with it */ + else if ($rule['gateway'] <> "" && $type == "pass") { + if (isset($GatewaysList[$rule['gateway']])) { + /* Add the load balanced gateways */ + $aline['route'] = " \$GW{$rule['gateway']} "; + } else if (isset($config['system']['skip_rules_gw_down'])) { + return "# rule " . $rule['descr'] . " disabled because gateway " . $rule['gateway'] . " is down "; + } else { + log_error("The gateway: {$rule['gateway']} is invalid or unknown, not using it."); + } + } + + if (isset($rule['protocol']) && !empty($rule['protocol'])) { + if ($rule['protocol'] == "tcp/udp") { + $aline['prot'] = " proto { tcp udp } "; + } elseif (($rule['protocol'] == "icmp") && ($rule['ipprotocol'] == "inet6")) { + $aline['prot'] = " proto ipv6-icmp "; + } elseif ($rule['protocol'] == "icmp") { + $aline['prot'] = " proto icmp "; + } else { + $aline['prot'] = " proto {$rule['protocol']} "; + } + } else { + if ($rule['source']['port'] <> "" || $rule['destination']['port'] <> "") { + $aline['prot'] = " proto tcp "; + } + } + update_filter_reload_status(sprintf(gettext("Creating rule %s"), $rule['descr'])); + + /* source address */ + $src = trim(filter_generate_address($rule, "source")); + if (empty($src) || ($src == "/")) { + return "# at the break!"; + } + $aline['src'] = " from $src "; + + /* OS signatures */ + if (($rule['protocol'] == "tcp") && ($rule['os'] <> "")) { + $aline['os'] = " os \"{$rule['os']}\" "; + } + + /* destination address */ + $dst = trim(filter_generate_address($rule, "destination")); + if (empty($dst) || ($dst == "/")) { + return "# returning at dst $dst == \"/\""; + } + $aline['dst'] = "to $dst "; + + //Layer7 support + $l7_present = false; + $l7_structures = array(); + if (isset($rule['l7container']) && $rule['l7container'] != "none") { + $l7_present = true; + $l7rule =& $layer7_rules_list[$rule['l7container']]; + $l7_structures = $l7rule->get_unique_structures(); + $aline['divert'] = "divert-to " . $l7rule->GetRPort() . " "; + } + if (($rule['protocol'] == "icmp") && $rule['icmptype'] && ($rule['ipprotocol'] == "inet")) { + $aline['icmp-type'] = "icmp-type {$rule['icmptype']} "; + } + if (($rule['protocol'] == "icmp") && $rule['icmptype'] && ($rule['ipprotocol'] == "inet6")) { + $aline['icmp6-type'] = "icmp6-type {$rule['icmptype']} "; + } + if (!empty($rule['tag'])) { + if (ctype_digit($rule['tag'])) { + $aline['tag'] = " tag \"" .$rule['tag']. "\" "; + } else { + $aline['tag'] = " tag " .$rule['tag']. " "; + } + } + if (!empty($rule['tagged'])) { + $aline['tagged'] = " tagged " .$rule['tagged'] . " "; + } + if (!empty($rule['dscp'])) { + switch (strtolower($rule['dscp'])) { + case 'va': + $aline['dscp'] = " dscp \"44\" "; + break; + case 'VA': + $aline['dscp'] = " dscp \"44\" "; + break; + case 'cs1': + $aline['dscp'] = " dscp \"8\" "; + break; + case 'cs2': + $aline['dscp'] = " dscp \"16\" "; + break; + case 'cs3': + $aline['dscp'] = " dscp \"24\" "; + break; + case 'cs4': + $aline['dscp'] = " dscp \"32\" "; + break; + case 'cs5': + $aline['dscp'] = " dscp \"40\" "; + break; + case 'cs6': + $aline['dscp'] = " dscp \"48\" "; + break; + case 'cs7': + $aline['dscp'] = " dscp \"56\" "; + break; + default: + $aline['dscp'] = " dscp " . $rule['dscp'] . " "; + break; + } + } + if (!empty($rule['vlanprio']) && ($rule['vlanprio'] != "none")) { + $aline['vlanprio'] = " ieee8021q-pcp " . $rule['vlanprio'] . " "; + } + if (!empty($rule['vlanprioset']) && ($rule['vlanprioset'] != "none")) { + $aline['vlanprioset'] = " ieee8021q-setpcp " . $rule['vlanprioset'] . " "; + } + if ($type == "pass") { + if (isset($rule['allowopts'])) { + $aline['allowopts'] = " allow-opts "; + } + } + $aline['flags'] = ""; + if ($rule['protocol'] == "tcp") { + if (isset($rule['tcpflags_any'])) { + $aline['flags'] = "flags any "; + } else if (!empty($rule['tcpflags2'])) { + $aline['flags'] = "flags "; + if (!empty($rule['tcpflags1'])) { + $flags1 = explode(",", $rule['tcpflags1']); + foreach ($flags1 as $flag1) { + // CWR flag needs special treatment + if ($flag1[0] == "c") { + $aline['flags'] .= "W"; + } else { + $aline['flags'] .= strtoupper($flag1[0]); + } + } + } + $aline['flags'] .= "/"; + if (!empty($rule['tcpflags2'])) { + $flags2 = explode(",", $rule['tcpflags2']); + foreach ($flags2 as $flag2) { + // CWR flag needs special treatment + if ($flag2[0] == "c") { + $aline['flags'] .= "W"; + } else { + $aline['flags'] .= strtoupper($flag2[0]); + } + } + } + $aline['flags'] .= " "; + } else { + $aline['flags'] = "flags S/SA "; + } + } + if ($type == "pass") { + /* + * # keep state + * works with TCP, UDP, and ICMP. + * # modulate state + * works only with TCP. pfSense will generate strong Initial Sequence Numbers (ISNs) + * for packets matching this rule. + * # synproxy state + * proxies incoming TCP connections to help protect servers from spoofed TCP SYN floods. + * This option includes the functionality of keep state and modulate state combined. + * # none + * do not use state mechanisms to keep track. this is only useful if your doing advanced + * queueing in certain situations. please check the faq. + */ + $noadvoptions = false; + if (isset($rule['statetype']) && $rule['statetype'] <> "") { + switch ($rule['statetype']) { + case "none": + $noadvoptions = true; + $aline['flags'] .= " no state "; + break; + case "modulate state": + case "synproxy state": + if ($rule['protocol'] == "tcp") { + $aline['flags'] .= "{$rule['statetype']} "; + } + break; + case "sloppy state": + $aline['flags'] .= "keep state "; + $rule['sloppy'] = true; + break; + default: + $aline['flags'] .= "{$rule['statetype']} "; + break; + } + } else { + $aline['flags'] .= "keep state "; + } + + if ($noadvoptions == false && isset($rule['nopfsync'])) { + $rule['nopfsync'] = true; + } + + if ($noadvoptions == false || $l7_present) { + if ((isset($rule['source-track']) and $rule['source-track'] <> "") or + (isset($rule['max']) and $rule['max'] <> "") or + (isset($rule['max-src-nodes']) and $rule['max-src-nodes'] <> "") or + (isset($rule['max-src-states']) and $rule['max-src-states'] <> "") or + ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + ((isset($rule['statetimeout']) and $rule['statetimeout'] <> "") or + (isset($rule['max-src-conn']) and $rule['max-src-conn'] <> "") or + (isset($rule['max-src-conn-rate']) and $rule['max-src-conn-rate'] <> "") or + (isset($rule['max-src-conn-rates']) and $rule['max-src-conn-rates'] <> ""))) or + (isset($rule['sloppy'])) or + (isset($rule['nopfsync'])) or + ($l7_present)) { + $aline['flags'] .= "( "; + if (isset($rule['sloppy'])) { + $aline['flags'] .= "sloppy "; + } + if (isset($rule['nopfsync'])) { + $aline['flags'] .= "no-sync "; + } + if (isset($rule['source-track']) and $rule['source-track'] <> "") { + $aline['flags'] .= "source-track rule "; + } + if (isset($rule['max']) and $rule['max'] <> "") { + $aline['flags'] .= "max " . $rule['max'] . " "; + } + if (isset($rule['max-src-nodes']) and $rule['max-src-nodes'] <> "") { + $aline['flags'] .= "max-src-nodes " . $rule['max-src-nodes'] . " "; + } + if ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + (isset($rule['max-src-conn'])) and + ($rule['max-src-conn'] <> "")) { + $aline['flags'] .= "max-src-conn " . $rule['max-src-conn'] . " "; + } + if (isset($rule['max-src-states']) and $rule['max-src-states'] <> "") { + $aline['flags'] .= "max-src-states " . $rule['max-src-states'] . " "; + } + if ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + (isset($rule['statetimeout'])) and + ($rule['statetimeout'] <> "")) { + $aline['flags'] .= "tcp.established " . $rule['statetimeout'] . " "; + } + if ((in_array($rule['protocol'], array("tcp", "tcp/udp"))) and + (isset($rule['max-src-conn-rate'])) and + ($rule['max-src-conn-rate'] <> "") and + (isset($rule['max-src-conn-rates'])) and + ($rule['max-src-conn-rates'] <> "")) { + $aline['flags'] .= "max-src-conn-rate " . $rule['max-src-conn-rate'] . " "; + $aline['flags'] .= "/" . $rule['max-src-conn-rates'] . ", overload flush global "; + } + + if (!empty($aline['divert'])) { + $aline['flags'] .= "max-packets 8 "; + } + + $aline['flags'] .= " ) "; + } + } + } + if ($rule['defaultqueue'] <> "") { + $aline['queue'] = " queue (".$rule['defaultqueue']; + if ($rule['ackqueue'] <> "") { + $aline['queue'] .= "," . $rule['ackqueue']; + } + $aline['queue'] .= ") "; + } + if ($rule['dnpipe'] <> "") { + if (!empty($dummynet_name_list[$rule['dnpipe']])) { + if ($dummynet_name_list[$rule['dnpipe']][0] == "?") { + $aline['dnpipe'] = " dnqueue( "; + $aline['dnpipe'] .= substr($dummynet_name_list[$rule['dnpipe']], 1); + if ($rule['pdnpipe'] <> "") { + $aline['dnpipe'] .= "," . substr($dummynet_name_list[$rule['pdnpipe']], 1); + } + } else { + $aline['dnpipe'] = " dnpipe ( " . $dummynet_name_list[$rule['dnpipe']]; + if ($rule['pdnpipe'] <> "") { + $aline['dnpipe'] .= "," . $dummynet_name_list[$rule['pdnpipe']]; + } + } + $aline['dnpipe'] .= ") "; + } + } + + /* is a time based rule schedule attached? */ + if (!empty($rule['sched']) && !empty($config['schedules'])) { + $aline['schedlabel'] = ""; + foreach ($config['schedules']['schedule'] as $sched) { + if ($sched['name'] == $rule['sched']) { + if (!filter_get_time_based_rule_status($sched)) { + if (!isset($config['system']['schedule_states'])) { + mwexec("/sbin/pfctl -y {$sched['schedlabel']}"); + } + return "# schedule finished - {$rule['descr']}"; + } else if ($g['debug']) { + log_error("[TDR DEBUG] status true -- rule type '$type'"); + } + + $aline['schedlabel'] = " schedule \"{$sched['schedlabel']}\" "; + break; + } + } + } + + if (!empty($rule['tracker'])) { + $aline['tracker'] = "tracker {$rule['tracker']} "; + } + + $line = ""; + /* exception(s) to a user rules can go here. */ + /* rules with a gateway or pool should create another rule for routing to vpns */ + if ((($aline['route'] <> "") && (trim($aline['type']) == "pass") && strstr($dst, "any")) && (!isset($config['system']['disablenegate']))) { + /* negate VPN/PPTP/PPPoE/Static Route networks for load balancer/gateway rules */ + $negate_networks = " to " . filter_generate_port($rule, "destination"); + $line .= $aline['type'] . $aline['direction'] . $aline['log'] . $aline['quick'] . + $aline['interface'] . $aline['ipprotocol'] . $aline['prot'] . $aline['src'] . $aline['os'] . + $negate_networks . $aline['icmp-type'] . $aline['icmp6-type'] . $aline['tag'] . $aline['tagged'] . + $aline['vlanprio'] . $aline['vlanprioset'] . $aline['dscp'] . filter_negaterule_tracker() . $aline['allowopts'] . $aline['flags'] . + $aline['queue'] . $aline['dnpipe'] . $aline['schedlabel'] . + " label \"NEGATE_ROUTE: Negate policy routing for destination\"\n"; + + } + /* piece together the actual user rule */ + $line .= $aline['type'] . $aline['direction'] . $aline['log'] . $aline['quick'] . $aline['interface'] . + $aline['reply'] . $aline['route'] . $aline['ipprotocol'] . $aline['prot'] . $aline['src'] . $aline['os'] . $aline['dst'] . + $aline['divert'] . $aline['icmp-type'] . $aline['icmp6-type'] . $aline['tag'] . $aline['tagged'] . $aline['dscp'] . $aline['tracker'] . + $aline['vlanprio'] . $aline['vlanprioset'] . $aline['allowopts'] . $aline['flags'] . $aline['queue'] . $aline['dnpipe'] . $aline['schedlabel']; + + unset($aline); + + return $line; +} + +function filter_rules_generate() { + global $config, $g, $FilterIflist, $time_based_rules, $GatewaysList, $tracker; + + $fix_rule_label = 'fix_rule_label'; + $increment_tracker = 'filter_rule_tracker'; + + update_filter_reload_status(gettext("Creating default rules")); + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_rules_generate() being called $mt\n"; + } + + $pptpdcfg = $config['pptpd']; + + $ipfrules = ""; + $ipfrules .= discover_pkg_rules("pfearly"); + + /* relayd */ + $ipfrules .= "anchor \"relayd/*\"\n"; + /* OpenVPN user rules from radius */ + $ipfrules .= "anchor \"openvpn/*\"\n"; + /* IPsec user rules from radius */ + $ipfrules .= "anchor \"ipsec/*\"\n"; + # BEGIN OF firewall rules + /* default block logging? */ + $log = array(); + if (!isset($config['syslog']['nologdefaultblock'])) { + $log['block'] = "log"; + } + if (isset($config['syslog']['nologdefaultpass'])) { + $log['pass'] = "log"; + } + + $saved_tracker = $tracker; + + if (!isset($config['system']['ipv6allow'])) { + $ipfrules .= "# Allow IPv6 on loopback\n"; + $ipfrules .= "pass in {$log['pass']} quick on \$loopback inet6 all tracker {$increment_tracker($tracker)} label \"pass IPv6 loopback\"\n"; + $ipfrules .= "pass out {$log['pass']} quick on \$loopback inet6 all tracker {$increment_tracker($tracker)} label \"pass IPv6 loopback\"\n"; + $ipfrules .= "# Block all IPv6\n"; + $ipfrules .= "block in {$log['block']} quick inet6 all tracker {$increment_tracker($tracker)} label \"Block all IPv6\"\n"; + $ipfrules .= "block out {$log['block']} quick inet6 all tracker {$increment_tracker($tracker)} label \"Block all IPv6\"\n"; + } + + $saved_tracker += 100; + $tracker = $saved_tracker; + + if (!isset($config['system']['no_apipa_block'])) { + $ipfrules .= << to any tracker {$increment_tracker($tracker)} label "Block snort2c hosts" +block {$log['block']} quick from any to tracker {$increment_tracker($tracker)} label "Block snort2c hosts" + +EOD; + + $saved_tracker += 100; + $tracker = $saved_tracker; + + $ipfrules .= filter_process_carp_rules($log); + + $saved_tracker += 100; + $tracker = $saved_tracker; + + $ipfrules .= "\n# SSH lockout\n"; + if (is_array($config['system']['ssh']) && !empty($config['system']['ssh']['port'])) { + $ipfrules .= "block in {$log['block']} quick proto tcp from to (self) port "; + $ipfrules .= $config['system']['ssh']['port']; + $ipfrules .= " tracker {$increment_tracker($tracker)} label \"sshlockout\"\n"; + } else { + if ($config['system']['ssh']['port'] <> "") { + $sshport = $config['system']['ssh']['port']; + } else { + $sshport = 22; + } + if ($sshport) { + $ipfrules .= "block in {$log['block']} quick proto tcp from to (self) port {$sshport} tracker {$increment_tracker($tracker)} label \"sshlockout\"\n"; + } + } + + $saved_tracker += 50; + $tracker = $saved_tracker; + + $ipfrules .= "\n# webConfigurator lockout\n"; + if (!$config['system']['webgui']['port']) { + if ($config['system']['webgui']['protocol'] == "http") { + $webConfiguratorlockoutport = "80"; + } else { + $webConfiguratorlockoutport = "443"; + } + } else { + $webConfiguratorlockoutport = $config['system']['webgui']['port']; + } + if ($webConfiguratorlockoutport) { + $ipfrules .= "block in {$log['block']} quick proto tcp from to (self) port {$webConfiguratorlockoutport} tracker {$increment_tracker($tracker)} label \"webConfiguratorlockout\"\n"; + } + + $saved_tracker += 100; + $tracker = $saved_tracker; + + /* + * Support for allow limiting of TCP connections by establishment rate + * Useful for protecting against sudden outbursts, etc. + */ + $ipfrules .= "block in {$log['block']} quick from to any tracker 1000000400 label \"virusprot overload table\"\n"; + + $saved_tracker += 100; + $tracker = $saved_tracker; + + /* if captive portal is enabled, ensure that access to this port + * is allowed on a locked down interface + */ + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpcfg) { + if (!isset($cpcfg['enable'])) { + continue; + } + $cpinterfaces = explode(",", $cpcfg['interface']); + $cpiflist = array(); + $cpiplist = array(); + foreach ($cpinterfaces as $cpifgrp) { + if (!isset($FilterIflist[$cpifgrp])) { + continue; + } + $tmpif = get_real_interface($cpifgrp); + if (!empty($tmpif)) { + $cpiflist[] = "{$tmpif}"; + $cpipm = get_interface_ip($cpifgrp); + if (is_ipaddr($cpipm)) { + $cpiplist[] = $cpipm; + if (!is_array($config['virtualip']) || !is_array($config['virtualip']['vip'])) { + continue; + } + foreach ($config['virtualip']['vip'] as $vip) { + if (($vip['interface'] == $cpifgrp) && (($vip['mode'] == "carp") || ($vip['mode'] == "ipalias"))) { + $cpiplist[] = $vip['subnet']; + } + } + } + } + } + if (count($cpiplist) > 0 && count($cpiflist) > 0) { + $cpinterface = implode(" ", $cpiflist); + $cpaddresses = implode(" ", $cpiplist); + $listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : 8000 + ($cpcfg['zoneid'] + 1); + $listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : 8000 + $cpcfg['zoneid']; + $portalias = $listenporthttps; + $portalias .= " {$listenporthttp}"; + $ipfrules .= "pass in {$log['pass']} quick on { {$cpinterface} } proto tcp from any to { {$cpaddresses} } port { {$portalias} } tracker {$increment_tracker($tracker)} keep state(sloppy)\n"; + $ipfrules .= "pass out {$log['pass']} quick on { {$cpinterface} } proto tcp from any to any flags any tracker {$increment_tracker($tracker)} keep state(sloppy)\n"; + } + } + } + + $bogontableinstalled = 0; + foreach ($FilterIflist as $on => $oc) { + $saved_tracker += 10; + $tracker = $saved_tracker; + + if (isset($config['system']['ipv6allow']) && ($oc['type6'] == "slaac" || $oc['type6'] == "dhcp6")) { + // The DHCPv6 client rules ***MUST BE ABOVE BOGONSV6!*** https://redmine.pfsense.org/issues/3395 + $ipfrules .= << to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("block bogon IPv4 networks from {$oc['descr']}")}" + +EOD; + + if (isset($config['system']['ipv6allow'])) { + $ipfrules .= << to any tracker {$increment_tracker($tracker)} label "{$fix_rule_label("block bogon IPv6 networks from {$oc['descr']}")}" + +EOD; + } + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + + $isbridged = false; + if (is_array($config['bridges']['bridged'])) { + foreach ($config['bridges']['bridged'] as $oc2) { + if (stristr($oc2['members'], $on)) { + $isbridged = true; + break; + } + } + } + + if ($oc['ip'] && !($isbridged) && isset($oc['spoofcheck'])) { + $ipfrules .= filter_rules_spoofcheck_generate($on, $oc, $log); + } + + /* block private networks ? */ + if (!isset($config['syslog']['nologprivatenets'])) { + $privnetlog = "log"; + } else { + $privnetlog = ""; + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + + if (isset($config['interfaces'][$on]['blockpriv'])) { + if ($isbridged == false) { + $ipfrules .= << "") { + $ipfrules .= << $ifcfg) { + if (isset($ifcfg['virtual'])) { + continue; + } + + $gw = get_interface_gateway($ifdescr); + if (is_ipaddrv4($gw) && is_ipaddrv4($ifcfg['ip'])) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$ifcfg['if']} {$gw} ) from {$ifcfg['ip']} to !{$ifcfg['sa']}/{$ifcfg['sn']} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + if (is_array($ifcfg['vips'])) { + foreach ($ifcfg['vips'] as $vip) { + if (ip_in_subnet($vip['ip'], "{$ifcfg['sa']}/{$ifcfg['sn']}")) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$ifcfg['if']} {$gw} ) from {$vip['ip']} to !{$ifcfg['sa']}/{$ifcfg['sn']} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + } else { + $ipfrules .= "pass out {$log['pass']} route-to ( {$ifcfg['if']} {$gw} ) from {$vip['ip']} to !" . gen_subnet($vip['ip'], $vip['sn']) . "/{$vip['sn']} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + } + } + } + } + + $gwv6 = get_interface_gateway_v6($ifdescr); + $stf = get_real_interface($ifdescr, "inet6"); + $pdlen = 64 - calculate_ipv6_delegation_length($ifdescr); + if (is_ipaddrv6($gwv6) && is_ipaddrv6($ifcfg['ipv6'])) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$stf} {$gwv6} ) inet6 from {$ifcfg['ipv6']} to !{$ifcfg['ipv6']}/{$pdlen} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + if (is_array($ifcfg['vips6'])) { + foreach ($ifcfg['vips6'] as $vip) { + $ipfrules .= "pass out {$log['pass']} route-to ( {$stf} {$gwv6} ) inet6 from {$vip['ip']} to !{$vip['ip']}/{$pdlen} tracker {$increment_tracker($tracker)} keep state allow-opts label \"let out anything from firewall host itself\"\n"; + } + } + } + } + + + $saved_tracker += 300; + $tracker = $saved_tracker; + /* add ipsec interfaces */ + if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enable'])) { + $ipfrules .= "pass out {$log['pass']} on \$IPsec all tracker {$increment_tracker($tracker)} tracker {$increment_tracker($tracker)} keep state label \"IPsec internal host to host\"\n"; + } + + $saved_tracker += 10; + $tracker = $saved_tracker; + if (is_array($config['system']['webgui']) && !isset($config['system']['webgui']['noantilockout'])) { + $alports = filter_get_antilockout_ports(); + + if (count($config['interfaces']) > 1 && !empty($FilterIflist['lan']['if'])) { + /* if antilockout is enabled, LAN exists and has + * an IP and subnet mask assigned + */ + $lanif = $FilterIflist['lan']['if']; + $ipfrules .= << + + ScheduleMultipleTime + main descr + + + + +*/ +function filter_get_time_based_rule_status($schedule) { + + /* no schedule? rule should be installed */ + if (empty($schedule)) { + return true; + } + /* + * iterate through time blocks and determine + * if the rule should be installed or not. + */ + foreach ($schedule['timerange'] as $timeday) { + if (empty($timeday['month'])) { + $monthstatus = true; + } else { + $monthstatus = filter_tdr_month($timeday['month']); + } + if (empty($timeday['day'])) { + $daystatus = true; + } else { + $daystatus = filter_tdr_day($timeday['day']); + } + if (empty($timeday['hour'])) { + $hourstatus = true; + } else { + $hourstatus = filter_tdr_hour($timeday['hour']); + } + if (empty($timeday['position'])) { + $positionstatus = true; + } else { + $positionstatus = filter_tdr_position($timeday['position']); + } + + if ($monthstatus == true && $daystatus == true && $positionstatus == true && $hourstatus == true) { + return true; + } + } + + return false; +} + +function filter_tdr_day($schedule) { + global $g; + + if ($g['debug']) { + log_error("[TDR DEBUG] filter_tdr_day($schedule)"); + } + + /* + * Calculate day of month. + * IE: 29th of may + */ + $date = date("d"); + $defined_days = explode(",", $schedule); + foreach ($defined_days as $dd) { + if ($date == $dd) { + return true; + } + } + return false; +} +function filter_tdr_hour($schedule) { + global $g; + + /* $schedule should be a string such as 16:00-19:00 */ + $tmp = explode("-", $schedule); + $starting_time = strtotime($tmp[0]); + $ending_time = strtotime($tmp[1]); + $now = strtotime("now"); + if ($g['debug']) { + log_error("[TDR DEBUG] S: $starting_time E: $ending_time N: $now"); + } + if ($now >= $starting_time and $now < $ending_time) { + return true; + } + return false; +} + +function filter_tdr_position($schedule) { + global $g; + + /* + * Calculate position, ie: day of week. + * Sunday = 7, Monday = 1, Tuesday = 2 + * Weds = 3, Thursday = 4, Friday = 5, + * Saturday = 6 + * ... + */ + $weekday = date("w"); + if ($g['debug']) { + log_error("[TDR DEBUG] filter_tdr_position($schedule) $weekday"); + } + if ($weekday == 0) { + $weekday = 7; + } + $schedule_days = explode(",", $schedule); + foreach ($schedule_days as $day) { + if ($day == $weekday) { + return true; + } + } + return false; +} + +function filter_tdr_month($schedule) { + global $g; + + /* + * Calculate month + */ + $todays_month = date("n"); + $months = explode(",", $schedule); + if ($g['debug']) { + log_error("[TDR DEBUG] filter_tdr_month($schedule)"); + } + foreach ($months as $month) { + if ($month == $todays_month) { + return true; + } + } + return false; +} + +function filter_setup_logging_interfaces() { + global $config, $FilterIflist; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_setup_logging_interfaces() being called $mt\n"; + } + $rules = ""; + if (isset($FilterIflist['lan'])) { + $rules .= "set loginterface {$FilterIflist['lan']['if']}\n"; + } else if (isset($FilterIflist['wan'])) { + $rules .= "set loginterface {$FilterIflist['wan']['if']}\n"; + } + + return $rules; +} + +function filter_process_carp_rules($log) { + global $g, $config, $tracker; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_process_carp_rules() being called $mt\n"; + } + + $increment_tracker = 'filter_rule_tracker'; + $lines = ""; + /* return if there are no carp configured items */ + if (!empty($config['hasync']) or !empty($config['virtualip']['vip'])) { + $lines .= "block in {$log['block']} quick proto carp from (self) to any tracker {$increment_tracker($tracker)}\n"; + $lines .= "pass {$log['pass']} quick proto carp tracker {$increment_tracker($tracker)}\n"; + } + return $lines; +} + +/* Generate IPsec Filter Items */ +function filter_generate_ipsec_rules($log = array()) { + global $config, $g, $FilterIflist, $tracker; + + if (isset($config['system']['developerspew'])) { + $mt = microtime(); + echo "filter_generate_ipsec_rules() being called $mt\n"; + } + + if (isset($config['system']['disablevpnrules'])) { + return "\n# VPN Rules not added disabled in System->Advanced.\n"; + } + + $increment_tracker = 'filter_rule_tracker'; + + $ipfrules = "\n# VPN Rules\n"; + if ((isset($config['ipsec']['enable'])) && + (is_array($config['ipsec']['phase1']))) { + /* step through all phase1 entries */ + foreach ($config['ipsec']['phase1'] as $ph1ent) { + $tracker += 10; + + if (isset ($ph1ent['disabled'])) { + continue; + } + /* determine local and remote peer addresses */ + if (!isset($ph1ent['mobile'])) { + if (!function_exists('ipsec_get_phase1_dst')) { + require_once("ipsec.inc"); + } + $rgip = ipsec_get_phase1_dst($ph1ent); + if (!$rgip) { + $ipfrules .= "# ERROR! Unable to determine remote IPsec peer address for {$ph1ent['remote-gateway']}\n"; + continue; + } + } else { + $rgip = " any "; + } + /* Determine best description */ + if ($ph1ent['descr']) { + $descr = $ph1ent['descr']; + } else { + $descr = $rgip; + } + /* + * Step through all phase2 entries and determine + * which protocols are in use with this peer + */ + $prot_used_esp = false; + $prot_used_ah = false; + if (is_array($config['ipsec']['phase2'])) { + foreach ($config['ipsec']['phase2'] as $ph2ent) { + /* only evaluate ph2's bound to our ph1 */ + if ($ph2ent['ikeid'] != $ph1ent['ikeid']) { + continue; + } + if ($ph2ent['protocol'] == 'esp') { + $prot_used_esp = true; + } + if ($ph2ent['protocol'] == 'ah') { + $prot_used_ah = true; + } + } + } + + if (strpos($ph1ent['interface'], "_vip")) { + $parentinterface = get_configured_carp_interface_list($ph1ent['interface'], '', 'iface'); + } else { + $parentinterface = $ph1ent['interface']; + } + if (empty($FilterIflist[$parentinterface]['descr'])) { + $ipfrules .= "# Could not locate interface for IPsec: {$descr}\n"; + continue; + } + + unset($gateway); + /* add endpoint routes to correct gateway on interface if the + remote endpoint is not on this interface's subnet */ + if ((isset($ph1ent['mobile']) || is_ipaddrv4($rgip)) && (interface_has_gateway($parentinterface))) { + $parentifsubnet = get_interface_ip($parentinterface) . "/" . get_interface_subnet($parentinterface); + if (isset($ph1ent['mobile']) || !ip_in_subnet($rgip, $parentifsubnet)) { + $gateway = get_interface_gateway($parentinterface); + $interface = $FilterIflist[$parentinterface]['if']; + + $route_to = " route-to ( $interface $gateway ) "; + $reply_to = " reply-to ( $interface $gateway ) "; + } + } else if ((isset($ph1ent['mobile']) || is_ipaddrv6($rgip)) && (interface_has_gatewayv6($parentinterface))) { + $parentifsubnet = get_interface_ipv6($parentinterface) . "/" . get_interface_subnetv6($parentinterface); + if (isset($ph1ent['mobile']) || !ip_in_subnet($rgip, $parentifsubnet)) { + $gateway = get_interface_gateway_v6($parentinterface); + $interface = $FilterIflist[$parentinterface]['if']; + + $route_to = " route-to ( $interface $gateway ) "; + $reply_to = " reply-to ( $interface $gateway ) "; + } + } + + /* Just in case */ + if ((!is_ipaddr($gateway) || empty($interface))) { + $route_to = " "; + $reply_to = " "; + } + + /* Add rules to allow IKE to pass */ + $shorttunneldescr = substr($descr, 0, 35); + $ipfrules .= << 0) { + $errorrules = sprintf(gettext("There was an error while parsing the package filter rules for %s."), $pkg_inc) . "\n"; + log_error($errorrules); + file_put_contents("{$g['tmp_path']}/rules.packages.{$pkg}", "#{$errorrules}\n{$tmprules}\n"); + continue; + } + $rules .= $tmprules; + } + } + return $rules; +} + +function filter_get_antilockout_ports($wantarray = false) { + global $config; + + $lockoutports = array(); + $guiport = ($config['system']['webgui']['protocol'] == "https") ? "443" : "80"; + $guiport = empty($config['system']['webgui']['port']) ? $guiport : $config['system']['webgui']['port']; + $lockoutports[] = $guiport; + + if (($config['system']['webgui']['protocol'] == "https") && !isset($config['system']['webgui']['disablehttpredirect']) && ($guiport != "80")) { + $lockoutports[] = "80"; + } + + if (isset($config['system']['enablesshd'])) { + $lockoutports[] = empty($config['system']['ssh']['port']) ? "22" : $config['system']['ssh']['port']; + } + + if ($wantarray) { + return $lockoutports; + } else { + return implode(" ", $lockoutports); + } + +} + +?> -- cgit v1.1