1
0
Fork 0
mirror of https://passt.top/passt synced 2025-08-14 19:03:12 +02:00

udp: Use connect()ed sockets for initiating side

Currently we have an asymmetry in how we handle UDP sockets.  For flows
where the target side is a socket, we create a new connect()ed socket
- the "reply socket" specifically for that flow used for sending and
receiving datagrams on that flow and only that flow.  For flows where the
initiating side is a socket, we continue to use the "listening" socket (or
rather, a dup() of it).  This has some disadvantages:

 * We need a hash lookup for every datagram on the listening socket in
   order to work out what flow it belongs to
 * The dup() keeps the socket alive even if automatic forwarding removes
   the listening socket.  However, the epoll data remains the same
   including containing the now stale original fd.  This causes bug 103.
 * We can't (easily) set flow-specific options on an initiating side
   socket, because that could affect other flows as well

Alter the code to use a connect()ed socket on the initiating side as well
as the target side.  There's no way to "clone and connect" the listening
socket (a loose equivalent of accept() for UDP), so we have to create a
new socket.  We have to bind() this socket before we connect() it, which
is allowed thanks to SO_REUSEADDR, but does leave a small window where it
could receive datagrams not intended for this flow.  For now we handle this
by simply discarding any datagrams received between bind() and connect(),
but I intend to improve this in a later patch.

Link: https://bugs.passt.top/show_bug.cgi?id=103
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2025-04-04 21:15:31 +11:00 committed by Stefano Brivio
commit d74b5a7c10
6 changed files with 43 additions and 55 deletions

View file

@ -49,10 +49,7 @@ void udp_flow_close(const struct ctx *c, struct udp_flow *uflow)
flow_foreach_sidei(sidei) {
flow_hash_remove(c, FLOW_SIDX(uflow, sidei));
if (uflow->s[sidei] >= 0) {
/* The listening socket needs to stay in epoll, but the
* flow specific one needs to be removed */
if (sidei == TGTSIDE)
epoll_del(c, uflow->s[sidei]);
epoll_del(c, uflow->s[sidei]);
close(uflow->s[sidei]);
uflow->s[sidei] = -1;
}
@ -81,7 +78,7 @@ static int udp_flow_sock(const struct ctx *c,
} fref = { .sidx = FLOW_SIDX(uflow, sidei) };
int rc, s;
s = flowside_sock_l4(c, EPOLL_TYPE_UDP_REPLY, pif, side, fref.data);
s = flowside_sock_l4(c, EPOLL_TYPE_UDP, pif, side, fref.data);
if (s < 0) {
flow_dbg_perror(uflow, "Couldn't open flow specific socket");
return s;
@ -120,13 +117,12 @@ static int udp_flow_sock(const struct ctx *c,
* udp_flow_new() - Common setup for a new UDP flow
* @c: Execution context
* @flow: Initiated flow
* @s_ini: Initiating socket (or -1)
* @now: Timestamp
*
* Return: UDP specific flow, if successful, NULL on failure
*/
static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
int s_ini, const struct timespec *now)
const struct timespec *now)
{
struct udp_flow *uflow = NULL;
unsigned sidei;
@ -139,22 +135,12 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
uflow->s[INISIDE] = uflow->s[TGTSIDE] = -1;
uflow->ttl[INISIDE] = uflow->ttl[TGTSIDE] = 0;
if (s_ini >= 0) {
/* When using auto port-scanning the listening port could go
* away, so we need to duplicate the socket
*/
uflow->s[INISIDE] = fcntl(s_ini, F_DUPFD_CLOEXEC, 0);
if (uflow->s[INISIDE] < 0) {
flow_perror(uflow,
"Couldn't duplicate listening socket");
goto cancel;
}
flow_foreach_sidei(sidei) {
if (pif_is_socket(uflow->f.pif[sidei]))
if ((uflow->s[sidei] = udp_flow_sock(c, uflow, sidei)) < 0)
goto cancel;
}
if (pif_is_socket(flow->f.pif[TGTSIDE]))
if ((uflow->s[TGTSIDE] = udp_flow_sock(c, uflow, TGTSIDE)) < 0)
goto cancel;
/* Tap sides always need to be looked up by hash. Socket sides don't
* always, but sometimes do (receiving packets on a socket not specific
* to one flow). Unconditionally hash both sides so all our bases are
@ -225,7 +211,7 @@ flow_sidx_t udp_flow_from_sock(const struct ctx *c, union epoll_ref ref,
return FLOW_SIDX_NONE;
}
return udp_flow_new(c, flow, ref.fd, now);
return udp_flow_new(c, flow, now);
}
/**
@ -281,7 +267,7 @@ flow_sidx_t udp_flow_from_tap(const struct ctx *c,
return FLOW_SIDX_NONE;
}
return udp_flow_new(c, flow, -1, now);
return udp_flow_new(c, flow, now);
}
/**