resolved: split oversized mDNS announcements across MTU-sized packets (#43667)

Split out of https://github.com/systemd/systemd/pull/42983

## Problem
`dns_scope_announce()` put the entire zone into a single reply packet —
for DNS-SD that is every PTR/SRV/TXT record of every published service.
With a few hundred services that grows far past the interface MTU, and
past what RFC 6762 section 17 allows: a Multicast DNS packet larger than
the MTU is sent using fragments, and such a packet "MUST NOT contain
more than one resource record".

## Fix
Pack an announcement into as many MTU-sized packets as needed. A record
that does not fit the MTU on its own is emitted alone in an oversized
packet relying on IP fragmentation — the case section 17 explicitly
allows — bounded by its hard 9000-byte ceiling including headers, which
also caps jumbo-frame links.

Without a usable interface MTU (no link on the scope, or `IFLA_MTU` not
read yet) packets are cut to the largest datagram a host of the family
must accept unfragmented — RFC 791's 576 bytes for IPv4, RFC 8200
section 5's 1280-byte minimum link MTU for IPv6 — rather than to that
ceiling, so an announcement is never fragmented merely because the MTU
was not known yet, and the lone-oversized-record path stays reachable.

A record that cannot be encoded at any size (an rdata past 64KiB, a
character string past 255 bytes) is skipped like one too large for the
ceiling rather than failing the whole announcement:
`dns_packet_append_rr()` rolls the packet back on every failure, so its
neighbours are unaffected either way.

The packing lives in `resolved-mdns.c` as
`mdns_announcement_packetize()`, taking an answer and the two size
bounds and returning finalized packets, so it is testable without a
scope, a link or a socket. Emission is best effort per packet — a failed
send no longer withholds the rest — and each pass logs the packet and
record counts it actually put on the wire.
This commit is contained in:
Yu Watanabe
2026-09-23 11:36:09 +09:00
committed by GitHub
7 changed files with 869 additions and 28 deletions
+2
View File
@@ -297,6 +297,8 @@ DnsPacket *dns_packet_unref(DnsPacket *p) {
return NULL;
}
DEFINE_POINTER_ARRAY_FREE_FUNC(DnsPacket*, dns_packet_unref);
int dns_packet_validate(DnsPacket *p) {
assert(p);
+1
View File
@@ -160,6 +160,7 @@ void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool trun
DnsPacket *dns_packet_ref(DnsPacket *p);
DnsPacket *dns_packet_unref(DnsPacket *p);
void dns_packet_unref_array(DnsPacket **array, size_t n);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsPacket*, dns_packet_unref);