diff --git a/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php b/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php index 079ba63c9..6c834459e 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php +++ b/src/opnsense/mvc/app/library/OPNsense/Firewall/Rule.php @@ -326,6 +326,9 @@ abstract class Rule */ protected function parseInterface($value, $prefix = "on ", $suffix = "") { + if (!empty($this->rule['interfacenot'])) { + $prefix = "{$prefix} ! "; + } if (empty($value)) { return ""; } elseif (empty($this->interfaceMapping[$value]['if'])) { diff --git a/src/www/firewall_rules.php b/src/www/firewall_rules.php index 31a27baed..413e190a6 100644 --- a/src/www/firewall_rules.php +++ b/src/www/firewall_rules.php @@ -757,12 +757,17 @@ $( document ).ready(function() { if (empty($ifgroups) && $rule->ruleOrigin() == 'group'){ // group view, skip group section (groups can't be nested) $is_selected = false; - } elseif ($rule->getInterface() == $selected_if) { + } elseif ($rule->getInterface() == $selected_if && empty($rule->getRawRule()['interfacenot'])) { // interface view and this interface is selected $is_selected = true; } elseif ($selected_if == "FloatingRules" && $rule->ruleOrigin() == 'floating') { // floating view, skip floating $is_selected = false; + } elseif (!empty($rule->getRawRule()['interfacenot'])) { + // inverted interface, all but selected + if (!in_array($selected_if, explode(',', $rule->getInterface()))) { + $is_selected = true; + } } elseif (($rule->getInterface() == "" || strpos($rule->getInterface(), ",") !== false) && $selected_if == "FloatingRules") { // floating type of rule and "floating" view $is_selected = true; @@ -832,6 +837,7 @@ $( document ).ready(function() { =$intf_count;?> + = !empty($rule->getRawRule()['interfacenot']) ? '!' : '';?> =$intf_count;?> @@ -970,6 +976,7 @@ $( document ).ready(function() { =$intf_count;?> + = !empty($filterent['interfacenot']) ? '!' : '';?> =$intf_count;?> diff --git a/src/www/firewall_rules_edit.php b/src/www/firewall_rules_edit.php index 1324f65b5..ed03e85cc 100644 --- a/src/www/firewall_rules_edit.php +++ b/src/www/firewall_rules_edit.php @@ -102,6 +102,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { 'gateway', 'icmptype', 'icmp6-type', + 'interfacenot', 'interface', 'ipprotocol', 'log', @@ -217,6 +218,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors); + if (!empty($pconfig['interfacenot']) && ( + (is_array($pconfig['interface']) && count($pconfig['interface']) != 1 ) || empty($pconfig['interface'])) + ) { + $input_errors[] = gettext("Inverting interfaces is only allowed for single targets to avoid mis-interpretations"); + } + if ($pconfig['ipprotocol'] == "inet46" && !empty($pconfig['gateway'])) { $input_errors[] = gettext("You can not assign a gateway to a rule that applies to IPv4 and IPv6"); } @@ -475,6 +482,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') { } } + $filterent['interfacenot'] = !empty($pconfig['interfacenot']); + // allow 0 in adaptive timeouts if (is_numericint($pconfig['adaptivestart']) && is_numericint($pconfig['adaptiveend'])) { $filterent['adaptivestart'] = $pconfig['adaptivestart']; @@ -851,6 +860,20 @@ include("head.inc"); + +