diff --git a/Cargo.lock b/Cargo.lock
index 558b9cf5..7c027c23 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2314,8 +2314,9 @@ dependencies = [
[[package]]
name = "nftnl"
-version = "0.9.2"
-source = "git+https://github.com/DefGuard/nftnl-rs.git?rev=abd34b08c53b88a47346da6362e360d522cf8ca2#abd34b08c53b88a47346da6362e360d522cf8ca2"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f44c7df2060dc8fc8388ca2717f1a2a7eb2193d1f6dc357671a93f0cc97fc67"
dependencies = [
"bitflags 2.13.1",
"log",
@@ -2326,7 +2327,8 @@ dependencies = [
[[package]]
name = "nftnl-sys"
version = "0.6.4"
-source = "git+https://github.com/DefGuard/nftnl-rs.git?rev=abd34b08c53b88a47346da6362e360d522cf8ca2#abd34b08c53b88a47346da6362e360d522cf8ca2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cffa28b4c81eea6a8a4fd454634d425e1a559250a50a926de4517dd9607fe9a4"
dependencies = [
"cfg-if",
"libc",
diff --git a/Cargo.toml b/Cargo.toml
index 476954b2..43e69d16 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -44,7 +44,7 @@ tracing = "0.1"
tracing-subscriber = "0.3"
[target.'cfg(target_os = "linux")'.dependencies]
-nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "abd34b08c53b88a47346da6362e360d522cf8ca2" }
+nftnl = "0.9"
mnl = "0.3"
[dev-dependencies]
diff --git a/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc b/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc
index 5584a3b4..3696a19f 100644
--- a/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc
+++ b/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc
@@ -81,5 +81,12 @@ function defguardgateway_firewall($fw)
// $fw->registerAnchor('defguard/*', 'nat', 1, 'head');
// $fw->registerAnchor('defguard/*', 'rdr', 1, 'head');
- $fw->registerAnchor('defguard/*', 'fw', 1, 'head', true);
+ // IMPORTANT: never register this anchor as 'quick'. On FreeBSD 15+
+ // (OPNsense 26.7+), a 'quick' anchor short-circuits the parent ruleset
+ // even when no inner rule matches, silently skipping OPNsense's own
+ // firewall rules.
+ $model = new OPNsense\DefguardGateway\DefguardGateway();
+ $placement = (string) $model->general->FwAnchorPlacement ?: 'tail';
+ $quick = (string) $model->general->FwAnchorQuick === '1';
+ $fw->registerAnchor('defguard/*', 'fw', 1, $placement, $quick);
}
diff --git a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php
index 2c340eb0..553ba3a5 100644
--- a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php
+++ b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php
@@ -14,4 +14,13 @@ class ServiceController extends ApiMutableServiceControllerBase
protected static $internalServiceClass = "\OPNsense\DefguardGateway\DefguardGateway";
protected static $internalServiceTemplate = "OPNsense/DefguardGateway";
protected static $internalServiceEnabled = "general.Enabled";
+
+ /**
+ * The 'defguard/*' anchor is hooked into the ruleset by defguardgateway_firewall().
+ * Changes only take effect once the filter is reloaded.
+ */
+ protected function invokeFirewallReload()
+ {
+ return true;
+ }
}
diff --git a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml
index 3b9b81e2..29709dad 100644
--- a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml
+++ b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml
@@ -134,4 +134,18 @@
Directory where the gateway stores generated gRPC certificates.
Default value: /etc/defguard/certs
+
+ defguardgateway.general.FwAnchorPlacement
+
+ dropdown
+ Where in the pf ruleset the Defguard anchor is inserted. Tail (default) evaluates OPNsense rules first; Head places Defguard rules before OPNsense rules.
+ true
+
+
+ defguardgateway.general.FwAnchorQuick
+
+ checkbox
+ Make the Defguard anchor 'quick'. WARNING: on OPNsense 26.7+ (FreeBSD 15.1+) this short-circuits the parent ruleset even when no inner rule matches, silently bypassing OPNsense's own firewall rules. Leave disabled unless you fully understand the risk.
+ true
+
diff --git a/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml b/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml
index 5c0a8ee8..d7e0562c 100644
--- a/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml
+++ b/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml
@@ -90,6 +90,18 @@
/etc/defguard/certs
Y
+
+ tail
+ Y
+
+ Tail (evaluate after OPNsense rules)
+ Head (evaluate before OPNsense rules)
+
+
+
+ 0
+ Y
+
diff --git a/src/enterprise/firewall/nftables/netfilter.rs b/src/enterprise/firewall/nftables/netfilter.rs
index ba7d3e24..a551aefb 100644
--- a/src/enterprise/firewall/nftables/netfilter.rs
+++ b/src/enterprise/firewall/nftables/netfilter.rs
@@ -160,7 +160,8 @@ fn add_rule_comment(rule: &mut Rule, comment: &str) -> Result<(), FirewallError>
"Failed to create CString from string {comment}. Error: {e:?}"
))
})?;
- rule.set_comment(comment);
+ rule.set_comment(comment)
+ .map_err(|err| FirewallError::NetlinkError(err.into()))?;
debug!("Added comment to nftables expression: {comment:?}");
Ok(())
}
diff --git a/src/enterprise/firewall/packetfilter/api.rs b/src/enterprise/firewall/packetfilter/api.rs
index 96f7b78e..af541bfe 100644
--- a/src/enterprise/firewall/packetfilter/api.rs
+++ b/src/enterprise/firewall/packetfilter/api.rs
@@ -55,7 +55,7 @@ impl FirewallManagementApi for FirewallApi {
for rule in rules {
if let Err(err) = self.add_rule(rule, ticket, pool_ticket, anchor) {
- error!("Firewall rule {} can't be added", &rule.id);
+ error!("Firewall rule {} can't be added", rule.id);
debug!("Rollback pf transaction");
// Rule cannot be added, so rollback.
let result = unsafe { ioctl(self.fd(), DIOCXROLLBACK, &raw mut ioc_trans) };