some unexpected errors occur, responding to IPv4 clients on NetBSD
Summary
BIND keeps logging (not super-high frequency, but enough to worry):
Oct 11 20:16:44 nn named[478]: socket.c:2170: unexpected error:
Oct 11 20:16:44 nn named[478]: internal_send: 62.212.64.122#47249: Invalid argument
Oct 11 20:16:44 nn named[478]: client @0x7f7fec9de000 62.212.64.122#47249 (smtp.hedmark.org): error sending response: invalid file
...
Oct 11 20:16:51 nn named[478]: socket.c:2170: unexpected error:
Oct 11 20:16:51 nn named[478]: internal_send: 109.247.114.11#33800: Invalid argument
Oct 11 20:16:51 nn named[478]: client @0x7f7fec9ee000 109.247.114.11#33800 (gbubwsavujj.hedmark.org): error sending response: invalid file
Oct 11 20:16:51 nn named[478]: socket.c:2170: unexpected error:
Oct 11 20:16:51 nn named[478]: internal_send: 109.247.114.11#33617: Invalid argument
Oct 11 20:16:51 nn named[478]: client @0x7f7fec8ce000 109.247.114.11#33617 (sdtwslh.hedmark.org): error sending response: invalid file
I suspect the corresponding messages gets lost.
BIND version used
BIND 9.12.2-P2 <id:b2bf278>
running on NetBSD amd64 7.1_STABLE NetBSD 7.1_STABLE (GENERIC) #0: Mon Feb 5 12:42:24 CET 2018 he@xxxxxx:/usr/obj/sys/arch/amd64/compile/GENERIC
built by make with '--with-libxml2=yes' '--with-tuning=large' '--with-libtool'
compiled by GCC 4.8.4
compiled with OpenSSL version: OpenSSL 1.0.1t 3 May 2016
linked to OpenSSL version: OpenSSL 1.0.1t 3 May 2016
compiled with libxml2 version: 2.9.4
linked to libxml2 version: 20904
compiled with zlib version: 1.2.3
linked to zlib version: 1.2.3
threads support is enabled
Steps to reproduce
Run a publishing instance of BIND on NetBSD, deployed in a dual-stack IPv4/IPv6 environment.
Watch the log, observe these occur from time to time.
Our name server provides slave service for many others, and serves a little more than 3500 zones, and receives around 3-400 qps.
What is the current bug behavior?
Some packets are in all probability being lost on the way out of the BIND name server.
Doing some system call tracing reveals:
478 5 named CALL sendmsg(0x202,0x7f7ff07f8e80,0)
478 5 named MISC msghdr: [name=0x7f7feca05098, namelen=16, iov=0x7f7ff07f8ef0, iovlen=1, control=0x7f7ff07f8ff0, controllen=24, flags=4000000]
478 5 named MISC mbsoname: [74.125.46.12]
478 5 named MISC mbcontrol: [len=20, level=41, type=42]
478 5 named RET sendmsg -1 errno 22 Invalid argument
The important points here are:
- The response is being sent to an IPv4 destination
- The "control message" is for level=41 == IPPROTO_IPV6
- The "control type" is type=42 == IPV6_USE_MIN_MTU
Knowing that NetBSD insists on separate sockets for IPv4 and IPv6, it will in all probability balk at this, and this is the probable cause for returning EINVAL and dropping the packet.
Now, lib/isc/unix/socket.c
's build_msghdr_send()
only adds the
IPV6_USE_MIN_MTU if it is a UDP socket and
((dev->attributes & ISC_SOCKEVENTATTR_USEMINMTU) != 0)
holds true.
In turn, the ISC_SOCKEVENTATTR_USEMINMTU
is set in lib/ns/client.c
under this condition: (!TCP_CLIENT(client) && r.length > 1432)
.
You will notice the conspicuous absence of testing for what protocol
family this socket actually belongs to, and this therefore probably
causes all UDP responses larger than 1432 to IPv4 destinations to be
dropped.
What is the expected correct behavior?
IPv4 UDP response packets above size 1432 should not be dropped.
...by avoiding setting IPv6-only-relevant options in IPv4 sockets.
Relevant configuration files
This isn't dependent on configuration.
Relevant logs and/or screenshots
Excerpt of log found above.
Possible fixes
The check for the appropriate protocol family appear to be possible
to do in lib/isc/unix/socket.c
:
--- lib/isc/unix/socket.c.orig 2018-09-04 03:50:19.000000000 +0000
+++ lib/isc/unix/socket.c
@@ -1572,7 +1572,8 @@ build_msghdr_send(isc__socket_t *sock, c
#if defined(IPV6_USE_MIN_MTU)
if ((sock->type == isc_sockettype_udp) &&
- ((dev->attributes & ISC_SOCKEVENTATTR_USEMINMTU) != 0))
+ ((dev->attributes & ISC_SOCKEVENTATTR_USEMINMTU) != 0) &&
+ (sock->pf == AF_INET6))
{
int use_min_mtu = 1; /* -1, 0, 1 */
Perhaps suitably adorned with ifdefs for those kind of systems which do not need this additional test (Linux?).