diff --git a/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml b/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml
index dadce138db8..3ff39eae3ac 100644
--- a/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml
+++ b/src/opnsense/mvc/app/controllers/OPNsense/Firewall/forms/dialogFilterRule.xml
@@ -659,6 +659,31 @@
false
+
+ header
+
+ true
+
+
+ rule.af-to-source
+
+ text
+ Source address used after translating the packet to the opposite address family. Address family translation is only valid for inbound pass rules using an explicit IPv4 or IPv6 version.
+ true
+
+ false
+
+
+
+ rule.af-to-destination
+
+ text
+ Optional destination address or network used after translating the packet to the opposite address family.
+ true
+
+ false
+
+
rule.statistics
diff --git a/src/opnsense/mvc/app/library/OPNsense/Firewall/FilterRule.php b/src/opnsense/mvc/app/library/OPNsense/Firewall/FilterRule.php
index 8004e3206dd..ba11d97db95 100644
--- a/src/opnsense/mvc/app/library/OPNsense/Firewall/FilterRule.php
+++ b/src/opnsense/mvc/app/library/OPNsense/Firewall/FilterRule.php
@@ -67,6 +67,7 @@ class FilterRule extends Rule
'dn' => 'parsePlain',
'divert-to' => 'parsePlain,divert-to ',
'label' => 'parsePlain,label ",",63',
+ 'af-to' => 'parsePlain, af-to ', // af-to always appears after the label in pfctl
'descr' => 'parseComment'
);
@@ -305,6 +306,14 @@ private function parseFilterRules()
$rule['dn'] = sprintf('%s %s', $shaper1['type'], $shaper1['id']);
}
}
+ // restructure address family translation
+ if (!empty($rule['af-to-source'])) {
+ $address_family = $rule['ipprotocol'] === 'inet' ? 'inet6' : 'inet';
+ $rule['af-to'] = sprintf('%s from %s', $address_family, $rule['af-to-source']);
+ if (!empty($rule['af-to-destination'])) {
+ $rule['af-to'] .= sprintf(' to %s', $rule['af-to-destination']);
+ }
+ }
yield $rule;
}
diff --git a/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php b/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php
index 5372a8de594..646e535279c 100644
--- a/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php
+++ b/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.php
@@ -285,6 +285,52 @@ public function performValidation($validateFullModel = false)
$rule->replyto->__reference
));
}
+ // af-to validation
+ if (!$rule->{'af-to-destination'}->isEmpty() && $rule->{'af-to-source'}->isEmpty()) {
+ $messages->appendMessage(
+ new Message(
+ gettext("Translation destination requires a translation source."),
+ $rule->{'af-to-source'}->__reference
+ )
+ );
+ } elseif (!$rule->{'af-to-source'}->isEmpty()) {
+ if ($rule->action != 'pass') {
+ $messages->appendMessage(new Message(
+ gettext("Address family translation is only valid for pass rules."),
+ $rule->{'af-to-source'}->__reference
+ ));
+ }
+ if ($rule->direction != 'in') {
+ $messages->appendMessage(new Message(
+ gettext("Address family translation is only valid for inbound rules."),
+ $rule->{'af-to-source'}->__reference
+ ));
+ }
+ if (!in_array((string)$rule->ipprotocol, ['inet', 'inet6'], true)) {
+ $messages->appendMessage(new Message(
+ gettext("An address family must be selected when using address family translation."),
+ $rule->ipprotocol->__reference
+ ));
+ }
+ if ($rule->statetype == 'none') {
+ $messages->appendMessage(new Message(
+ gettext("Address family translation requires state tracking."),
+ $rule->{'af-to-source'}->__reference
+ ));
+ }
+ if (!$rule->gateway->isEmpty()) {
+ $messages->appendMessage(new Message(
+ gettext("Address family translation cannot be combined with route-to."),
+ $rule->gateway->__reference
+ ));
+ }
+ if (!$rule->replyto->isEmpty()) {
+ $messages->appendMessage(new Message(
+ gettext("Address family translation cannot be combined with reply-to."),
+ $rule->replyto->__reference
+ ));
+ }
+ }
}
}
}
diff --git a/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml b/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml
index 793a66d24c1..a8a1ec4a1fd 100644
--- a/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml
+++ b/src/opnsense/mvc/app/models/OPNsense/Firewall/Filter.xml
@@ -192,6 +192,13 @@
filter list diverts
Specify a valid divert-to target.
+
+ N
+
+
+ Y
+ Y
+
interface gateways list -g
Specify a valid gateway from the list matching the networks ip protocol.