public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH] udp: Split activity timeouts for UDP flows
@ 2026-02-12  8:04 Yumei Huang
  2026-02-12  8:59 ` Stefano Brivio
  2026-02-12 21:51 ` Stefano Brivio
  0 siblings, 2 replies; 11+ messages in thread
From: Yumei Huang @ 2026-02-12  8:04 UTC (permalink / raw)
  To: passt-dev, sbrivio; +Cc: david, yuhuang

Frequent DNS queries over UDP from a container or guest can result
in many sockets shown in ss(8), typically one per flow. This is
expected and harmless, but it can make the output of ss(8) look
noisy and potentially concern users.

This patch splits UDP flow timeouts into two, mirroring the Linux
kernel, and sources the values from kernel parameters. The shorter
timeout is applied to unidirectional flows and minimal bidirectional
exchanges (single datagram and reply), while the longer timeout is
used for bidirectional flows with multiple datagrams on either side.

Link: https://bugs.passt.top/show_bug.cgi?id=197
Suggested-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: Yumei Huang <yuhuang@redhat.com>
---
 udp.c      | 33 ++++++++++++++++++++++++++++++++-
 udp.h      |  4 ++++
 udp_flow.c | 11 ++++++++---
 udp_flow.h | 13 +++++++++++++
 4 files changed, 57 insertions(+), 4 deletions(-)

diff --git a/udp.c b/udp.c
index b2383e2..3afec35 100644
--- a/udp.c
+++ b/udp.c
@@ -26,7 +26,10 @@
  *
  * We track pseudo-connections of this type as flow table entries of type
  * FLOW_UDP.  We store the time of the last traffic on the flow in uflow->ts,
- * and let the flow expire if there is no traffic for UDP_CONN_TIMEOUT seconds.
+ * and let the flow expire if there is no traffic for UDP_TIMEOUT seconds for
+ * unidirectional flows and flows with only one datagram and one reply, or
+ * UDP_TIMEOUT_STREAM seconds for bidirectional flows with more than one
+ * datagram on either side.
  *
  * NOTE: This won't handle multicast protocols, or some protocols with different
  * port usage.  We'll need specific logic if we want to handle those.
@@ -118,6 +121,13 @@
 
 #define UDP_MAX_FRAMES		32  /* max # of frames to receive at once */
 
+#define UDP_TIMEOUT	"/proc/sys/net/netfilter/nf_conntrack_udp_timeout"
+#define UDP_TIMEOUT_STREAM	\
+	"/proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream"
+
+#define UDP_TIMEOUT_DEFAULT		30	/* s */
+#define UDP_TIMEOUT_STREAM_DEFAULT	120	/* s */
+
 /* Maximum UDP data to be returned in ICMP messages */
 #define ICMP4_MAX_DLEN 8
 #define ICMP6_MAX_DLEN (IPV6_MIN_MTU			\
@@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c, union epoll_ref ref,
 
 		flow_trace(uflow, "Received data on reply socket");
 		uflow->ts = now->tv_sec;
+		udp_flow_activity(uflow, !tosidx.sidei);
 
 		if (pif_is_socket(topif)) {
 			udp_sock_to_sock(c, ref.fd, n, tosidx);
@@ -1179,6 +1190,24 @@ static void udp_splice_iov_init(void)
 	}
 }
 
+/**
+ * udp_get_timeout_params() - Get host kernel UDP timeout parameters
+ * @c:		Execution context
+ */
+static void udp_get_timeout_params(struct ctx *c)
+{
+	intmax_t v;
+
+	v = read_file_integer(UDP_TIMEOUT, UDP_TIMEOUT_DEFAULT);
+	c->udp.timeout = v;
+
+	v = read_file_integer(UDP_TIMEOUT_STREAM, UDP_TIMEOUT_STREAM_DEFAULT);
+	c->udp.stream_timeout = v;
+
+	debug("Using UDP timeout parameters, timeout: %d, stream_timeout: %d",
+	      c->udp.timeout, c->udp.stream_timeout);
+}
+
 /**
  * udp_init() - Initialise per-socket data, and sockets in namespace
  * @c:		Execution context
@@ -1189,6 +1218,8 @@ int udp_init(struct ctx *c)
 {
 	ASSERT(!c->no_udp);
 
+	udp_get_timeout_params(c);
+
 	udp_iov_init(c);
 
 	if (fwd_listen_sync(c, &c->udp.fwd_in, PIF_HOST, IPPROTO_UDP) < 0)
diff --git a/udp.h b/udp.h
index 2b91d72..da9c2df 100644
--- a/udp.h
+++ b/udp.h
@@ -24,11 +24,15 @@ void udp_update_l2_buf(const unsigned char *eth_d);
  * @fwd_in:		Port forwarding configuration for inbound packets
  * @fwd_out:		Port forwarding configuration for outbound packets
  * @timer_run:		Timestamp of most recent timer run
+ * @timeout:		Timeout for unidirectional flows (in s)
+ * @stream_timeout:	Timeout for stream-like flows (in s)
  */
 struct udp_ctx {
 	struct fwd_ports fwd_in;
 	struct fwd_ports fwd_out;
 	struct timespec timer_run;
+	int timeout;
+	int stream_timeout;
 };
 
 #endif /* UDP_H */
diff --git a/udp_flow.c b/udp_flow.c
index 1f5e84e..9b22586 100644
--- a/udp_flow.c
+++ b/udp_flow.c
@@ -17,8 +17,6 @@
 #include "udp_internal.h"
 #include "epoll_ctl.h"
 
