[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
On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio
wrote: On Fri, 13 Feb 2026 14:45:24 +0800 Yumei Huang
wrote: On Fri, Feb 13, 2026 at 5:51 AM Stefano Brivio
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
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