diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2025-04-15 17:16:21 +1000 |
---|---|---|
committer | Stefano Brivio <sbrivio@redhat.com> | 2025-04-15 19:45:19 +0200 |
commit | 04984578b00f7507a05544b7a5490b03ab2d5135 (patch) | |
tree | 75b033e6d108ffe44ff7b2aeefa84a88770a59e6 | |
parent | 3f995586b35494b08631081fbf609ff932110849 (diff) | |
download | passt-04984578b00f7507a05544b7a5490b03ab2d5135.tar passt-04984578b00f7507a05544b7a5490b03ab2d5135.tar.gz passt-04984578b00f7507a05544b7a5490b03ab2d5135.tar.bz2 passt-04984578b00f7507a05544b7a5490b03ab2d5135.tar.lz passt-04984578b00f7507a05544b7a5490b03ab2d5135.tar.xz passt-04984578b00f7507a05544b7a5490b03ab2d5135.tar.zst passt-04984578b00f7507a05544b7a5490b03ab2d5135.zip |
udp: Deal with errors as we go in udp_sock_fwd()
When we get an epoll event on a listening socket, we first deal with any
errors (udp_sock_errs()), then with any received packets (udp_sock_fwd()).
However, it's theoretically possible that new errors could get flagged on
the socket after we call udp_sock_errs(), in which case we could get errors
returned in in udp_sock_fwd() -> udp_peek_addr() -> recvmsg().
In fact, we do deal with this correctly, although the path is somewhat
non-obvious. The recvmsg() error will cause us to bail out of
udp_sock_fwd(), but the EPOLLERR event will now be flagged, so we'll come
back here next epoll loop and call udp_sock_errs().
Except.. we call udp_sock_fwd() from udp_flush_flow() as well as from
epoll events. This is to deal with any packets that arrived between bind()
and connect(), and so might not be associated with the socket's intended
flow. This expects udp_sock_fwd() to flush _all_ queued datagrams, so that
anything received later must be for the correct flow.
At the moment, udp_sock_errs() might fail to flush all datagrams if errors
occur. In particular this can happen in practice for locally reported
errors which occur immediately after connect() (e.g. connecting to a local
port with nothing listening).
We can deal with the problem case, and also make the flow a little more
natural for the common case by having udp_sock_fwd() call udp_sock_errs()
to handle errors as the occur, rather than trying to deal with all errors
in advance.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r-- | udp.c | 45 |
1 files changed, 26 insertions, 19 deletions
@@ -601,7 +601,7 @@ static int udp_sock_errs(const struct ctx *c, int s, flow_sidx_t sidx) * @src: Socket address (output) * @dst: (Local) destination address (output) * - * Return: 0 on success, -1 otherwise + * Return: 0 if no more packets, 1 on success, -ve error code on error */ static int udp_peek_addr(int s, union sockaddr_inany *src, union inany_addr *dst) @@ -619,9 +619,9 @@ static int udp_peek_addr(int s, union sockaddr_inany *src, rc = recvmsg(s, &msg, MSG_PEEK | MSG_DONTWAIT); if (rc < 0) { - trace("Error peeking at socket address: %s", strerror_(errno)); - /* Bail out and let the EPOLLERR handler deal with it */ - return rc; + if (errno == EAGAIN || errno == EWOULDBLOCK) + return 0; + return -errno; } hdr = CMSG_FIRSTHDR(&msg); @@ -644,7 +644,7 @@ static int udp_peek_addr(int s, union sockaddr_inany *src, sockaddr_ntop(src, sastr, sizeof(sastr)), inany_ntop(dst, dstr, sizeof(dstr))); - return 0; + return 1; } /** @@ -740,11 +740,27 @@ void udp_sock_fwd(const struct ctx *c, int s, uint8_t frompif, { union sockaddr_inany src; union inany_addr dst; + int rc; - while (udp_peek_addr(s, &src, &dst) == 0) { - flow_sidx_t tosidx = udp_flow_from_sock(c, frompif, - &dst, port, &src, now); - uint8_t topif = pif_at_sidx(tosidx); + while ((rc = udp_peek_addr(s, &src, &dst)) != 0) { + flow_sidx_t tosidx; + uint8_t topif; + + if (rc < 0) { + trace("Error peeking at socket address: %s", + strerror_(-rc)); + /* Clear errors & carry on */ + if (udp_sock_errs(c, s, FLOW_SIDX_NONE) < 0) { + err( +"UDP: Unrecoverable error on listening socket: (%s port %hu)", + pif_name(frompif), port); + /* FIXME: what now? close/re-open socket? */ + } + continue; + } + + tosidx = udp_flow_from_sock(c, frompif, &dst, port, &src, now); + topif = pif_at_sidx(tosidx); if (pif_is_socket(topif)) { udp_sock_to_sock(c, s, 1, tosidx); @@ -776,16 +792,7 @@ void udp_listen_sock_handler(const struct ctx *c, union epoll_ref ref, uint32_t events, const struct timespec *now) { - if (events & EPOLLERR) { - if (udp_sock_errs(c, ref.fd, FLOW_SIDX_NONE) < 0) { - err("UDP: Unrecoverable error on listening socket:" - " (%s port %hu)", pif_name(ref.udp.pif), ref.udp.port); - /* FIXME: what now? close/re-open socket? */ - return; - } - } - - if (events & EPOLLIN) + if (events & (EPOLLERR | EPOLLIN)) udp_sock_fwd(c, ref.fd, ref.udp.pif, ref.udp.port, now); } |