-#define UDP_CONN_TIMEOUT	180 /* s, timeout for ephemeral or local bind */
-
 /**
  * udp_at_sidx() - Get UDP specific flow at given sidx
  * @sidx:    Flow and side to retrieve
@@ -152,6 +150,7 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
 	uflow->ts = now->tv_sec;
 	uflow->s[INISIDE] = uflow->s[TGTSIDE] = -1;
 	uflow->ttl[INISIDE] = uflow->ttl[TGTSIDE] = 0;
+	uflow->activity[INISIDE] = uflow->activity[TGTSIDE] = 0;
 
 	flow_foreach_sidei(sidei) {
 		if (pif_is_socket(uflow->f.pif[sidei]))
@@ -362,7 +361,13 @@ bool udp_flow_defer(const struct ctx *c, struct udp_flow *uflow,
 bool udp_flow_timer(const struct ctx *c, struct udp_flow *uflow,
 		    const struct timespec *now)
 {
-	if (now->tv_sec - uflow->ts <= UDP_CONN_TIMEOUT)
+	int timeout = c->udp.timeout;
+
+	if (uflow->activity[TGTSIDE] &&
+	    (uflow->activity[INISIDE] > 1 || uflow->activity[TGTSIDE] > 1))
+		timeout = c->udp.stream_timeout;
+
+	if (now->tv_sec - uflow->ts <= timeout)
 		return false;
 
 	udp_flow_close(c, uflow);
diff --git a/udp_flow.h b/udp_flow.h
index 14e0f92..158a0f6 100644
--- a/udp_flow.h
+++ b/udp_flow.h
@@ -16,6 +16,7 @@
  * @flush1:	@s[1] may have datagrams queued for other flows
  * @ts:		Activity timestamp
  * @s:		Socket fd (or -1) for each side of the flow
+ * @activity:	Activity for each side of the flow
  */
 struct udp_flow {
 	/* Must be first element */
@@ -29,8 +30,20 @@ struct udp_flow {
 
 	time_t ts;
 	int s[SIDES];
+	uint8_t activity[SIDES];
 };
 
+/**
+ * udp_flow_activity() - Track activity of a udp flow
+ * @uflow:	UDP flow
+ * @sidei:	Side index of the flow
+ */
+static inline void udp_flow_activity(struct udp_flow *uflow, unsigned int sidei)
+{
+	if (uflow->activity[sidei] < UINT8_MAX)
+		uflow->activity[sidei]++;
+}
+
 struct udp_flow *udp_at_sidx(flow_sidx_t sidx);
 flow_sidx_t udp_flow_from_sock(const struct ctx *c, uint8_t pif,
 			       const union inany_addr *dst, in_port_t port,
-- 
2.52.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-12  8:04 [PATCH] udp: Split activity timeouts for UDP flows Yumei Huang
@ 2026-02-12  8:59 ` Stefano Brivio
  2026-02-12 21:51 ` Stefano Brivio
  1 sibling, 0 replies; 11+ messages in thread
From: Stefano Brivio @ 2026-02-12  8:59 UTC (permalink / raw)
  To: Yumei Huang; +Cc: passt-dev, david

I haven't tested this yet, just two exceedingly minor nits (I can fix
it up on merge if you're fine with it and if no other changes are
needed):

On Thu, 12 Feb 2026 16:04:14 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> Frequent DNS queries over UDP from a container or guest can result
> in many sockets shown in ss(8), typically one per flow. This is
> expected and harmless, but it can make the output of ss(8) look
> noisy and potentially concern users.
> 
> This patch splits UDP flow timeouts into two, mirroring the Linux
> kernel, and sources the values from kernel parameters. The shorter
> timeout is applied to unidirectional flows and minimal bidirectional
> exchanges (single datagram and reply), while the longer timeout is
> used for bidirectional flows with multiple datagrams on either side.
> 
> Link: https://bugs.passt.top/show_bug.cgi?id=197
> Suggested-by: Stefano Brivio <sbrivio@redhat.com>
> Signed-off-by: Yumei Huang <yuhuang@redhat.com>
> ---
>  udp.c      | 33 ++++++++++++++++++++++++++++++++-
>  udp.h      |  4 ++++
>  udp_flow.c | 11 ++++++++---
>  udp_flow.h | 13 +++++++++++++
>  4 files changed, 57 insertions(+), 4 deletions(-)
> 
> diff --git a/udp.c b/udp.c
> index b2383e2..3afec35 100644
> --- a/udp.c
> +++ b/udp.c
> @@ -26,7 +26,10 @@
>   *
>   * We track pseudo-connections of this type as flow table entries of type
>   * FLOW_UDP.  We store the time of the last traffic on the flow in uflow->ts,
> - * and let the flow expire if there is no traffic for UDP_CONN_TIMEOUT seconds.
> + * and let the flow expire if there is no traffic for UDP_TIMEOUT seconds for
> + * unidirectional flows and flows with only one datagram and one reply, or
> + * UDP_TIMEOUT_STREAM seconds for bidirectional flows with more than one
> + * datagram on either side.
>   *
>   * NOTE: This won't handle multicast protocols, or some protocols with different
>   * port usage.  We'll need specific logic if we want to handle those.
> @@ -118,6 +121,13 @@
>  
>  #define UDP_MAX_FRAMES		32  /* max # of frames to receive at once */
>  
> +#define UDP_TIMEOUT	"/proc/sys/net/netfilter/nf_conntrack_udp_timeout"
> +#define UDP_TIMEOUT_STREAM	\
> +	"/proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream"
> +
> +#define UDP_TIMEOUT_DEFAULT		30	/* s */
> +#define UDP_TIMEOUT_STREAM_DEFAULT	120	/* s */
> +
>  /* Maximum UDP data to be returned in ICMP messages */
>  #define ICMP4_MAX_DLEN 8
>  #define ICMP6_MAX_DLEN (IPV6_MIN_MTU			\
> @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c, union epoll_ref ref,
>  
>  		flow_trace(uflow, "Received data on reply socket");
>  		uflow->ts = now->tv_sec;
> +		udp_flow_activity(uflow, !tosidx.sidei);
>  
>  		if (pif_is_socket(topif)) {
>  			udp_sock_to_sock(c, ref.fd, n, tosidx);
> @@ -1179,6 +1190,24 @@ static void udp_splice_iov_init(void)
>  	}
>  }
>  
> +/**
> + * udp_get_timeout_params() - Get host kernel UDP timeout parameters
> + * @c:		Execution context
> + */
> +static void udp_get_timeout_params(struct ctx *c)
> +{
> +	intmax_t v;
> +
> +	v = read_file_integer(UDP_TIMEOUT, UDP_TIMEOUT_DEFAULT);
> +	c->udp.timeout = v;
> +
> +	v = read_file_integer(UDP_TIMEOUT_STREAM, UDP_TIMEOUT_STREAM_DEFAULT);
> +	c->udp.stream_timeout = v;
> +
> +	debug("Using UDP timeout parameters, timeout: %d, stream_timeout: %d",
> +	      c->udp.timeout, c->udp.stream_timeout);
> +}
> +
>  /**
>   * udp_init() - Initialise per-socket data, and sockets in namespace
>   * @c:		Execution context
> @@ -1189,6 +1218,8 @@ int udp_init(struct ctx *c)
>  {
>  	ASSERT(!c->no_udp);
>  
> +	udp_get_timeout_params(c);
> +
>  	udp_iov_init(c);
>  
>  	if (fwd_listen_sync(c, &c->udp.fwd_in, PIF_HOST, IPPROTO_UDP) < 0)
> diff --git a/udp.h b/udp.h
> index 2b91d72..da9c2df 100644
> --- a/udp.h
> +++ b/udp.h
> @@ -24,11 +24,15 @@ void udp_update_l2_buf(const unsigned char *eth_d);
>   * @fwd_in:		Port forwarding configuration for inbound packets
>   * @fwd_out:		Port forwarding configuration for outbound packets
>   * @timer_run:		Timestamp of most recent timer run
> + * @timeout:		Timeout for unidirectional flows (in s)
> + * @stream_timeout:	Timeout for stream-like flows (in s)
>   */
>  struct udp_ctx {
>  	struct fwd_ports fwd_in;
>  	struct fwd_ports fwd_out;
>  	struct timespec timer_run;
> +	int timeout;
> +	int stream_timeout;
>  };
>  
>  #endif /* UDP_H */
> diff --git a/udp_flow.c b/udp_flow.c
> index 1f5e84e..9b22586 100644
> --- a/udp_flow.c
> +++ b/udp_flow.c
> @@ -17,8 +17,6 @@
>  #include "udp_internal.h"
>  #include "epoll_ctl.h"
>  
> -#define UDP_CONN_TIMEOUT	180 /* s, timeout for ephemeral or local bind */
> -
>  /**
>   * udp_at_sidx() - Get UDP specific flow at given sidx
>   * @sidx:    Flow and side to retrieve
> @@ -152,6 +150,7 @@ static flow_sidx_t udp_flow_new(const struct ctx *c, union flow *flow,
>  	uflow->ts = now->tv_sec;
>  	uflow->s[INISIDE] = uflow->s[TGTSIDE] = -1;
>  	uflow->ttl[INISIDE] = uflow->ttl[TGTSIDE] = 0;
> +	uflow->activity[INISIDE] = uflow->activity[TGTSIDE] = 0;
>  
>  	flow_foreach_sidei(sidei) {
>  		if (pif_is_socket(uflow->f.pif[sidei]))
> @@ -362,7 +361,13 @@ bool udp_flow_defer(const struct ctx *c, struct udp_flow *uflow,
>  bool udp_flow_timer(const struct ctx *c, struct udp_flow *uflow,
>  		    const struct timespec *now)
>  {
> -	if (now->tv_sec - uflow->ts <= UDP_CONN_TIMEOUT)
> +	int timeout = c->udp.timeout;
> +
> +	if (uflow->activity[TGTSIDE] &&
> +	    (uflow->activity[INISIDE] > 1 || uflow->activity[TGTSIDE] > 1))
> +		timeout = c->udp.stream_timeout;
> +
> +	if (now->tv_sec - uflow->ts <= timeout)
>  		return false;
>  
>  	udp_flow_close(c, uflow);
> diff --git a/udp_flow.h b/udp_flow.h
> index 14e0f92..158a0f6 100644
> --- a/udp_flow.h
> +++ b/udp_flow.h
> @@ -16,6 +16,7 @@
>   * @flush1:	@s[1] may have datagrams queued for other flows
>   * @ts:		Activity timestamp
>   * @s:		Socket fd (or -1) for each side of the flow
> + * @activity:	Activity for each side of the flow

It's not clear what the measurement unit is, here. I think we should
also specify that it's packets *coming from* (right?), not going to,
each side of the flow. Maybe:

 * @activity:	Packets seen from each side of the flow, up to UINT8_MAX

...if I got it right?

>   */
>  struct udp_flow {
>  	/* Must be first element */
> @@ -29,8 +30,20 @@ struct udp_flow {
>  
>  	time_t ts;
>  	int s[SIDES];
> +	uint8_t activity[SIDES];
>  };
>  
> +/**
> + * udp_flow_activity() - Track activity of a udp flow

UDP

> + * @uflow:	UDP flow
> + * @sidei:	Side index of the flow

Maybe we can specify here:

	(INISIDE or TGTSIDE)

> + */
> +static inline void udp_flow_activity(struct udp_flow *uflow, unsigned int sidei)
> +{
> +	if (uflow->activity[sidei] < UINT8_MAX)
> +		uflow->activity[sidei]++;
> +}
> +
>  struct udp_flow *udp_at_sidx(flow_sidx_t sidx);
>  flow_sidx_t udp_flow_from_sock(const struct ctx *c, uint8_t pif,
>  			       const union inany_addr *dst, in_port_t port,

Everything else looks good to me! (But again, I haven't tested this
yet).

-- 
Stefano


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-12  8:04 [PATCH] udp: Split activity timeouts for UDP flows Yumei Huang
  2026-02-12  8:59 ` Stefano Brivio
@ 2026-02-12 21:51 ` Stefano Brivio
       [not found]   ` <CANsz47mGXDgJSKpLqFiW_n5bXW13ZiayC_xhBEEGeBJTZwN5Xw@mail.gmail.com>
  1 sibling, 1 reply; 11+ messages in thread
From: Stefano Brivio @ 2026-02-12 21:51 UTC (permalink / raw)
  To: Yumei Huang; +Cc: passt-dev, david

Oops, I missed one point at a first review, and also during a quick
test.

I just tried outbound DNS queries in pasta with single responses, not
inbound traffic or passt in vhost-user mode. Then I realised
that:

On Thu, 12 Feb 2026 16:04:14 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> [...]
> @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c, union epoll_ref ref,
>  
>  		flow_trace(uflow, "Received data on reply socket");
>  		uflow->ts = now->tv_sec;
> +		udp_flow_activity(uflow, !tosidx.sidei);

...this only covers three of the four paths we need to act upon:

1. inbound datagrams received on the reply socket via
   udp_buf_sock_to_tap(), called from here

2. inbound datagrams received on the reply socket in passt's vhost-user
   mode, that's udp_vu_sock_recv(), also called from here

3. "spliced" sockets (that's not really the case for UDP, we can't call
   splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
   UDP traffic, handled by udp_sock_to_sock(), called from here as well

but not:

4. outbound, non-spliced datagrams from container/guest: that's
   udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
   udp_flow_from_tap() in udp_flow.c.

   I guess we want to take care of this directly from udp_flow_from_tap(),
   for consistency, because that's also where we update the timestamp
   value:

	sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
	if ((uflow = udp_at_sidx(sidx))) {
		uflow->ts = now->tv_sec;

		^^^ here

		return flow_sidx_opposite(sidx);
	}

I haven't really tested this side of it but it should be fairly easy
with socat and a UDP "server" inside pasta or a guest.

Another thing I noticed later:

> [...]
>
> diff --git a/udp_flow.h b/udp_flow.h
> index 14e0f92..158a0f6 100644
> --- a/udp_flow.h
> +++ b/udp_flow.h
> @@ -16,6 +16,7 @@
>   * @flush1:	@s[1] may have datagrams queued for other flows
>   * @ts:		Activity timestamp
>   * @s:		Socket fd (or -1) for each side of the flow
> + * @activity:	Activity for each side of the flow
>   */
>  struct udp_flow {
>  	/* Must be first element */
> @@ -29,8 +30,20 @@ struct udp_flow {
>  
>  	time_t ts;
>  	int s[SIDES];
> +	uint8_t activity[SIDES];
>  };
>  
> +/**
> + * udp_flow_activity() - Track activity of a udp flow
> + * @uflow:	UDP flow
> + * @sidei:	Side index of the flow
> + */
> +static inline void udp_flow_activity(struct udp_flow *uflow, unsigned int sidei)
> +{
> +	if (uflow->activity[sidei] < UINT8_MAX)
> +		uflow->activity[sidei]++;
> +}

This is an inline function in a header file for no good reason. It
could be a normal static function in udp.c. See also:

  https://www.kernel.org/doc/html/latest/process/coding-style.html#the-inline-disease

...and yes, it's two lines of code, but there's really no reason to
decide we want to inline this instead of letting the compiler decide.

-- 
Stefano


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
       [not found]   ` <CANsz47mGXDgJSKpLqFiW_n5bXW13ZiayC_xhBEEGeBJTZwN5Xw@mail.gmail.com>
@ 2026-02-13  7:08     ` Stefano Brivio
       [not found]       ` <CANsz47m8BPdUK2N-_Ka5GUHP_USnyHgO01Accktf-wxuX5rxDw@mail.gmail.com>
  0 siblings, 1 reply; 11+ messages in thread
From: Stefano Brivio @ 2026-02-13  7:08 UTC (permalink / raw)
  To: Yumei Huang; +Cc: passt-dev, david

On Fri, 13 Feb 2026 14:45:24 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio <sbrivio@redhat.com> wrote:
> 
> > Oops, I missed one point at a first review, and also during a quick
> > test.
> >
> > I just tried outbound DNS queries in pasta with single responses, not
> > inbound traffic or passt in vhost-user mode. Then I realised
> > that:
> >
> > On Thu, 12 Feb 2026 16:04:14 +0800
> > Yumei Huang <yuhuang@redhat.com> wrote:
> >  
> > > [...]
> > > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c, union  
> > epoll_ref ref,  
> > >
> > >               flow_trace(uflow, "Received data on reply socket");
> > >               uflow->ts = now->tv_sec;
> > > +             udp_flow_activity(uflow, !tosidx.sidei);  
> >
> > ...this only covers three of the four paths we need to act upon:
> >
> > 1. inbound datagrams received on the reply socket via
> >    udp_buf_sock_to_tap(), called from here
> >
> > 2. inbound datagrams received on the reply socket in passt's vhost-user
> >    mode, that's udp_vu_sock_recv(), also called from here
> >
> > 3. "spliced" sockets (that's not really the case for UDP, we can't call
> >    splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
> >    UDP traffic, handled by udp_sock_to_sock(), called from here as well
> >
> > but not:
> >
> > 4. outbound, non-spliced datagrams from container/guest: that's
> >    udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
> >    udp_flow_from_tap() in udp_flow.c.
> >
> >    I guess we want to take care of this directly from udp_flow_from_tap(),
> >    for consistency, because that's also where we update the timestamp
> >    value:
> >
> >         sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
> >         if ((uflow = udp_at_sidx(sidx))) {
> >                 uflow->ts = now->tv_sec;
> >
> >                 ^^^ here
> >
> >                 return flow_sidx_opposite(sidx);
> >         }
> >
> > I haven't really tested this side of it but it should be fairly easy
> > with socat and a UDP "server" inside pasta or a guest.
> 
> Somehow, it worked well in my tests with pasta, it looks like the if
> condition always returns false.

Hmm, weird, it should return false only for the first *inbound* datagram
of a UDP flow.

> But now when I test with passt, it becomes
> an issue and we need to track the activity here as you mentioned.
> 
> Besides, I also noticed we update the timestamp value in
> udp_flow_from_sock() as well. I feel we should call udp_flow_activity()
> there too,  but couldn't come up with a test to prove it.

I haven't really checked, but udp_sock_handler() should anyway be
called for the datagram triggering udp_flow_from_sock(), so I don't
think you need an extra call to udp_flow_activity() there.

But you should check that with a pair of debugging prints, I guess.

> On top of it,  I just found two other issues.
> 1. in udp_flow_new(),  we should initialize uflow->activity[INISIDE] to 1
> instead of 0. Otherwise, we fail to track the first datagram.

Same here, I *thought* that calling udp_flow_activity() from
udp_sock_handler() *and* udp_tap_handler() would anyway account for the
first datagram, but I didn't check.

> 2. I guess we need to add the profs entries (nf_conntrack_udp_timeout
> and nf_conntrack_udp_timeout_stream) to apparmor like the tcp ones in
> https://passt.top/passt/commit/?id=2aa63237109b97a55c85e4c86c72db0d055bfe7a.
> I don't have an environment to test it now. Maybe I can set up a debian vm
> later.

Ah, right, good catch. The rules are quite obvious, so you can just add
them to the patch, and I'll test them later on Debian anyway.

-- 
Stefano


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
       [not found]       ` <CANsz47m8BPdUK2N-_Ka5GUHP_USnyHgO01Accktf-wxuX5rxDw@mail.gmail.com>
@ 2026-02-13  9:12         ` Stefano Brivio
  2026-02-13  9:54           ` Yumei Huang
  0 siblings, 1 reply; 11+ messages in thread
From: Stefano Brivio @ 2026-02-13  9:12 UTC (permalink / raw)
  To: Yumei Huang; +Cc: passt-dev, david

[Side note: can you disable sending HTML emails, otherwise they won't
be archived for the passt-dev list for simplicity / security? Thanks]

On Fri, 13 Feb 2026 15:49:41 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> 
> > On Fri, 13 Feb 2026 14:45:24 +0800
> > Yumei Huang <yuhuang@redhat.com> wrote:
> >  
> > > On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio <sbrivio@redhat.com>  
> > wrote:  
> > >  
> > > > Oops, I missed one point at a first review, and also during a quick
> > > > test.
> > > >
> > > > I just tried outbound DNS queries in pasta with single responses, not
> > > > inbound traffic or passt in vhost-user mode. Then I realised
> > > > that:
> > > >
> > > > On Thu, 12 Feb 2026 16:04:14 +0800
> > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > >  
> > > > > [...]
> > > > > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c,  
> > union  
> > > > epoll_ref ref,  
> > > > >
> > > > >               flow_trace(uflow, "Received data on reply socket");
> > > > >               uflow->ts = now->tv_sec;
> > > > > +             udp_flow_activity(uflow, !tosidx.sidei);  
> > > >
> > > > ...this only covers three of the four paths we need to act upon:
> > > >
> > > > 1. inbound datagrams received on the reply socket via
> > > >    udp_buf_sock_to_tap(), called from here
> > > >
> > > > 2. inbound datagrams received on the reply socket in passt's vhost-user
> > > >    mode, that's udp_vu_sock_recv(), also called from here
> > > >
> > > > 3. "spliced" sockets (that's not really the case for UDP, we can't call
> > > >    splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
> > > >    UDP traffic, handled by udp_sock_to_sock(), called from here as well
> > > >
> > > > but not:
> > > >
> > > > 4. outbound, non-spliced datagrams from container/guest: that's
> > > >    udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
> > > >    udp_flow_from_tap() in udp_flow.c.
> > > >
> > > >    I guess we want to take care of this directly from  
> > udp_flow_from_tap(),  
> > > >    for consistency, because that's also where we update the timestamp
> > > >    value:
> > > >
> > > >         sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
> > > >         if ((uflow = udp_at_sidx(sidx))) {
> > > >                 uflow->ts = now->tv_sec;
> > > >
> > > >                 ^^^ here
> > > >
> > > >                 return flow_sidx_opposite(sidx);
> > > >         }
> > > >
> > > > I haven't really tested this side of it but it should be fairly easy
> > > > with socat and a UDP "server" inside pasta or a guest.  
> > >
> > > Somehow, it worked well in my tests with pasta, it looks like the if
> > > condition always returns false.  
> >
> > Hmm, weird, it should return false only for the first *inbound* datagram
> > of a UDP flow.
> >  
> > > But now when I test with passt, it becomes
> > > an issue and we need to track the activity here as you mentioned.
> > >
> > > Besides, I also noticed we update the timestamp value in
> > > udp_flow_from_sock() as well. I feel we should call udp_flow_activity()
> > > there too,  but couldn't come up with a test to prove it.  
> >
> > I haven't really checked, but udp_sock_handler() should anyway be
> > called for the datagram triggering udp_flow_from_sock(), so I don't
> > think you need an extra call to udp_flow_activity() there.
> >
> > But you should check that with a pair of debugging prints, I guess.  
> 
> Actually I did. udp_sock_handler() is called everytime there is new data
> from the socket.

Okay, so the udp_flow_activity() you already added (at least for the
socket -> tap path) is enough, right...?

> But in my test, udp_flow_from_sock() is only called for
> the first datagram, so the if condition after flow_lookup_sa() always
> returns false, and a new UDP flow is created.

Ah, right! See below.

> Tried either spliced /
> non-spliced, pasta / passt case, no exceptions observed.  I was wondering
> if there is a scenario I'm not aware of.

Yes, I think it's just for one corner case David described in the "Flow
sockets" section of the "Theory of Operation" documentation in udp.c:

 * NOTE: A flow socket can have a bound address overlapping with a listening
 * socket.  That will happen naturally for flows initiated from a socket, but is
 * also possible (though unlikely) for tap initiated flows, depending on the
 * source port.  We assume datagrams for the flow will come to a connect()ed
 * socket in preference to a listening socket.  The sample program
 * doc/platform-requirements/reuseaddr-priority.c documents and tests that
 * assumption.

...if they don't come through the connect()ed socket, we would end up
in that case.

Long story short, we need to update the activity array there as well,
because it could happen. I'm not sure if reuseaddr-priority.c can be
used to test this case together with pasta, I don't think it's really
needed though.

> > > On top of it,  I just found two other issues.
> > > 1. in udp_flow_new(),  we should initialize uflow->activity[INISIDE] to 1
> > > instead of 0. Otherwise, we fail to track the first datagram.  
> >
> > Same here, I *thought* that calling udp_flow_activity() from
> > udp_sock_handler() *and* udp_tap_handler() would anyway account for the
> > first datagram, but I didn't check.
> 
> udp_sock_handler() is only called *after* the flow is created. But only
> when the first datagram comes, we create the flow. Similarly,
> udp_flow_from_tap() (called by udp_tap_handler()) calls udp_flow_new() to
> create a new flow for the first datagram too. That's why we missed the
> first one.

Oh, I see, thanks for the explanation.

-- 
Stefano


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-13  9:12         ` Stefano Brivio
@ 2026-02-13  9:54           ` Yumei Huang
  2026-02-13 10:00             ` Stefano Brivio
  0 siblings, 1 reply; 11+ messages in thread
From: Yumei Huang @ 2026-02-13  9:54 UTC (permalink / raw)
  To: Stefano Brivio; +Cc: passt-dev, david

On Fri, Feb 13, 2026 at 5:13 PM Stefano Brivio <sbrivio@redhat.com> wrote:
>
> [Side note: can you disable sending HTML emails, otherwise they won't
> be archived for the passt-dev list for simplicity / security? Thanks]

Yeah, sorry about that, will keep in mind to enable the plain text mode.
>
> On Fri, 13 Feb 2026 15:49:41 +0800
> Yumei Huang <yuhuang@redhat.com> wrote:
>
> > On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> >
> > > On Fri, 13 Feb 2026 14:45:24 +0800
> > > Yumei Huang <yuhuang@redhat.com> wrote:
> > >
> > > > On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio <sbrivio@redhat.com>
> > > wrote:
> > > >
> > > > > Oops, I missed one point at a first review, and also during a quick
> > > > > test.
> > > > >
> > > > > I just tried outbound DNS queries in pasta with single responses, not
> > > > > inbound traffic or passt in vhost-user mode. Then I realised
> > > > > that:
> > > > >
> > > > > On Thu, 12 Feb 2026 16:04:14 +0800
> > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > >
> > > > > > [...]
> > > > > > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c,
> > > union
> > > > > epoll_ref ref,
> > > > > >
> > > > > >               flow_trace(uflow, "Received data on reply socket");
> > > > > >               uflow->ts = now->tv_sec;
> > > > > > +             udp_flow_activity(uflow, !tosidx.sidei);
> > > > >
> > > > > ...this only covers three of the four paths we need to act upon:
> > > > >
> > > > > 1. inbound datagrams received on the reply socket via
> > > > >    udp_buf_sock_to_tap(), called from here
> > > > >
> > > > > 2. inbound datagrams received on the reply socket in passt's vhost-user
> > > > >    mode, that's udp_vu_sock_recv(), also called from here
> > > > >
> > > > > 3. "spliced" sockets (that's not really the case for UDP, we can't call
> > > > >    splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
> > > > >    UDP traffic, handled by udp_sock_to_sock(), called from here as well
> > > > >
> > > > > but not:
> > > > >
> > > > > 4. outbound, non-spliced datagrams from container/guest: that's
> > > > >    udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
> > > > >    udp_flow_from_tap() in udp_flow.c.
> > > > >
> > > > >    I guess we want to take care of this directly from
> > > udp_flow_from_tap(),
> > > > >    for consistency, because that's also where we update the timestamp
> > > > >    value:
> > > > >
> > > > >         sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
> > > > >         if ((uflow = udp_at_sidx(sidx))) {
> > > > >                 uflow->ts = now->tv_sec;
> > > > >
> > > > >                 ^^^ here
> > > > >
> > > > >                 return flow_sidx_opposite(sidx);
> > > > >         }
> > > > >
> > > > > I haven't really tested this side of it but it should be fairly easy
> > > > > with socat and a UDP "server" inside pasta or a guest.
> > > >
> > > > Somehow, it worked well in my tests with pasta, it looks like the if
> > > > condition always returns false.
> > >
> > > Hmm, weird, it should return false only for the first *inbound* datagram
> > > of a UDP flow.
> > >
> > > > But now when I test with passt, it becomes
> > > > an issue and we need to track the activity here as you mentioned.
> > > >
> > > > Besides, I also noticed we update the timestamp value in
> > > > udp_flow_from_sock() as well. I feel we should call udp_flow_activity()
> > > > there too,  but couldn't come up with a test to prove it.
> > >
> > > I haven't really checked, but udp_sock_handler() should anyway be
> > > called for the datagram triggering udp_flow_from_sock(), so I don't
> > > think you need an extra call to udp_flow_activity() there.
> > >
> > > But you should check that with a pair of debugging prints, I guess.
> >
> > Actually I did. udp_sock_handler() is called everytime there is new data
> > from the socket.
>
> Okay, so the udp_flow_activity() you already added (at least for the
> socket -> tap path) is enough, right...?

Yes, it's enough for the socket->tap path.
>
> > But in my test, udp_flow_from_sock() is only called for
> > the first datagram, so the if condition after flow_lookup_sa() always
> > returns false, and a new UDP flow is created.
>
> Ah, right! See below.
>
> > Tried either spliced /
> > non-spliced, pasta / passt case, no exceptions observed.  I was wondering
> > if there is a scenario I'm not aware of.
>
> Yes, I think it's just for one corner case David described in the "Flow
> sockets" section of the "Theory of Operation" documentation in udp.c:
>
>  * NOTE: A flow socket can have a bound address overlapping with a listening
>  * socket.  That will happen naturally for flows initiated from a socket, but is
>  * also possible (though unlikely) for tap initiated flows, depending on the
>  * source port.  We assume datagrams for the flow will come to a connect()ed
>  * socket in preference to a listening socket.  The sample program
>  * doc/platform-requirements/reuseaddr-priority.c documents and tests that
>  * assumption.
>
> ...if they don't come through the connect()ed socket, we would end up
> in that case.
>
> Long story short, we need to update the activity array there as well,
> because it could happen. I'm not sure if reuseaddr-priority.c can be
> used to test this case together with pasta, I don't think it's really
> needed though.

Thanks, I will add that and send v2 soon.
>
> > > > On top of it,  I just found two other issues.
> > > > 1. in udp_flow_new(),  we should initialize uflow->activity[INISIDE] to 1
> > > > instead of 0. Otherwise, we fail to track the first datagram.
> > >
> > > Same here, I *thought* that calling udp_flow_activity() from
> > > udp_sock_handler() *and* udp_tap_handler() would anyway account for the
> > > first datagram, but I didn't check.
> >
> > udp_sock_handler() is only called *after* the flow is created. But only
> > when the first datagram comes, we create the flow. Similarly,
> > udp_flow_from_tap() (called by udp_tap_handler()) calls udp_flow_new() to
> > create a new flow for the first datagram too. That's why we missed the
> > first one.
>
> Oh, I see, thanks for the explanation.
>
> --
> Stefano
>


-- 
Thanks,

Yumei Huang


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-13  9:54           ` Yumei Huang
@ 2026-02-13 10:00             ` Stefano Brivio
  2026-02-13 10:04               ` Yumei Huang
  0 siblings, 1 reply; 11+ messages in thread
From: Stefano Brivio @ 2026-02-13 10:00 UTC (permalink / raw)
  To: Yumei Huang; +Cc: passt-dev, david

On Fri, 13 Feb 2026 17:54:56 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> On Fri, Feb 13, 2026 at 5:13 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> >
> > [Side note: can you disable sending HTML emails, otherwise they won't
> > be archived for the passt-dev list for simplicity / security? Thanks]  
> 
> Yeah, sorry about that, will keep in mind to enable the plain text mode.
> >
> > On Fri, 13 Feb 2026 15:49:41 +0800
> > Yumei Huang <yuhuang@redhat.com> wrote:
> >  
> > > On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> > >  
> > > > On Fri, 13 Feb 2026 14:45:24 +0800
> > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > >  
> > > > > On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio <sbrivio@redhat.com>  
> > > > wrote:  
> > > > >  
> > > > > > Oops, I missed one point at a first review, and also during a quick
> > > > > > test.
> > > > > >
> > > > > > I just tried outbound DNS queries in pasta with single responses, not
> > > > > > inbound traffic or passt in vhost-user mode. Then I realised
> > > > > > that:
> > > > > >
> > > > > > On Thu, 12 Feb 2026 16:04:14 +0800
> > > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > > >  
> > > > > > > [...]
> > > > > > > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c,  
> > > > union  
> > > > > > epoll_ref ref,  
> > > > > > >
> > > > > > >               flow_trace(uflow, "Received data on reply socket");
> > > > > > >               uflow->ts = now->tv_sec;
> > > > > > > +             udp_flow_activity(uflow, !tosidx.sidei);  
> > > > > >
> > > > > > ...this only covers three of the four paths we need to act upon:
> > > > > >
> > > > > > 1. inbound datagrams received on the reply socket via
> > > > > >    udp_buf_sock_to_tap(), called from here
> > > > > >
> > > > > > 2. inbound datagrams received on the reply socket in passt's vhost-user
> > > > > >    mode, that's udp_vu_sock_recv(), also called from here
> > > > > >
> > > > > > 3. "spliced" sockets (that's not really the case for UDP, we can't call
> > > > > >    splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
> > > > > >    UDP traffic, handled by udp_sock_to_sock(), called from here as well
> > > > > >
> > > > > > but not:
> > > > > >
> > > > > > 4. outbound, non-spliced datagrams from container/guest: that's
> > > > > >    udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
> > > > > >    udp_flow_from_tap() in udp_flow.c.
> > > > > >
> > > > > >    I guess we want to take care of this directly from  
> > > > udp_flow_from_tap(),  
> > > > > >    for consistency, because that's also where we update the timestamp
> > > > > >    value:
> > > > > >
> > > > > >         sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
> > > > > >         if ((uflow = udp_at_sidx(sidx))) {
> > > > > >                 uflow->ts = now->tv_sec;
> > > > > >
> > > > > >                 ^^^ here
> > > > > >
> > > > > >                 return flow_sidx_opposite(sidx);
> > > > > >         }
> > > > > >
> > > > > > I haven't really tested this side of it but it should be fairly easy
> > > > > > with socat and a UDP "server" inside pasta or a guest.  
> > > > >
> > > > > Somehow, it worked well in my tests with pasta, it looks like the if
> > > > > condition always returns false.  
> > > >
> > > > Hmm, weird, it should return false only for the first *inbound* datagram
> > > > of a UDP flow.
> > > >  
> > > > > But now when I test with passt, it becomes
> > > > > an issue and we need to track the activity here as you mentioned.
> > > > >
> > > > > Besides, I also noticed we update the timestamp value in
> > > > > udp_flow_from_sock() as well. I feel we should call udp_flow_activity()
> > > > > there too,  but couldn't come up with a test to prove it.  
> > > >
> > > > I haven't really checked, but udp_sock_handler() should anyway be
> > > > called for the datagram triggering udp_flow_from_sock(), so I don't
> > > > think you need an extra call to udp_flow_activity() there.
> > > >
> > > > But you should check that with a pair of debugging prints, I guess.  
> > >
> > > Actually I did. udp_sock_handler() is called everytime there is new data
> > > from the socket.  
> >
> > Okay, so the udp_flow_activity() you already added (at least for the
> > socket -> tap path) is enough, right...?  
> 
> Yes, it's enough for the socket->tap path.
> >  
> > > But in my test, udp_flow_from_sock() is only called for
> > > the first datagram, so the if condition after flow_lookup_sa() always
> > > returns false, and a new UDP flow is created.  
> >
> > Ah, right! See below.
> >  
> > > Tried either spliced /
> > > non-spliced, pasta / passt case, no exceptions observed.  I was wondering
> > > if there is a scenario I'm not aware of.  
> >
> > Yes, I think it's just for one corner case David described in the "Flow
> > sockets" section of the "Theory of Operation" documentation in udp.c:
> >
> >  * NOTE: A flow socket can have a bound address overlapping with a listening
> >  * socket.  That will happen naturally for flows initiated from a socket, but is
> >  * also possible (though unlikely) for tap initiated flows, depending on the
> >  * source port.  We assume datagrams for the flow will come to a connect()ed
> >  * socket in preference to a listening socket.  The sample program
> >  * doc/platform-requirements/reuseaddr-priority.c documents and tests that
> >  * assumption.
> >
> > ...if they don't come through the connect()ed socket, we would end up
> > in that case.
> >
> > Long story short, we need to update the activity array there as well,
> > because it could happen. I'm not sure if reuseaddr-priority.c can be
> > used to test this case together with pasta, I don't think it's really
> > needed though.  
> 
> Thanks, I will add that and send v2 soon.

Actually, if we always want to update the 'activity' array when we
update the timestamp, maybe you could add a helper that does both,
update 'ts' and 'activity'.

It could still be called ...activity() because the timestamp is also an
activity timestamp.

I haven't checked all the possible paths though, I'm not sure if it's
the right thing to do.

-- 
Stefano


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-13 10:00             ` Stefano Brivio
@ 2026-02-13 10:04               ` Yumei Huang
  2026-02-13 10:17                 ` Stefano Brivio
  0 siblings, 1 reply; 11+ messages in thread
From: Yumei Huang @ 2026-02-13 10:04 UTC (permalink / raw)
  To: Stefano Brivio; +Cc: passt-dev, david

On Fri, Feb 13, 2026 at 6:00 PM Stefano Brivio <sbrivio@redhat.com> wrote:
>
> On Fri, 13 Feb 2026 17:54:56 +0800
> Yumei Huang <yuhuang@redhat.com> wrote:
>
> > On Fri, Feb 13, 2026 at 5:13 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> > >
> > > [Side note: can you disable sending HTML emails, otherwise they won't
> > > be archived for the passt-dev list for simplicity / security? Thanks]
> >
> > Yeah, sorry about that, will keep in mind to enable the plain text mode.
> > >
> > > On Fri, 13 Feb 2026 15:49:41 +0800
> > > Yumei Huang <yuhuang@redhat.com> wrote:
> > >
> > > > On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> > > >
> > > > > On Fri, 13 Feb 2026 14:45:24 +0800
> > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > >
> > > > > > On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio <sbrivio@redhat.com>
> > > > > wrote:
> > > > > >
> > > > > > > Oops, I missed one point at a first review, and also during a quick
> > > > > > > test.
> > > > > > >
> > > > > > > I just tried outbound DNS queries in pasta with single responses, not
> > > > > > > inbound traffic or passt in vhost-user mode. Then I realised
> > > > > > > that:
> > > > > > >
> > > > > > > On Thu, 12 Feb 2026 16:04:14 +0800
> > > > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > > > >
> > > > > > > > [...]
> > > > > > > > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c,
> > > > > union
> > > > > > > epoll_ref ref,
> > > > > > > >
> > > > > > > >               flow_trace(uflow, "Received data on reply socket");
> > > > > > > >               uflow->ts = now->tv_sec;
> > > > > > > > +             udp_flow_activity(uflow, !tosidx.sidei);
> > > > > > >
> > > > > > > ...this only covers three of the four paths we need to act upon:
> > > > > > >
> > > > > > > 1. inbound datagrams received on the reply socket via
> > > > > > >    udp_buf_sock_to_tap(), called from here
> > > > > > >
> > > > > > > 2. inbound datagrams received on the reply socket in passt's vhost-user
> > > > > > >    mode, that's udp_vu_sock_recv(), also called from here
> > > > > > >
> > > > > > > 3. "spliced" sockets (that's not really the case for UDP, we can't call
> > > > > > >    splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
> > > > > > >    UDP traffic, handled by udp_sock_to_sock(), called from here as well
> > > > > > >
> > > > > > > but not:
> > > > > > >
> > > > > > > 4. outbound, non-spliced datagrams from container/guest: that's
> > > > > > >    udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
> > > > > > >    udp_flow_from_tap() in udp_flow.c.
> > > > > > >
> > > > > > >    I guess we want to take care of this directly from
> > > > > udp_flow_from_tap(),
> > > > > > >    for consistency, because that's also where we update the timestamp
> > > > > > >    value:
> > > > > > >
> > > > > > >         sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
> > > > > > >         if ((uflow = udp_at_sidx(sidx))) {
> > > > > > >                 uflow->ts = now->tv_sec;
> > > > > > >
> > > > > > >                 ^^^ here
> > > > > > >
> > > > > > >                 return flow_sidx_opposite(sidx);
> > > > > > >         }
> > > > > > >
> > > > > > > I haven't really tested this side of it but it should be fairly easy
> > > > > > > with socat and a UDP "server" inside pasta or a guest.
> > > > > >
> > > > > > Somehow, it worked well in my tests with pasta, it looks like the if
> > > > > > condition always returns false.
> > > > >
> > > > > Hmm, weird, it should return false only for the first *inbound* datagram
> > > > > of a UDP flow.
> > > > >
> > > > > > But now when I test with passt, it becomes
> > > > > > an issue and we need to track the activity here as you mentioned.
> > > > > >
> > > > > > Besides, I also noticed we update the timestamp value in
> > > > > > udp_flow_from_sock() as well. I feel we should call udp_flow_activity()
> > > > > > there too,  but couldn't come up with a test to prove it.
> > > > >
> > > > > I haven't really checked, but udp_sock_handler() should anyway be
> > > > > called for the datagram triggering udp_flow_from_sock(), so I don't
> > > > > think you need an extra call to udp_flow_activity() there.
> > > > >
> > > > > But you should check that with a pair of debugging prints, I guess.
> > > >
> > > > Actually I did. udp_sock_handler() is called everytime there is new data
> > > > from the socket.
> > >
> > > Okay, so the udp_flow_activity() you already added (at least for the
> > > socket -> tap path) is enough, right...?
> >
> > Yes, it's enough for the socket->tap path.
> > >
> > > > But in my test, udp_flow_from_sock() is only called for
> > > > the first datagram, so the if condition after flow_lookup_sa() always
> > > > returns false, and a new UDP flow is created.
> > >
> > > Ah, right! See below.
> > >
> > > > Tried either spliced /
> > > > non-spliced, pasta / passt case, no exceptions observed.  I was wondering
> > > > if there is a scenario I'm not aware of.
> > >
> > > Yes, I think it's just for one corner case David described in the "Flow
> > > sockets" section of the "Theory of Operation" documentation in udp.c:
> > >
> > >  * NOTE: A flow socket can have a bound address overlapping with a listening
> > >  * socket.  That will happen naturally for flows initiated from a socket, but is
> > >  * also possible (though unlikely) for tap initiated flows, depending on the
> > >  * source port.  We assume datagrams for the flow will come to a connect()ed
> > >  * socket in preference to a listening socket.  The sample program
> > >  * doc/platform-requirements/reuseaddr-priority.c documents and tests that
> > >  * assumption.
> > >
> > > ...if they don't come through the connect()ed socket, we would end up
> > > in that case.
> > >
> > > Long story short, we need to update the activity array there as well,
> > > because it could happen. I'm not sure if reuseaddr-priority.c can be
> > > used to test this case together with pasta, I don't think it's really
> > > needed though.
> >
> > Thanks, I will add that and send v2 soon.
>
> Actually, if we always want to update the 'activity' array when we
> update the timestamp, maybe you could add a helper that does both,
> update 'ts' and 'activity'.
>
> It could still be called ...activity() because the timestamp is also an
> activity timestamp.

Good idea.  Guess I sent v2 too soon.
>
> I haven't checked all the possible paths though, I'm not sure if it's
> the right thing to do.

I will check tmr. If no other changes are needed, I will sent v3 then.
>
> --
> Stefano
>


-- 
Thanks,

Yumei Huang


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-13 10:04               ` Yumei Huang
@ 2026-02-13 10:17                 ` Stefano Brivio
  2026-02-14  7:20                   ` Yumei Huang
  0 siblings, 1 reply; 11+ messages in thread
From: Stefano Brivio @ 2026-02-13 10:17 UTC (permalink / raw)
  To: Yumei Huang; +Cc: passt-dev, david

On Fri, 13 Feb 2026 18:04:59 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> On Fri, Feb 13, 2026 at 6:00 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> >
> > On Fri, 13 Feb 2026 17:54:56 +0800
> > Yumei Huang <yuhuang@redhat.com> wrote:
> >  
> > > On Fri, Feb 13, 2026 at 5:13 PM Stefano Brivio <sbrivio@redhat.com> wrote:  
> > > >
> > > > [Side note: can you disable sending HTML emails, otherwise they won't
> > > > be archived for the passt-dev list for simplicity / security? Thanks]  
> > >
> > > Yeah, sorry about that, will keep in mind to enable the plain text mode.  
> > > >
> > > > On Fri, 13 Feb 2026 15:49:41 +0800
> > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > >  
> > > > > On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> > > > >  
> > > > > > On Fri, 13 Feb 2026 14:45:24 +0800
> > > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > > >  
> > > > > > > On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio <sbrivio@redhat.com>  
> > > > > > wrote:  
> > > > > > >  
> > > > > > > > Oops, I missed one point at a first review, and also during a quick
> > > > > > > > test.
> > > > > > > >
> > > > > > > > I just tried outbound DNS queries in pasta with single responses, not
> > > > > > > > inbound traffic or passt in vhost-user mode. Then I realised
> > > > > > > > that:
> > > > > > > >
> > > > > > > > On Thu, 12 Feb 2026 16:04:14 +0800
> > > > > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > > > > >  
> > > > > > > > > [...]
> > > > > > > > > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c,  
> > > > > > union  
> > > > > > > > epoll_ref ref,  
> > > > > > > > >
> > > > > > > > >               flow_trace(uflow, "Received data on reply socket");
> > > > > > > > >               uflow->ts = now->tv_sec;
> > > > > > > > > +             udp_flow_activity(uflow, !tosidx.sidei);  
> > > > > > > >
> > > > > > > > ...this only covers three of the four paths we need to act upon:
> > > > > > > >
> > > > > > > > 1. inbound datagrams received on the reply socket via
> > > > > > > >    udp_buf_sock_to_tap(), called from here
> > > > > > > >
> > > > > > > > 2. inbound datagrams received on the reply socket in passt's vhost-user
> > > > > > > >    mode, that's udp_vu_sock_recv(), also called from here
> > > > > > > >
> > > > > > > > 3. "spliced" sockets (that's not really the case for UDP, we can't call
> > > > > > > >    splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
> > > > > > > >    UDP traffic, handled by udp_sock_to_sock(), called from here as well
> > > > > > > >
> > > > > > > > but not:
> > > > > > > >
> > > > > > > > 4. outbound, non-spliced datagrams from container/guest: that's
> > > > > > > >    udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
> > > > > > > >    udp_flow_from_tap() in udp_flow.c.
> > > > > > > >
> > > > > > > >    I guess we want to take care of this directly from  
> > > > > > udp_flow_from_tap(),  
> > > > > > > >    for consistency, because that's also where we update the timestamp
> > > > > > > >    value:
> > > > > > > >
> > > > > > > >         sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
> > > > > > > >         if ((uflow = udp_at_sidx(sidx))) {
> > > > > > > >                 uflow->ts = now->tv_sec;
> > > > > > > >
> > > > > > > >                 ^^^ here
> > > > > > > >
> > > > > > > >                 return flow_sidx_opposite(sidx);
> > > > > > > >         }
> > > > > > > >
> > > > > > > > I haven't really tested this side of it but it should be fairly easy
> > > > > > > > with socat and a UDP "server" inside pasta or a guest.  
> > > > > > >
> > > > > > > Somehow, it worked well in my tests with pasta, it looks like the if
> > > > > > > condition always returns false.  
> > > > > >
> > > > > > Hmm, weird, it should return false only for the first *inbound* datagram
> > > > > > of a UDP flow.
> > > > > >  
> > > > > > > But now when I test with passt, it becomes
> > > > > > > an issue and we need to track the activity here as you mentioned.
> > > > > > >
> > > > > > > Besides, I also noticed we update the timestamp value in
> > > > > > > udp_flow_from_sock() as well. I feel we should call udp_flow_activity()
> > > > > > > there too,  but couldn't come up with a test to prove it.  
> > > > > >
> > > > > > I haven't really checked, but udp_sock_handler() should anyway be
> > > > > > called for the datagram triggering udp_flow_from_sock(), so I don't
> > > > > > think you need an extra call to udp_flow_activity() there.
> > > > > >
> > > > > > But you should check that with a pair of debugging prints, I guess.  
> > > > >
> > > > > Actually I did. udp_sock_handler() is called everytime there is new data
> > > > > from the socket.  
> > > >
> > > > Okay, so the udp_flow_activity() you already added (at least for the
> > > > socket -> tap path) is enough, right...?  
> > >
> > > Yes, it's enough for the socket->tap path.  
> > > >  
> > > > > But in my test, udp_flow_from_sock() is only called for
> > > > > the first datagram, so the if condition after flow_lookup_sa() always
> > > > > returns false, and a new UDP flow is created.  
> > > >
> > > > Ah, right! See below.
> > > >  
> > > > > Tried either spliced /
> > > > > non-spliced, pasta / passt case, no exceptions observed.  I was wondering
> > > > > if there is a scenario I'm not aware of.  
> > > >
> > > > Yes, I think it's just for one corner case David described in the "Flow
> > > > sockets" section of the "Theory of Operation" documentation in udp.c:
> > > >
> > > >  * NOTE: A flow socket can have a bound address overlapping with a listening
> > > >  * socket.  That will happen naturally for flows initiated from a socket, but is
> > > >  * also possible (though unlikely) for tap initiated flows, depending on the
> > > >  * source port.  We assume datagrams for the flow will come to a connect()ed
> > > >  * socket in preference to a listening socket.  The sample program
> > > >  * doc/platform-requirements/reuseaddr-priority.c documents and tests that
> > > >  * assumption.
> > > >
> > > > ...if they don't come through the connect()ed socket, we would end up
> > > > in that case.
> > > >
> > > > Long story short, we need to update the activity array there as well,
> > > > because it could happen. I'm not sure if reuseaddr-priority.c can be
> > > > used to test this case together with pasta, I don't think it's really
> > > > needed though.  
> > >
> > > Thanks, I will add that and send v2 soon.  
> >
> > Actually, if we always want to update the 'activity' array when we
> > update the timestamp, maybe you could add a helper that does both,
> > update 'ts' and 'activity'.
> >
> > It could still be called ...activity() because the timestamp is also an
> > activity timestamp.  
> 
> Good idea.  Guess I sent v2 too soon.
> >
> > I haven't checked all the possible paths though, I'm not sure if it's
> > the right thing to do.  
> 
> I will check tmr. If no other changes are needed, I will sent v3 then.

Okay, thanks!

-- 
Stefano


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-13 10:17                 ` Stefano Brivio
@ 2026-02-14  7:20                   ` Yumei Huang
  2026-02-14  9:15                     ` Stefano Brivio
  0 siblings, 1 reply; 11+ messages in thread
From: Yumei Huang @ 2026-02-14  7:20 UTC (permalink / raw)
  To: Stefano Brivio; +Cc: passt-dev, david

On Fri, Feb 13, 2026 at 6:17 PM Stefano Brivio <sbrivio@redhat.com> wrote:
>
> On Fri, 13 Feb 2026 18:04:59 +0800
> Yumei Huang <yuhuang@redhat.com> wrote:
>
> > On Fri, Feb 13, 2026 at 6:00 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> > >
> > > On Fri, 13 Feb 2026 17:54:56 +0800
> > > Yumei Huang <yuhuang@redhat.com> wrote:
> > >
> > > > On Fri, Feb 13, 2026 at 5:13 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> > > > >
> > > > > [Side note: can you disable sending HTML emails, otherwise they won't
> > > > > be archived for the passt-dev list for simplicity / security? Thanks]
> > > >
> > > > Yeah, sorry about that, will keep in mind to enable the plain text mode.
> > > > >
> > > > > On Fri, 13 Feb 2026 15:49:41 +0800
> > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > >
> > > > > > On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> > > > > >
> > > > > > > On Fri, 13 Feb 2026 14:45:24 +0800
> > > > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > > > >
> > > > > > > > On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio <sbrivio@redhat.com>
> > > > > > > wrote:
> > > > > > > >
> > > > > > > > > Oops, I missed one point at a first review, and also during a quick
> > > > > > > > > test.
> > > > > > > > >
> > > > > > > > > I just tried outbound DNS queries in pasta with single responses, not
> > > > > > > > > inbound traffic or passt in vhost-user mode. Then I realised
> > > > > > > > > that:
> > > > > > > > >
> > > > > > > > > On Thu, 12 Feb 2026 16:04:14 +0800
> > > > > > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > > > > > >
> > > > > > > > > > [...]
> > > > > > > > > > @@ -954,6 +964,7 @@ void udp_sock_handler(const struct ctx *c,
> > > > > > > union
> > > > > > > > > epoll_ref ref,
> > > > > > > > > >
> > > > > > > > > >               flow_trace(uflow, "Received data on reply socket");
> > > > > > > > > >               uflow->ts = now->tv_sec;
> > > > > > > > > > +             udp_flow_activity(uflow, !tosidx.sidei);
> > > > > > > > >
> > > > > > > > > ...this only covers three of the four paths we need to act upon:
> > > > > > > > >
> > > > > > > > > 1. inbound datagrams received on the reply socket via
> > > > > > > > >    udp_buf_sock_to_tap(), called from here
> > > > > > > > >
> > > > > > > > > 2. inbound datagrams received on the reply socket in passt's vhost-user
> > > > > > > > >    mode, that's udp_vu_sock_recv(), also called from here
> > > > > > > > >
> > > > > > > > > 3. "spliced" sockets (that's not really the case for UDP, we can't call
> > > > > > > > >    splice(), but a pair of recvmmsg() / sendmmsg()), that is, loopback
> > > > > > > > >    UDP traffic, handled by udp_sock_to_sock(), called from here as well
> > > > > > > > >
> > > > > > > > > but not:
> > > > > > > > >
> > > > > > > > > 4. outbound, non-spliced datagrams from container/guest: that's
> > > > > > > > >    udp_tap_handler(), in both vhost-user and non-vhost-user cases, or
> > > > > > > > >    udp_flow_from_tap() in udp_flow.c.
> > > > > > > > >
> > > > > > > > >    I guess we want to take care of this directly from
> > > > > > > udp_flow_from_tap(),
> > > > > > > > >    for consistency, because that's also where we update the timestamp
> > > > > > > > >    value:
> > > > > > > > >
> > > > > > > > >         sidx = flow_lookup_sa(c, IPPROTO_UDP, pif, s_in, dst, port);
> > > > > > > > >         if ((uflow = udp_at_sidx(sidx))) {
> > > > > > > > >                 uflow->ts = now->tv_sec;
> > > > > > > > >
> > > > > > > > >                 ^^^ here
> > > > > > > > >
> > > > > > > > >                 return flow_sidx_opposite(sidx);
> > > > > > > > >         }
> > > > > > > > >
> > > > > > > > > I haven't really tested this side of it but it should be fairly easy
> > > > > > > > > with socat and a UDP "server" inside pasta or a guest.
> > > > > > > >
> > > > > > > > Somehow, it worked well in my tests with pasta, it looks like the if
> > > > > > > > condition always returns false.
> > > > > > >
> > > > > > > Hmm, weird, it should return false only for the first *inbound* datagram
> > > > > > > of a UDP flow.
> > > > > > >
> > > > > > > > But now when I test with passt, it becomes
> > > > > > > > an issue and we need to track the activity here as you mentioned.
> > > > > > > >
> > > > > > > > Besides, I also noticed we update the timestamp value in
> > > > > > > > udp_flow_from_sock() as well. I feel we should call udp_flow_activity()
> > > > > > > > there too,  but couldn't come up with a test to prove it.
> > > > > > >
> > > > > > > I haven't really checked, but udp_sock_handler() should anyway be
> > > > > > > called for the datagram triggering udp_flow_from_sock(), so I don't
> > > > > > > think you need an extra call to udp_flow_activity() there.
> > > > > > >
> > > > > > > But you should check that with a pair of debugging prints, I guess.
> > > > > >
> > > > > > Actually I did. udp_sock_handler() is called everytime there is new data
> > > > > > from the socket.
> > > > >
> > > > > Okay, so the udp_flow_activity() you already added (at least for the
> > > > > socket -> tap path) is enough, right...?
> > > >
> > > > Yes, it's enough for the socket->tap path.
> > > > >
> > > > > > But in my test, udp_flow_from_sock() is only called for
> > > > > > the first datagram, so the if condition after flow_lookup_sa() always
> > > > > > returns false, and a new UDP flow is created.
> > > > >
> > > > > Ah, right! See below.
> > > > >
> > > > > > Tried either spliced /
> > > > > > non-spliced, pasta / passt case, no exceptions observed.  I was wondering
> > > > > > if there is a scenario I'm not aware of.
> > > > >
> > > > > Yes, I think it's just for one corner case David described in the "Flow
> > > > > sockets" section of the "Theory of Operation" documentation in udp.c:

Just have another look, instead of this case, I feel it's more like
the one described in
https://passt.top/passt/commit/udp_flow.c?id=9725e79888374a4e4060a2d798f3407c0006cc8a,
which is packets arriving between bind() and connect(), and
udp_sock_fwd() / udp_flow_from_sock() is called again to forward the
packets. In this case, we should count the activity. The timestamp
might not be so accurate, but should be very close. So I'm keeping it.
> > > > >
> > > > >  * NOTE: A flow socket can have a bound address overlapping with a listening
> > > > >  * socket.  That will happen naturally for flows initiated from a socket, but is
> > > > >  * also possible (though unlikely) for tap initiated flows, depending on the
> > > > >  * source port.  We assume datagrams for the flow will come to a connect()ed
> > > > >  * socket in preference to a listening socket.  The sample program
> > > > >  * doc/platform-requirements/reuseaddr-priority.c documents and tests that
> > > > >  * assumption.
> > > > >
> > > > > ...if they don't come through the connect()ed socket, we would end up
> > > > > in that case.
> > > > >
> > > > > Long story short, we need to update the activity array there as well,
> > > > > because it could happen. I'm not sure if reuseaddr-priority.c can be
> > > > > used to test this case together with pasta, I don't think it's really
> > > > > needed though.
> > > >
> > > > Thanks, I will add that and send v2 soon.
> > >
> > > Actually, if we always want to update the 'activity' array when we
> > > update the timestamp, maybe you could add a helper that does both,
> > > update 'ts' and 'activity'.
> > >
> > > It could still be called ...activity() because the timestamp is also an
> > > activity timestamp.
> >
> > Good idea.  Guess I sent v2 too soon.
> > >
> > > I haven't checked all the possible paths though, I'm not sure if it's
> > > the right thing to do.
> >
> > I will check tmr. If no other changes are needed, I will sent v3 then.
>
> Okay, thanks!
>
> --
> Stefano
>


-- 
Thanks,

Yumei Huang


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] udp: Split activity timeouts for UDP flows
  2026-02-14  7:20                   ` Yumei Huang
@ 2026-02-14  9:15                     ` Stefano Brivio
  0 siblings, 0 replies; 11+ messages in thread
From: Stefano Brivio @ 2026-02-14  9:15 UTC (permalink / raw)
  To: Yumei Huang; +Cc: passt-dev, david

On Sat, 14 Feb 2026 15:20:26 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> On Fri, Feb 13, 2026 at 6:17 PM Stefano Brivio <sbrivio@redhat.com> wrote:
> >
> > On Fri, 13 Feb 2026 18:04:59 +0800
> > Yumei Huang <yuhuang@redhat.com> wrote:
> >  
> > > On Fri, Feb 13, 2026 at 6:00 PM Stefano Brivio <sbrivio@redhat.com> wrote:  
> > > >
> > > > On Fri, 13 Feb 2026 17:54:56 +0800
> > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > >  
> > > > > On Fri, Feb 13, 2026 at 5:13 PM Stefano Brivio <sbrivio@redhat.com> wrote:  
> > > > > >
> > > > > > On Fri, 13 Feb 2026 15:49:41 +0800
> > > > > > Yumei Huang <yuhuang@redhat.com> wrote:
> > > > > >  
> > > > > > > But in my test, udp_flow_from_sock() is only called for
> > > > > > > the first datagram, so the if condition after flow_lookup_sa() always
> > > > > > > returns false, and a new UDP flow is created.  
> > > > > >
> > > > > > Ah, right! See below.
> > > > > >  
> > > > > > > Tried either spliced /
> > > > > > > non-spliced, pasta / passt case, no exceptions observed.  I was wondering
> > > > > > > if there is a scenario I'm not aware of.  
> > > > > >
> > > > > > Yes, I think it's just for one corner case David described in the "Flow
> > > > > > sockets" section of the "Theory of Operation" documentation in udp.c:  
> 
> [...]
>
> Just have another look, instead of this case, I feel it's more like
> the one described in
> https://passt.top/passt/commit/udp_flow.c?id=9725e79888374a4e4060a2d798f3407c0006cc8a,
> which is packets arriving between bind() and connect(), and
> udp_sock_fwd() / udp_flow_from_sock() is called again to forward the
> packets. In this case, we should count the activity. The timestamp
> might not be so accurate, but should be very close. So I'm keeping it.

Hmm, I guess you're right, and actually that path shouldn't be called
for listening sockets. In any case, if you update the packet count
whenever the timestamp is updated, as you did on v3, that should be
correct. Running tests there in a bit.

-- 
Stefano


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-02-14  9:15 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-12  8:04 [PATCH] udp: Split activity timeouts for UDP flows Yumei Huang
2026-02-12  8:59 ` Stefano Brivio
2026-02-12 21:51 ` Stefano Brivio
     [not found]   ` <CANsz47mGXDgJSKpLqFiW_n5bXW13ZiayC_xhBEEGeBJTZwN5Xw@mail.gmail.com>
2026-02-13  7:08     ` Stefano Brivio
     [not found]       ` <CANsz47m8BPdUK2N-_Ka5GUHP_USnyHgO01Accktf-wxuX5rxDw@mail.gmail.com>
2026-02-13  9:12         ` Stefano Brivio
2026-02-13  9:54           ` Yumei Huang
2026-02-13 10:00             ` Stefano Brivio
2026-02-13 10:04               ` Yumei Huang
2026-02-13 10:17                 ` Stefano Brivio
2026-02-14  7:20                   ` Yumei Huang
2026-02-14  9:15                     ` Stefano Brivio

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).