On Fri, Feb 13, 2026 at 3:08 PM Stefano Brivio
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. 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. Tried either spliced / non-spliced, pasta / passt case, no exceptions observed. I was wondering if there is a scenario I'm not aware of.
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.
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.
Great, will do!
-- Stefano
-- Thanks, Yumei Huang