From e271497d97acf54bd35fd7f859859b2dac97418e Mon Sep 17 00:00:00 2001 From: r-vdp Date: Thu, 12 Feb 2026 23:52:54 +0200 Subject: [PATCH] dns-delegates: add support for setting a firewall mark This makes it possible to have DNS requests for certain domains routed differently than normal requests, which is for instance useful when using policy routing to route traffic over a VPN but DNS requests for the VPN endpoint itself, should be routed differently. It doesn't make much sense to configure a firewall mark at the level of a network interface, but at the level of a DNS delegate it can be very useful. --- man/systemd.dns-delegate.xml | 17 +++++++++++++++-- src/resolve/resolved-dns-delegate-gperf.gperf | 1 + src/resolve/resolved-dns-delegate.h | 2 ++ src/resolve/resolved-dns-scope.c | 6 ++++++ test/units/TEST-75-RESOLVED.sh | 1 + 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/man/systemd.dns-delegate.xml b/man/systemd.dns-delegate.xml index a06ccad529..0ef36d2bf5 100644 --- a/man/systemd.dns-delegate.xml +++ b/man/systemd.dns-delegate.xml @@ -69,6 +69,18 @@ + + FirewallMark= + + Takes a 32 bit unsigned integer value. Controls the firewall mark of packets generated by the + socket used to make DNS requests for this DNS delegate. This can be used in the firewall logic to + filter packets from this socket. + This sets the SO_MARK socket option. See iptables8 for + details. + + + @@ -79,10 +91,11 @@ # /etc/systemd/dns-delegate.d/foobar.dns-delegate [Delegate] DNS=203.0.113.47 -Domains=foobar.com +Domains=foobar.com +FirewallMark=42 This ensures lookups of foobar.com and any domains below it are directed to - DNS server 203.0.113.47. + DNS server 203.0.113.47 and any packets related to this lookup have a firewall mark set to 42. diff --git a/src/resolve/resolved-dns-delegate-gperf.gperf b/src/resolve/resolved-dns-delegate-gperf.gperf index 3040770f3a..27dfe43366 100644 --- a/src/resolve/resolved-dns-delegate-gperf.gperf +++ b/src/resolve/resolved-dns-delegate-gperf.gperf @@ -21,3 +21,4 @@ struct ConfigPerfItem; Delegate.DNS, config_parse_delegate_dns_servers, 0, 0 Delegate.Domains, config_parse_delegate_domains, 0, 0 Delegate.DefaultRoute, config_parse_tristate, 0, offsetof(DnsDelegate, default_route) +Delegate.FirewallMark, config_parse_unsigned, 0, offsetof(DnsDelegate, fwmark) diff --git a/src/resolve/resolved-dns-delegate.h b/src/resolve/resolved-dns-delegate.h index dfb8429de2..5ec070db37 100644 --- a/src/resolve/resolved-dns-delegate.h +++ b/src/resolve/resolved-dns-delegate.h @@ -23,6 +23,8 @@ typedef struct DnsDelegate { int default_route; + uint32_t fwmark; + DnsScope *scope; LIST_FIELDS(DnsDelegate, delegates); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index c6c6bdc866..8fe0a8bc5f 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -475,6 +475,12 @@ static int dns_scope_socket( return r; } + if (s->delegate && s->delegate->fwmark > 0) { + r = setsockopt_int(fd, SOL_SOCKET, SO_MARK, s->delegate->fwmark); + if (r < 0) + return log_debug_errno(r, "Failed to set firewall mark on DNS socket: %m)"); + } + bool addr_is_nonlocal = s->link && !manager_find_link_address(s->manager, sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) && in_addr_is_localhost(sa.sa.sa_family, sockaddr_in_addr(&sa.sa)) == 0; diff --git a/test/units/TEST-75-RESOLVED.sh b/test/units/TEST-75-RESOLVED.sh index e6ef9214a3..b3656da940 100755 --- a/test/units/TEST-75-RESOLVED.sh +++ b/test/units/TEST-75-RESOLVED.sh @@ -1452,6 +1452,7 @@ testcase_delegate() { [Delegate] DNS=192.168.77.78 Domains=exercise.test +FirewallMark=42 EOF systemctl reload systemd-resolved resolvectl status