From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail.ozlabs.org (gandalf.ozlabs.org [150.107.74.76]) by passt.top (Postfix) with ESMTPS id 7515E5A004F for ; Wed, 17 Jul 2024 02:36:15 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202312; t=1721176566; bh=HQ24gLpspCVkeG1EmxaD6iNlTymi7fNTAAzI44GtydM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=l2f+G7xSKYxZK1pW4Q8drJnbaYlCzYX99h2+PRgvq7vOlpIDYTI8lxPK/xUu0ohPs f5HcHnljn1yimj+AeZOqdbwf0nKYY1CEE3uBcreH2dg6DWEslxqDyBTnUEVwJ2k81r XJyD94xz5Sb8duDKVV/2z2lSzvUHEhxJDtU3aMnyEraT0pGE5XFeLNrr2/oVy4GMnK QRMlvPslsvfZnsvL9IK/0fMwR9MzF4dkwdwCzaq2CXWaYWB+ETtd44W4XEklHzcxgp VY+mR0Z6EtBKPP7idhUBe53QAgMGYUTjhCTDSK697ucKv24dBmyRaArPNTE+3gxw5j 4rzNxwZlqKnkw== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4WNxnL0KJYz4x3J; Wed, 17 Jul 2024 10:36:06 +1000 (AEST) From: David Gibson To: Stefano Brivio , passt-dev@passt.top Subject: [PATCH v2 5/5] udp: Handle errors on UDP sockets Date: Wed, 17 Jul 2024 10:36:04 +1000 Message-ID: <20240717003604.1577052-6-david@gibson.dropbear.id.au> X-Mailer: git-send-email 2.45.2 In-Reply-To: <20240717003604.1577052-1-david@gibson.dropbear.id.au> References: <20240717003604.1577052-1-david@gibson.dropbear.id.au> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: BJM2M5XWJOLXK5FJMZT7UIGPI6FO7YGM X-Message-ID-Hash: BJM2M5XWJOLXK5FJMZT7UIGPI6FO7YGM X-MailFrom: dgibson@gandalf.ozlabs.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: David Gibson X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Currently we ignore all events other than EPOLLIN on UDP sockets. This means that if we ever receive an EPOLLERR event, we'll enter an infinite loop on epoll, because we'll never do anything to clear the error. Luckily that doesn't seem to have happened in practice, but it's certainly fragile. Furthermore changes in how we handle UDP sockets with the flow table mean we will start receiving error events. Add handling of EPOLLERR events. For now we just read the error from the error queue (thereby clearing the error state) and print a debug message. We can add more substantial handling of specific events in future if we want to. Signed-off-by: David Gibson --- udp.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.c | 29 ++++++++++++++++++++++++++++ util.h | 3 +++ 3 files changed, 92 insertions(+) diff --git a/udp.c b/udp.c index fbf3ce73..150f970a 100644 --- a/udp.c +++ b/udp.c @@ -110,6 +110,7 @@ #include #include #include +#include #include "checksum.h" #include "util.h" @@ -728,6 +729,59 @@ static void udp_tap_prepare(const struct ctx *c, const struct mmsghdr *mmh, (*tap_iov)[UDP_IOV_PAYLOAD].iov_len = l4len; } +/** + * udp_sock_recverr() - Receive and clear an error from a socket + * @s: Socket to receive from + * + * Return: true if errors received and processed, false if no more errors + * + * #syscalls recvmsg + */ +static bool udp_sock_recverr(int s) +{ + const struct sock_extended_err *ee; + const struct cmsghdr *hdr; + char buf[CMSG_SPACE(sizeof(*ee))]; + struct msghdr mh = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = NULL, + .msg_iovlen = 0, + .msg_control = buf, + .msg_controllen = sizeof(buf), + }; + ssize_t rc; + + rc = recvmsg(s, &mh, MSG_ERRQUEUE); + if (rc < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + err_perror("Failed to read error queue"); + return false; + } + + if (!(mh.msg_flags & MSG_ERRQUEUE)) { + err("Missing MSG_ERRQUEUE flag reading error queue"); + return false; + } + + hdr = CMSG_FIRSTHDR(&mh); + if (!((hdr->cmsg_level == IPPROTO_IP && + hdr->cmsg_type == IP_RECVERR) || + (hdr->cmsg_level == IPPROTO_IPV6 && + hdr->cmsg_type == IPV6_RECVERR))) { + err("Unexpected cmsg reading error queue"); + return false; + } + + ee = (const struct sock_extended_err *)CMSG_DATA(hdr); + + /* TODO: When possible propagate and otherwise handle errors */ + debug("%s error on UDP socket %i: %s", + str_ee_origin(ee), s, strerror(ee->ee_errno)); + + return true; +} + /** * udp_sock_recv() - Receive datagrams from a socket * @c: Execution context @@ -751,6 +805,12 @@ static int udp_sock_recv(const struct ctx *c, int s, uint32_t events, ASSERT(!c->no_udp); + /* Clear any errors first */ + if (events & EPOLLERR) { + while (udp_sock_recverr(s)) + ; + } + if (!(events & EPOLLIN)) return 0; diff --git a/util.c b/util.c index 428847d2..1569f1c0 100644 --- a/util.c +++ b/util.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "util.h" #include "iov.h" @@ -97,6 +98,14 @@ static int sock_l4_sa(const struct ctx *c, enum epoll_type type, if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y))) debug("Failed to set SO_REUSEADDR on socket %i", fd); + if (proto == IPPROTO_UDP) { + int level = af == AF_INET ? IPPROTO_IP : IPPROTO_IPV6; + int opt = af == AF_INET ? IP_RECVERR : IPV6_RECVERR; + + if (setsockopt(fd, level, opt, &y, sizeof(y))) + die_perror("Failed to set RECVERR on socket %i", fd); + } + if (ifname && *ifname) { /* Supported since kernel version 5.7, commit c427bfec18f2 * ("net: core: enable SO_BINDTODEVICE for non-root users"). If @@ -654,3 +663,23 @@ const char *sockaddr_ntop(const void *sa, char *dst, socklen_t size) return dst; } + +/** str_ee_origin() - Convert socket extended error origin to a string + * @ee: Socket extended error structure + * + * Return: Static string describing error origin + */ +const char *str_ee_origin(const struct sock_extended_err *ee) +{ + const char *const desc[] = { + [SO_EE_ORIGIN_NONE] = "", + [SO_EE_ORIGIN_LOCAL] = "Local", + [SO_EE_ORIGIN_ICMP] = "ICMP", + [SO_EE_ORIGIN_ICMP6] = "ICMPv6", + }; + + if (ee->ee_origin < ARRAY_SIZE(desc)) + return desc[ee->ee_origin]; + + return ""; +} diff --git a/util.h b/util.h index d0150396..1d479ddf 100644 --- a/util.h +++ b/util.h @@ -194,7 +194,10 @@ static inline const char *af_name(sa_family_t af) #define SOCKADDR_STRLEN MAX(SOCKADDR_INET_STRLEN, SOCKADDR_INET6_STRLEN) +struct sock_extended_err; + const char *sockaddr_ntop(const void *sa, char *dst, socklen_t size); +const char *str_ee_origin(const struct sock_extended_err *ee); /** * mod_sub() - Modular arithmetic subtraction -- 2.45.2