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:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user