Bug 15536 - Samba internal DNS client is limited to UDP 512 bytes (not even EDNS0-aware), can not find servers in large AD
Summary: Samba internal DNS client is limited to UDP 512 bytes (not even EDNS0-aware),...
Status: NEW
Alias: None
Product: Samba 4.1 and newer
Classification: Unclassified
Component: AD: LDB/DSDB/SAMDB (show other bugs)
Version: 4.19.3
Hardware: All All
: P5 normal (vote)
Target Milestone: ---
Assignee: Samba QA Contact
QA Contact: Samba QA Contact
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-12-13 21:03 UTC by Michael Tokarev
Modified: 2024-02-26 12:13 UTC (History)
2 users (show)

See Also:


Attachments
patch adding minimal EDNS0 support (1.82 KB, patch)
2024-02-26 11:22 UTC, Michael Tokarev
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Tokarev 2023-12-13 21:03:38 UTC
When joining a samba server to an AD Domain, samba software uses DNS to find various components of the structure (with SRV records).  The samba internal DNS client seems to be UDP-only.  However, with a large distributed AD Domain, list of SRV records does not fit in a single UDP packet, even when EDNS0 is in use.  There's an example AD setup in wild with about 70 SRV records for _ldap._tcp.dc._msdcs (properly grouped in different sites, fwiw), - and in such a domain, samba server is unable to find any auth servers at all, since the nameserver return 0-entry answer with TC bit set, which means the query should be retried using TCP.

For added fun, this is observed with local named9 cache running between samba and the AD - named does not return any answers to this query when ADDITIONAL section (with A records for these SRVs) does not fit in a single UDP packet but all SRVs can fit).  This is unlike the windows nameservers which return partial ADDITIONAL section and complete ANSWER section in such case, -- this is the workaround we used, - switched to avoid local named9 cache.
Comment 1 Michael Tokarev 2023-12-22 15:09:30 UTC
Looking at bind9 source, it seems like it *should* return partial reply if ADDITIONAL section does not fit: https://gitlab.isc.org/isc-projects/bind9/-/blob/main/lib/ns/client.c?ref_type=heads#L584 (DNS_MESSAGEFLAG_TC handling in various cases).
Comment 2 Michael Tokarev 2023-12-22 22:13:01 UTC
After more debugging, it turned out the problem is the EDNS0 large packets handling in named9.  It has max-udp-size which is 1232 by default (so the resulting packet fits into 1500-sized MTU).  And this is the upper limit of any UDP reply named sends.  Even to queries sent from loopback interface!  Apparently other nameservers (including microsoft dns, unbound, systemd-resolved) has much more relaxed default value, even if it is larger than typical ethernet MTU of 1500 bytes.  So the end result is that queries to named cache over loopback interface doesn't work, but queries over LAN to a non-local nameserver (even located in a different city!) works.  Go figure..  And indeed, I didn't know about max-udp-size option in named, - I found edns-udp-size, increased it, but that didn't help.  When large UDP packets are sent over network, they're split into two and reaches destination just fine.  And this is completely unnecessary on a loobpack, obviously.

So my problem is solved by increasing max-udp-size in named.conf.  And this whole - quite interesting - investigation shows two issues facing each other. I'm not sure which side is better to address the resulting non-working samba, - either by implementing TCP DNS queries in samba or "misconfiguring" named to send replies larger than network allows, but being efficient at that.
Comment 3 Jeremy Allison 2023-12-23 05:49:28 UTC
Look in vi libcli/dns/dns.c:dns_cli_request_udp_done()



        operation = PULL_BE_U16(reply.data, 2);
        if ((operation & DNS_FLAG_TRUNCATION) != 0) {
                DBG_DEBUG("Reply was truncated, retrying TCP\n");
                subreq = dns_tcp_request_send(
                        state,
                        state->ev,
                        state->nameserver,
                        state->query.data,
                        state->query.length);

This needs to be plumbed into the code path you're investigating.

However, it is only a simple DNS resolver. We really should eventually convert to an external supported async DNS resolver as we don't want to handle DNS-over-TLS and other exotica.
Comment 4 Michael Tokarev 2024-02-26 07:27:19 UTC
Actually this is much simpler *and* more stupid than I thought..

It looks out samba does not implement EDNS0 at all.  While I was sure it does.

So the reply is limited to standard 512 bytes.

This is the reason why winbind chooses DCs in random remote regions instead of the ones from the same site, - because windows nameservers return whatever SRV records which fits into 512 bytes, which aren't necessary the ones in the same site, and samba have to deal with whatever it has..

This is libcli/dns/dns.c:dns_cli_request_send() I think.  It should include ADDITIONAL section besides QUERY section, and ADDITIONAL should contain one EDNS0 record with the indicated max UDP packet size it is willing to accept.

I wonder if we can enable EDNS0 unconditionally these days..
Comment 5 Michael Tokarev 2024-02-26 11:22:12 UTC
Created attachment 18260 [details]
patch adding minimal EDNS0 support

This small patch fixes the issue for now, - until the SRV list wont fit in extended packet too.