Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
903 lines (814 sloc)
31.6 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Copyright (C) 2017 Deciso B.V. | |
* Copyright (C) 2004-2007 Scott Ullrich <sullrich@gmail.com> | |
* Copyright (C) 2005 Bill Marquette <bill.marquette@gmail.com> | |
* Copyright (C) 2006 Peter Allgeyer <allgeyer@web.de> | |
* Copyright (C) 2008-2010 Ermal Luçi | |
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net> | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the following disclaimer. | |
* | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | |
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | |
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
require_once('filter.lib.inc'); | |
function is_bogonsv6_used() | |
{ | |
global $config; | |
/* | |
* Only use bogonsv6 table if IPv6 Allow is on, and at least | |
* one enabled interface also has "blockbogons" enabled. | |
*/ | |
$usebogonsv6 = false; | |
if (isset($config['system']['ipv6allow']) && isset($config['interfaces'])) { | |
foreach ($config['interfaces'] as $ifacedata) { | |
if (isset($ifacedata['enable']) && isset($ifacedata['blockbogons'])) { | |
$usebogonsv6 = true; | |
break; | |
} | |
} | |
} | |
return $usebogonsv6; | |
} | |
/* sort by interface only, retain the original order of rules that apply to | |
the same interface */ | |
function filter_rules_sort() | |
{ | |
global $config; | |
/* mark each rule with the sequence number (to retain the order while sorting) */ | |
for ($i = 0; isset($config['filter']['rule'][$i]); $i++) { | |
$config['filter']['rule'][$i]['seq'] = $i; | |
} | |
usort($config['filter']['rule'], function ($a, $b) { | |
if (isset($a['floating']) && isset($b['floating'])) { | |
return $a['seq'] - $b['seq']; | |
} elseif (isset($a['floating'])) { | |
return -1; | |
} elseif (isset($b['floating'])) { | |
return 1; | |
} elseif ($a['interface'] == $b['interface']) { | |
return $a['seq'] - $b['seq']; | |
} elseif ($a['interface'] == 'wan') { | |
return -1; | |
} elseif ($b['interface'] == 'wan') { | |
return 1; | |
} elseif ($a['interface'] == 'lan') { | |
return -1; | |
} elseif ($b['interface'] == 'lan') { | |
return 1; | |
} else { | |
return strnatcmp($a['interface'], $b['interface']); | |
} | |
}); | |
/* strip the sequence numbers again */ | |
for ($i = 0; isset($config['filter']['rule'][$i]); $i++) { | |
unset($config['filter']['rule'][$i]['seq']); | |
} | |
} | |
function filter_configure() | |
{ | |
/* | |
* Defer this to configd which will avoid this call on bootup when | |
* this should not be triggered. The reason is that rc.bootup calls | |
* filter_configure_sync() directly which does this too. | |
*/ | |
configd_run('filter reload'); | |
} | |
/** | |
* sync interface groups, but leave the ones not managed by us intact. | |
*/ | |
function ifgroup_setup() | |
{ | |
global $config; | |
$all_ifgroups = array(); | |
$all_ifs = array(); | |
$interface_details = legacy_interfaces_details(); | |
if (isset($config['ifgroups']['ifgroupentry'])) { | |
foreach ($config['ifgroups']['ifgroupentry'] as $group) { | |
$all_ifgroups[$group['ifname']] = array(); | |
foreach (explode(" ", $group['members']) as $member) { | |
if (!empty($config['interfaces'][$member])) { | |
$if = $config['interfaces'][$member]['if']; | |
if (!isset($all_ifs[$if])) { | |
$all_ifs[$if] = array(); | |
} | |
$all_ifs[$if][] = $group['ifname']; | |
$all_ifgroups[$group['ifname']][] = $if; | |
} | |
} | |
} | |
} | |
foreach ($interface_details as $intf => $details) { | |
$thisifgroups = !empty($details['groups']) ? $details['groups'] : array(); | |
foreach ($thisifgroups as $ifgroup) { | |
if (isset($all_ifgroups[$ifgroup]) && !in_array($intf, $all_ifgroups[$ifgroup])) { | |
// detach | |
mwexecf('/sbin/ifconfig %s -group %s', array($intf, $ifgroup)); | |
} | |
} | |
if (!empty($all_ifs[$intf])) { | |
foreach ($all_ifs[$intf] as $ifgroup) { | |
if (!in_array($ifgroup, $thisifgroups)) { | |
// attach | |
mwexecf('/sbin/ifconfig %s group %s', array($intf, $ifgroup)); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* XXX: replace with check on interfaces section (see pf_interfaces) | |
*/ | |
function is_interface_group($if) | |
{ | |
global $config; | |
if (isset($config['ifgroups']['ifgroupentry'])) { | |
foreach ($config['ifgroups']['ifgroupentry'] as $groupentry) { | |
if ($groupentry['ifname'] === $if) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
function filter_configure_sync($verbose = false, $load_aliases = true) | |
{ | |
global $config; | |
$sched_kill_states = array(); // kill states for schedules | |
if ($verbose) { | |
echo 'Configuring firewall.'; | |
flush(); | |
} | |
/* Use filter lock to not allow concurrent filter reloads during this run. */ | |
$filterlck = lock('filter', LOCK_EX); | |
ifgroup_setup(); | |
if ($verbose) { | |
echo '.'; | |
flush(); | |
} | |
// initialize fw plugin object | |
$fw = filter_core_get_initialized_plugin_system(); | |
filter_core_bootstrap($fw); | |
$cnfint = iterator_to_array($fw->getInterfaceMapping()); | |
plugins_firewall($fw); | |
if (isset($config['filter']['rule'])) { | |
// register user rules | |
foreach ($config['filter']['rule'] as $rule) { | |
// calculate a hash for this area so we can track this rule, we should replace this | |
// with uuid's on the rules like the new style models do eventually. | |
$rule_hash = OPNsense\Firewall\Util::calcRuleHash($rule); | |
$rule['label'] = $rule_hash; | |
$sched = ''; | |
$descr = ''; | |
if (!empty($rule['sched'])) { | |
$sched = "({$rule['sched']})"; | |
} | |
if (!empty($rule['descr'])) { | |
$descr = ": {$rule['descr']}"; | |
} | |
$rule['descr'] = "{$sched}{$descr}"; | |
if (isset($rule['floating'])) { | |
$prio = 200000; | |
} elseif (is_interface_group($rule['interface']) || in_array($rule['interface'], array("l2tp", "pptp", "pppoe", "enc0", "openvpn"))) { | |
$prio = 300000; | |
} else { | |
$prio = 400000; | |
} | |
/* is a time based rule schedule attached? */ | |
if (!empty($rule['sched']) && !empty($config['schedules'])) { | |
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'])) { | |
$sched_kill_states[] = $rule['label']; | |
} | |
/* disable rule, suffix label to mark end of schedule */ | |
$rule['disabled'] = true; | |
$rule['descr'] = "[FIN]" . $rule['descr']; | |
} | |
break; | |
} | |
} | |
} | |
$fw->registerFilterRule($prio, $rule); | |
} | |
} | |
// manual outbound nat rules | |
if ( | |
!empty($config['nat']['outbound']['mode']) && | |
in_array($config['nat']['outbound']['mode'], array("advanced", "hybrid")) | |
) { | |
if (!empty($config['nat']['outbound']['rule'])) { | |
foreach ($config['nat']['outbound']['rule'] as $rule) { | |
$fw->registerSNatRule(100, $rule); | |
} | |
} | |
} | |
if ( | |
empty($config['nat']['outbound']['mode']) || | |
in_array($config['nat']['outbound']['mode'], array("automatic", "hybrid")) | |
) { | |
// generate standard outbound rules when mode is automatic ot hybrid | |
$intfv4 = array(); | |
foreach ($fw->getInterfaceMapping() as $intf => $intfcf) { | |
if (!empty($intfcf['ifconfig']['ipv4']) && empty($intfcf['gateway'])) { | |
$intfv4[] = $intf; | |
} | |
} | |
// add VPN and local networks | |
$intfv4 = array_merge($intfv4, filter_core_get_default_nat_outbound_networks()); | |
foreach ($fw->getInterfaceMapping() as $intf => $ifcfg) { | |
if (substr($ifcfg['if'], 0, 4) != 'ovpn' && !empty($ifcfg['gateway'])) { | |
foreach (array(500, null) as $dstport) { | |
$rule = array( | |
'descr' => 'Automatic outbound rule', | |
'destination' => array('any' => true), | |
'dstport' => $dstport, | |
'interface' => $intf, | |
'ipprotocol' => 'inet', | |
'log' => !empty($config['syslog']['logoutboundnat']), | |
'staticnatport' => !empty($dstport), | |
); | |
foreach ($intfv4 as $network) { | |
$rule['source'] = array("network" => $network); | |
$fw->registerSNatRule(200, $rule); | |
} | |
} | |
} | |
} | |
} | |
// prevent redirection on ports with "lock out" protection | |
foreach (filter_core_get_antilockout() as $lockoutif => $lockoutprts) { | |
foreach ($lockoutprts as $port) { | |
$rule = array( | |
'interface' => $lockoutif, | |
"nordr" => true, | |
"protocol" => "tcp", | |
'destination' => array('network' => "{$lockoutif}ip", 'port' => $port), | |
"descr" => "Anti lockout, prevent redirects for protected ports to this interface ip" | |
); | |
$fw->registerForwardRule(300, $rule); | |
} | |
} | |
if (!empty($config['nat']['npt'])) { | |
// register user npt rules | |
foreach ($config['nat']['npt'] as $rule) { | |
$fw->registerNptRule(400, $rule); | |
} | |
} | |
if (!empty($config['nat']['onetoone'])) { | |
// register user 1:1 mappings | |
foreach ($config['nat']['onetoone'] as $rule) { | |
$fw->registerDNatRule(500, $rule); | |
} | |
} | |
if (!empty($config['nat']['rule'])) { | |
// register user forward rules | |
foreach ($config['nat']['rule'] as $rule) { | |
$fw->registerForwardRule(600, $rule); | |
} | |
} | |
if (isset($config['system']['gw_switch_default'])) { | |
// When gateway switching is enabled, we might consider a different default gateway. | |
// although this isn't really the right spot for the feature (it's a monitoring/routing decision), | |
// we keep it here for now (historical reasons). | |
$down_gateways = return_down_gateways(); | |
foreach (array("inet", "inet6") as $ipprotocol) { | |
if (!empty($down_gateways)) { | |
log_error(sprintf("Ignore down %s gateways : %s", $ipprotocol, implode(",", $down_gateways))); | |
} | |
$default_gw = $fw->getGateways()->getDefaultGW($down_gateways, $ipprotocol); | |
if ($default_gw !== null) { | |
system_default_route( | |
$default_gw['gateway'], | |
$ipprotocol, | |
$default_gw['if'], | |
isset($default_gw['fargw']) | |
); | |
} | |
} | |
} | |
$aliases = filter_generate_aliases(); | |
$aliases .= "\n# Plugins tables\n"; | |
$aliases .= $fw->tablesToText(); | |
if ($verbose) { | |
echo '.'; | |
flush(); | |
} | |
$natrules = "\n# NAT Redirects\n"; | |
$natrules .= "no nat proto carp all\n"; | |
$natrules .= "no rdr proto carp all\n"; | |
$natrules .= $fw->outputNatRules(); | |
if ($verbose) { | |
echo '.'; | |
flush(); | |
} | |
/* enable pf if we need to, otherwise disable */ | |
if (!isset($config['system']['disablefilter'])) { | |
mwexec("/sbin/pfctl -e", true); | |
} else { | |
mwexec("/sbin/pfctl -d", true); | |
if ($verbose) { | |
echo "done.\n"; | |
} | |
unlock($filterlck); | |
return; | |
} | |
if ($verbose) { | |
echo '.'; | |
flush(); | |
} | |
$limitrules = ''; | |
if (!empty($config['system']['maximumtableentries'])) { | |
$limitrules .= "set limit table-entries {$config['system']['maximumtableentries']}\n"; | |
set_single_sysctl('net.pf.request_maxcount', $config['system']['maximumtableentries']); | |
} else { | |
$max_table_entries = default_table_entries_size(); | |
$req_table_entries = 1000000; | |
if ($max_table_entries <= $req_table_entries) { | |
$limitrules .= "set limit table-entries {$req_table_entries}\n"; | |
set_single_sysctl('net.pf.request_maxcount', $req_table_entries); | |
} else { | |
set_single_sysctl('net.pf.request_maxcount', $max_table_entries); | |
} | |
} | |
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"; | |
} | |
if (!empty($config['system']['adaptivestart']) && !empty($config['system']['adaptiveend'])) { | |
$limitrules .= "set timeout { adaptive.start {$config['system']['adaptivestart']}, adaptive.end {$config['system']['adaptiveend']} }\n"; | |
} else { | |
$limitrules .= "set timeout { adaptive.start 0, adaptive.end 0 }\n"; | |
} | |
if (!empty($config['system']['state-policy'])) { | |
$limitrules .= "set state-policy if-bound\n"; | |
} | |
if (!empty($config['system']['maximumstates'])) { | |
$limitrules .= "set limit states {$config['system']['maximumstates']}\n"; | |
$limitrules .= "set limit src-nodes {$config['system']['maximumstates']}\n"; | |
} else { | |
$max_states = default_state_size(); | |
$limitrules .= "set limit states {$max_states}\n"; | |
$limitrules .= "set limit src-nodes {$max_states}\n"; | |
} | |
if (!empty($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"; | |
} | |
if (!empty($config['system']['syncookies'])) { | |
$arange = ""; | |
if ($config['system']['syncookies'] == "adaptive") { | |
$arange = "(start {$config['system']['syncookies_adaptstart']}%, end {$config['system']['syncookies_adaptend']}%)"; | |
} | |
$limitrules .= "set syncookies {$config['system']['syncookies']} {$arange}\n"; | |
} | |
$rules = "{$limitrules}\n"; | |
$rules .= "{$aliases} \n"; | |
$rules .= filter_setup_logging_interfaces($cnfint); | |
$rules .= "\n"; | |
$rules .= "set skip on pfsync0\n"; | |
$rules .= "\n"; | |
$rules .= filter_generate_scrubing($cnfint); | |
$rules .= "\n"; | |
$rules .= $fw->anchorToText('nat,binat,rdr', 'head'); | |
$rules .= "{$natrules}\n"; | |
$rules .= $fw->anchorToText('nat,binat,rdr', 'tail'); | |
$rules .= $fw->anchorToText('fw', 'head'); | |
$rules .= filter_rules_legacy($cnfint); | |
$rules .= $fw->outputFilterRules(); | |
$rules .= $fw->anchorToText('fw', 'tail'); | |
// Copy rules.debug to rules.debug.old | |
if (file_exists('/tmp/rules.debug')) { | |
@copy('/tmp/rules.debug', '/tmp/rules.debug.old'); | |
} | |
if (!@file_put_contents('/tmp/rules.debug', $rules, LOCK_EX)) { | |
log_error("WARNING: Could not write new rules!"); | |
unlock($filterlck); | |
if ($verbose) { | |
echo "failed.\n"; | |
} | |
return; | |
} | |
@file_put_contents('/tmp/rules.limits', $limitrules); | |
mwexec('/sbin/pfctl -Of /tmp/rules.limits'); | |
exec('/sbin/pfctl -f /tmp/rules.debug 2>&1', $rules_error, $rules_loading); | |
foreach ($sched_kill_states as $label) { | |
mwexecf('/sbin/pfctl -k label -k %s', $label); | |
} | |
/* | |
* 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) { | |
$config_line = ''; | |
/* only report issues with line numbers */ | |
$line_error = explode(':', $rules_error[0]); | |
if (isset($line_error[1]) && (string)((int)$line_error[1]) == $line_error[1] && $line_error[1] > 0) { | |
$line_number = $line_error[1]; | |
$line_split = file('/tmp/rules.debug'); | |
if (is_array($line_split)) { | |
$config_line = sprintf(' - ' . gettext('The line in question reads [%s]: %s'), $line_number, $line_split[$line_number - 1]); | |
} | |
} | |
/* 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 /tmp/rules.debug'); | |
log_error('PF was wedged/busy and has been reset.'); | |
file_notice(gettext('PF was wedged/busy and has been reset.')); | |
} else { | |
exec('/sbin/pfctl -f /tmp/rules.debug.old 2>&1'); | |
} | |
log_error(sprintf('There were error(s) loading the rules: %s%s', $rules_error[0], $config_line)); | |
file_notice(sprintf(gettext('There were error(s) loading the rules: %s%s'), $rules_error[0], $config_line)); | |
unlock($filterlck); | |
if ($verbose) { | |
echo "failed.\n"; | |
} | |
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()) { | |
mwexec('/sbin/pfctl -t bogonsv6 -T kill'); | |
} | |
if ($verbose) { | |
echo '.'; | |
flush(); | |
} | |
if ($load_aliases) { | |
configd_run('template reload OPNsense/Filter'); | |
configd_run('filter refresh_aliases', true); | |
} | |
if ($verbose) { | |
echo '.'; | |
flush(); | |
} | |
/* enable permanent promiscuous mode to avoid dmesg noise */ | |
mwexec('/sbin/ifconfig pflog0 promisc'); | |
/* bring up new instance of filterlog to load new rules */ | |
killbypid('/var/run/filterlog.pid', 'TERM', true); | |
mwexec('/usr/local/sbin/filterlog -i pflog0 -p /var/run/filterlog.pid'); | |
if ($verbose) { | |
echo "done.\n"; | |
} | |
unlock($filterlck); | |
} | |
function filter_generate_scrubing(&$FilterIflist) | |
{ | |
global $config; | |
$scrubrules = ''; | |
/* custom rules must be first */ | |
if (!empty($config['filter']['scrub']['rule'])) { | |
foreach ($config['filter']['scrub']['rule'] as $scrub_rule) { | |
if (!isset($scrub_rule['disabled'])) { | |
$scrub_rule_out = !empty($scrub_rule['noscrub']) ? "no " : ""; | |
$scrub_rule_out .= "scrub"; | |
$scrub_rule_out .= !empty($scrub_rule['direction']) ? " " . $scrub_rule['direction'] : ""; | |
$scrub_rule_out .= " on "; | |
$interfaces = array(); | |
foreach (explode(',', $scrub_rule['interface']) as $interface) { | |
if (!empty($FilterIflist[$interface]['if'])) { | |
$interfaces[] = $FilterIflist[$interface]['if']; | |
} | |
} | |
$scrub_rule_out .= count($interfaces) > 1 ? "{ " . implode(' ', $interfaces) . " } " : $interfaces[0]; | |
switch ($scrub_rule['proto']) { | |
case 'any': | |
break; | |
case 'tcp/udp': | |
$scrub_rule_out .= " proto {tcp udp}"; | |
break; | |
default: | |
$scrub_rule_out .= " proto " . $scrub_rule['proto']; | |
break; | |
} | |
$scrub_rule_out .= " from "; | |
if (is_alias($scrub_rule['src'])) { | |
$scrub_rule_out .= !empty($scrub_rule['srcnot']) ? "!" : ""; | |
$scrub_rule_out .= '$' . $scrub_rule['src']; | |
} elseif (is_ipaddr($scrub_rule['src'])) { | |
$scrub_rule_out .= !empty($scrub_rule['srcnot']) ? "!" : ""; | |
$scrub_rule_out .= $scrub_rule['src'] . "/" . $scrub_rule['srcmask']; | |
} else { | |
$scrub_rule_out .= "any"; | |
} | |
if (!empty($scrub_rule['srcport']) && is_alias($scrub_rule['srcport'])) { | |
$scrub_rule_out .= " port $" . $scrub_rule['srcport']; | |
} else { | |
$scrub_rule_out .= !empty($scrub_rule['srcport']) ? " port " . $scrub_rule['srcport'] : ""; | |
} | |
$scrub_rule_out .= " to "; | |
if (is_alias($scrub_rule['dst'])) { | |
$scrub_rule_out .= !empty($scrub_rule['dstnot']) ? "!" : ""; | |
$scrub_rule_out .= '$' . $scrub_rule['dst']; | |
} elseif (is_ipaddr($scrub_rule['dst'])) { | |
$scrub_rule_out .= !empty($scrub_rule['dstnot']) ? "!" : ""; | |
$scrub_rule_out .= $scrub_rule['dst'] . "/" . $scrub_rule['dstmask']; | |
} else { | |
$scrub_rule_out .= "any"; | |
} | |
if (!empty($scrub_rule['dstport']) && is_alias($scrub_rule['dstport'])) { | |
$scrub_rule_out .= " port $" . $scrub_rule['dstport']; | |
} else { | |
$scrub_rule_out .= !empty($scrub_rule['dstport']) ? " port " . $scrub_rule['dstport'] : ""; | |
} | |
if (empty($scrub_rule['noscrub'])) { | |
$scrub_rule_out .= !empty($scrub_rule['no-df']) ? " no-df " : ""; | |
$scrub_rule_out .= !empty($scrub_rule['random-id']) ? " random-id " : ""; | |
$scrub_rule_out .= !empty($scrub_rule['max-mss']) ? " max-mss " . $scrub_rule['max-mss'] . " " : ""; | |
$scrub_rule_out .= !empty($scrub_rule['min-ttl']) ? " min-ttl " . $scrub_rule['min-ttl'] . " " : ""; | |
$scrub_rule_out .= !empty($scrub_rule['set-tos']) ? " set-tos " . $scrub_rule['set-tos'] . " " : ""; | |
} | |
$scrub_rule_out .= "\n"; | |
if (count($interfaces) == 0) { | |
# unknown interface, skip rule | |
$scrubrules .= "#"; | |
} | |
$scrubrules .= $scrub_rule_out; | |
} | |
} | |
} | |
/* scrub per interface options */ | |
if (empty($config['system']['scrub_interface_disable'])) { | |
foreach ($FilterIflist as $scrubcfg) { | |
if (isset($scrubcfg['virtual']) || empty($scrubcfg['descr'])) { | |
continue; | |
} | |
$mssclampv4 = ''; | |
$mssclampv6 = ''; | |
if ( | |
!empty($scrubcfg['mss']) && is_numeric($scrubcfg['mss']) && | |
!in_array($scrubcfg['if'], array('pppoe', 'pptp', 'l2tp')) | |
) { | |
$mssclampv4 = 'max-mss ' . (intval($scrubcfg['mss'] - 40)); | |
$mssclampv6 = 'max-mss ' . (intval($scrubcfg['mss'] - 60)); | |
} | |
$scrubnodf = !empty($config['system']['scrubnodf']) ? 'no-df' : ''; | |
$scrubrnid = !empty($config['system']['scrubrnid']) ? 'random-id' : ''; | |
if (!empty($mssclampv4)) { | |
$scrubrules .= "scrub on {$scrubcfg['if']} inet all {$scrubnodf} {$scrubrnid} {$mssclampv4}\n"; | |
$scrubrules .= "scrub on {$scrubcfg['if']} inet6 all {$scrubnodf} {$scrubrnid} {$mssclampv6}\n"; | |
} else { | |
$scrubrules .= "scrub on {$scrubcfg['if']} all {$scrubnodf} {$scrubrnid}\n"; | |
} | |
} | |
} | |
return $scrubrules; | |
} | |
function filter_generate_aliases() | |
{ | |
$aliases = "\n# Lockout tables\n"; | |
$aliases .= "table <sshlockout> persist\n"; | |
$aliases .= "\n# Other tables\n"; | |
$aliases .= "table <virusprot>\n"; | |
$aliases .= "\n# User Aliases\n"; | |
$aliasObject = new \OPNsense\Firewall\Alias(); | |
// list of registered aliases for faster is_alias() lookup | |
$all_aliases = []; | |
foreach ($aliasObject->aliasIterator() as $aliased) { | |
$all_aliases[] = $aliased['name']; | |
} | |
foreach ($aliasObject->aliasIterator() as $aliased) { | |
switch ($aliased['type']) { | |
case "urltable_ports": | |
case "url_ports": | |
# a bit of a hack, but prevents the ruleset from not being able to load if these types are in | |
# the configuration. | |
$aliases .= "{$aliased['name']} = \"{ 0 <> 65535 }\"\n"; | |
log_error(sprintf('URL port aliases types not supported [%s]', $aliased['name'])); | |
file_notice(sprintf(gettext('URL port aliases types not supported [%s]'), $aliased['name'])); | |
break; | |
case "port": | |
$tmp_ports = implode(" ", filter_core_get_port_alias($aliased['name'], [], $aliasObject, $all_aliases)); | |
if (empty($tmp_ports)) { | |
// we can't create empty port tables, so when it's empty we should make sure it can't match | |
$tmp_ports = "0 <> 65535"; | |
} | |
$aliases .= "{$aliased['name']} = \"{ {$tmp_ports} }\"\n"; | |
break; | |
default: | |
$tblopt = (!empty($aliased['counters']) ? 'counters' : '') . " persist "; | |
$aliases .= "table <{$aliased['name']}> {$tblopt} \n"; | |
$aliases .= "{$aliased['name']} = \"<{$aliased['name']}>\"\n"; | |
break; | |
} | |
} | |
$aliases .= "table <bogons> persist file \"/usr/local/etc/bogons\"\n"; | |
if (is_bogonsv6_used()) { | |
$aliases .= "table <bogonsv6> persist file \"/usr/local/etc/bogonsv6\"\n"; | |
} | |
return $aliases; | |
} | |
function filter_rules_legacy(&$FilterIflist) | |
{ | |
global $config; | |
$log = !isset($config['syslog']['nologdefaultblock']) ? "log" : ""; | |
$ipfrules = ""; | |
$bridge_interfaces = []; | |
if (!empty($config['bridges']['bridged'])) { | |
foreach ($config['bridges']['bridged'] as $oc2) { | |
$bridge_interfaces = array_merge(explode(',', $oc2['members']), $bridge_interfaces); | |
} | |
} | |
foreach ($FilterIflist as $on => $oc) { | |
if (!in_array($on, $bridge_interfaces) && !isset($oc['internal_dynamic']) && $oc['if'] != 'lo0') { | |
$ipfrules .= "antispoof {$log} for {$oc['if']} \n"; | |
} | |
} | |
return $ipfrules; | |
} | |
/****f* filter/filter_get_time_based_rule_status | |
* NAME | |
* filter_get_time_based_rule_status | |
* INPUTS | |
* xml schedule block | |
* RESULT | |
* true/false - true if the rule should be installed | |
******/ | |
/* | |
<schedules> | |
<schedule> | |
<name>ScheduleMultipleTime</name> | |
<descr>main descr</descr> | |
<time> | |
<position>0,1,2</position> | |
<hour>0:0-24:0</hour> | |
<desc>time range 2</desc> | |
</time> | |
<time> | |
<position>4,5,6</position> | |
<hour>0:0-24:0</hour> | |
<desc>time range 1</desc> | |
</time> | |
</schedule> | |
</schedules> | |
*/ | |
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) | |
{ | |
/* | |
* 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) | |
{ | |
/* $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 ($now >= $starting_time && $now < $ending_time) { | |
return true; | |
} | |
return false; | |
} | |
function filter_tdr_position($schedule) | |
{ | |
/* | |
* Calculate position, ie: day of week. | |
* Sunday = 7, Monday = 1, Tuesday = 2 | |
* Weds = 3, Thursday = 4, Friday = 5, | |
* Saturday = 6 | |
* ... | |
*/ | |
$weekday = date("w"); | |
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) | |
{ | |
/* | |
* Calculate month | |
*/ | |
$todays_month = date("n"); | |
$months = explode(",", $schedule); | |
foreach ($months as $month) { | |
if ($month == $todays_month) { | |
return true; | |
} | |
} | |
return false; | |
} | |
function filter_setup_logging_interfaces(&$FilterIflist) | |
{ | |
$rules = ''; | |
if (isset($FilterIflist['lan'])) { | |
$rules .= "set loginterface {$FilterIflist['lan']['if']}\n"; | |
} elseif (isset($FilterIflist['wan'])) { | |
$rules .= "set loginterface {$FilterIflist['wan']['if']}\n"; | |
} | |
return $rules; | |
} | |
function default_table_entries_size() | |
{ | |
$current = `pfctl -sm | grep table-entries | awk '{print $4};'`; | |
return $current; | |
} | |
function default_state_size() | |
{ | |
/* get system memory amount */ | |
$memory = get_memory(); | |
$physmem = $memory[0]; | |
/* Be cautious and only allocate 10% of system memory to the state table */ | |
$max_states = (int) ($physmem / 10) * 1000; | |
return $max_states; | |
} | |
function get_protocols() | |
{ | |
$protocols = array('any', 'TCP', 'UDP', 'TCP/UDP', 'ICMP', 'ESP', 'AH', 'GRE', 'IGMP', 'PIM', 'OSPF'); | |
/* IPv6 extension headers are skipped by the packet filter, we cannot police them */ | |
$ipv6_ext = array('IPV6-ROUTE', 'IPV6-FRAG', 'IPV6-OPTS', 'IPV6-NONXT', 'MOBILITY-HEADER'); | |
foreach (explode("\n", file_get_contents('/etc/protocols')) as $line) { | |
if (substr($line, 0, 1) != "#") { | |
$parts = preg_split('/\s+/', $line); | |
if (count($parts) >= 4 && $parts[1] > 0) { | |
$protocol = trim(strtoupper($parts[0])); | |
if (!in_array($protocol, $ipv6_ext) && !in_array($protocol, $protocols)) { | |
$protocols[] = $protocol; | |
} | |
} | |
} | |
} | |
return $protocols; | |
} |