From c382d39a85c1edbaf96f0cd84385cd6549c0bde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Fri, 29 May 2020 10:17:32 +0200 Subject: [PATCH] linux,udp: enable full ICMP error reporting The Linux kernel suppresses some ICMP error messages by default for UDP sockets. This commit sets IP_RECVERR/IPV6_RECVERR on the socket to enable full ICMP error reporting, hopefully resulting in faster failover to working name servers. PR-URL: https://github.com/libuv/libuv/pull/2872 Reviewed-By: Ben Noordhuis Reviewed-By: Santiago Gimeno --- docs/src/udp.rst | 11 ++++++++++- include/uv.h | 9 ++++++++- src/unix/udp.c | 30 +++++++++++++++++++++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docs/src/udp.rst b/docs/src/udp.rst index 827fbaad..3a7c6db4 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -53,6 +53,14 @@ Data types * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. */ UV_UDP_MMSG_FREE = 16, + /* + * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. + * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on + * Linux. This stops the Linux kernel from supressing some ICMP error messages + * and enables full ICMP error reporting for faster failover. + * This flag is no-op on platforms other than Linux. + */ + UV_UDP_LINUX_RECVERR = 32, /* * Indicates that recvmmsg should be used, if available. */ @@ -178,7 +186,8 @@ API with the address and port to bind to. :param flags: Indicate how the socket will be bound, - ``UV_UDP_IPV6ONLY`` and ``UV_UDP_REUSEADDR`` are supported. + ``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, and ``UV_UDP_RECVERR`` + are supported. :returns: 0 on success, or an error code < 0 on failure. diff --git a/include/uv.h b/include/uv.h index 1e1fc94b..cbdf1b02 100644 --- a/include/uv.h +++ b/include/uv.h @@ -626,7 +626,14 @@ enum uv_udp_flags { * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. */ UV_UDP_MMSG_FREE = 16, - + /* + * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. + * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on + * Linux. This stops the Linux kernel from supressing some ICMP error messages + * and enables full ICMP error reporting for faster failover. + * This flag is no-op on platforms other than Linux. + */ + UV_UDP_LINUX_RECVERR = 32, /* * Indicates that recvmmsg should be used, if available. */ diff --git a/src/unix/udp.c b/src/unix/udp.c index 7d699a16..5d0ec8d3 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -504,6 +504,28 @@ static int uv__set_reuse(int fd) { return 0; } +/* + * The Linux kernel suppresses some ICMP error messages by default for UDP + * sockets. Setting IP_RECVERR/IPV6_RECVERR on the socket enables full ICMP + * error reporting, hopefully resulting in faster failover to working name + * servers. + */ +static int uv__set_recverr(int fd, sa_family_t ss_family) { +#if defined(__linux__) + int yes; + + yes = 1; + if (ss_family == AF_INET) { + if (setsockopt(fd, IPPROTO_IP, IP_RECVERR, &yes, sizeof(yes))) + return UV__ERR(errno); + } else if (ss_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &yes, sizeof(yes))) + return UV__ERR(errno); + } +#endif + return 0; +} + int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, @@ -514,7 +536,7 @@ int uv__udp_bind(uv_udp_t* handle, int fd; /* Check for bad flags. */ - if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR)) + if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR)) return UV_EINVAL; /* Cannot set IPv6-only mode on non-IPv6 socket. */ @@ -530,6 +552,12 @@ int uv__udp_bind(uv_udp_t* handle, handle->io_watcher.fd = fd; } + if (flags & UV_UDP_LINUX_RECVERR) { + err = uv__set_recverr(fd, addr->sa_family); + if (err) + return err; + } + if (flags & UV_UDP_REUSEADDR) { err = uv__set_reuse(fd); if (err)