diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2024-07-17 10:36:04 +1000 |
---|---|---|
committer | Stefano Brivio <sbrivio@redhat.com> | 2024-07-17 07:05:21 +0200 |
commit | 2fa91ee391b7bbed960bf404a405573c64d836aa (patch) | |
tree | d958e4017cd4bee9fd4c0a0486000e7ed76613b9 /udp.c | |
parent | 6bd8283bf962126da73612737a5730082b70b5f3 (diff) | |
download | passt-2fa91ee391b7bbed960bf404a405573c64d836aa.tar passt-2fa91ee391b7bbed960bf404a405573c64d836aa.tar.gz passt-2fa91ee391b7bbed960bf404a405573c64d836aa.tar.bz2 passt-2fa91ee391b7bbed960bf404a405573c64d836aa.tar.lz passt-2fa91ee391b7bbed960bf404a405573c64d836aa.tar.xz passt-2fa91ee391b7bbed960bf404a405573c64d836aa.tar.zst passt-2fa91ee391b7bbed960bf404a405573c64d836aa.zip |
udp: Handle errors on UDP sockets
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 <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
Diffstat (limited to 'udp.c')
-rw-r--r-- | udp.c | 60 |
1 files changed, 60 insertions, 0 deletions
@@ -110,6 +110,7 @@ #include <sys/socket.h> #include <sys/uio.h> #include <time.h> +#include <linux/errqueue.h> #include "checksum.h" #include "util.h" @@ -729,6 +730,59 @@ static void udp_tap_prepare(const struct ctx *c, const struct mmsghdr *mmh, } /** + * 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 * @s: Socket to receive from @@ -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; |